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 fsckreporting no dangling objects - malicious-ish fixture: commit a tree, amend it, export -> old commit is NOT in the export
-
tip sha in
repo.gitequals the sha advertised in the clone block (short sharevon index) -
test asserts
git -C <out>/repo.git fsckhas nodanglinglines