---
id: 50c21acd-1a7e-4e98-84e9-f1d07e647641
title: repo.git leaks unreachable objects (not an exact snapshot)
state: closed
labels:
- security
assignees: []
milestone: null
created: 2026-10-27T14:47:37.930020568Z
updated: 2026-10-27T15:53:18.448696060Z
---
Adversarial review of #54 found a real information leak: `dist/repo.git` is not the snapshot it claims to be.

`export_dumb_repo` runs `git clone --bare <repo>`, which for a local path uses the *local* transport: git copies/hardlinks the entire source object database instead of transferring only objects reachable from the selected branch. `--single-branch` limits refs, not objects.

Verified on this repo:

```
$ git clone -q -b master http://127.0.0.1:39313/repo.git /tmp/httpclone
$ git -C /tmp/httpclone cat-file -e 0f20352a1fab8155e35e67b32602b90c93dd9d3a && echo PRESENT
PRESENT
$ git -C /tmp/httpclone fsck --no-progress | grep -c dangling
6
```

`git fsck` on the published artifact reports 61 dangling objects; a plain HTTP clone inherits 6 unreachable commits, e.g. `merge 3c5c330e-…` (2026-09-10) which is not on master.

Impact: force-pushed-away commits, deleted branches, amended commits and any blob ever committed become public to anyone who clones and runs `git fsck`/`git cat-file`. That includes dropped `.forge/` internals. The clone block simultaneously claims "exact snapshot of <branch> @ <sha>", which is false.

Fix: derive `repo.git` from the bundle `snapshot()` already writes, i.e. `git clone --bare --single-branch --branch <b> <out>/repo.bundle <out>/repo.git` (bundle transport negotiates reachable objects only), then `update-server-info`. Verified on this repo: 0 dangling, 1 pack, 24 files.

Acceptance:
- [ ] exporting a repo that contains unreachable objects yields a clone with `git fsck` reporting no dangling objects
- [ ] malicious-ish fixture: commit a tree, amend it, export -> old commit is NOT in the export
- [ ] tip sha in `repo.git` equals the sha advertised in the clone block (short sha `rev` on index)
- [ ] test asserts `git -C <out>/repo.git fsck` has no `dangling` lines
