You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Two node-wide metadata indexes are served with no authentication and no visibility filtering, exposing metadata about private-repo objects and ref history.
Where (verified by reading the handlers; both on routers with no auth layer)
crates/gitlawb-node/src/api/ipfs.rs — list_pins (:103), route GET /api/v1/ipfs/pins (server.rs:192). Returns the (sha256, cid, pinned_at) index for every pinned object on the node, with no repo scope and no caller. This is the targeting oracle for GET /ipfs/{cid} serves any git object by raw hash with no visibility check, leaking withheld blobs #110: it hands an anonymous caller the exact CIDs to fetch private-repo blob content through the ungated GET /ipfs/{cid}.
crates/gitlawb-node/src/api/arweave.rs — list_anchors (:24), route GET /api/v1/arweave/anchors (server.rs:195). Returns ref-transition anchors (owner DID, ref names, old/new SHAs, CIDs, tx ids) filtered only by ?repo=, with no visibility check. Today it is bounded by the write-side announce invariant (anchoring only fires for announce-able repos), but it trusts that invariant rather than enforcing it, and anchors are permanent: a repo anchored while public and later made private leaks its ref graph here forever.
/arweave/anchors discloses ref-transition metadata (ref names, commit SHAs, pusher/owner DIDs) for anchored repos, and survives a later visibility change.
/arweave/anchors: gate per ?repo= on the caller's read visibility (deny -> 404), rather than relying on the write-side invariant. Derive any count from the post-filter set.
Found during a read-only egress audit; related to #110 (content-addressed blob egress).
Summary
Two node-wide metadata indexes are served with no authentication and no visibility filtering, exposing metadata about private-repo objects and ref history.
Where (verified by reading the handlers; both on routers with no auth layer)
crates/gitlawb-node/src/api/ipfs.rs—list_pins(:103), routeGET /api/v1/ipfs/pins(server.rs:192). Returns the(sha256, cid, pinned_at)index for every pinned object on the node, with no repo scope and no caller. This is the targeting oracle for GET /ipfs/{cid} serves any git object by raw hash with no visibility check, leaking withheld blobs #110: it hands an anonymous caller the exact CIDs to fetch private-repo blob content through the ungatedGET /ipfs/{cid}.crates/gitlawb-node/src/api/arweave.rs—list_anchors(:24), routeGET /api/v1/arweave/anchors(server.rs:195). Returns ref-transition anchors (owner DID, ref names, old/new SHAs, CIDs, tx ids) filtered only by?repo=, with no visibility check. Today it is bounded by the write-side announce invariant (anchoring only fires for announce-able repos), but it trusts that invariant rather than enforcing it, and anchors are permanent: a repo anchored while public and later made private leaks its ref graph here forever.Impact
/ipfs/pins+ GET /ipfs/{cid} serves any git object by raw hash with no visibility check, leaking withheld blobs #110 together let an unauthenticated caller enumerate and then fetch private-repo blob content./arweave/anchorsdiscloses ref-transition metadata (ref names, commit SHAs, pusher/owner DIDs) for anchored repos, and survives a later visibility change.Fix
/ipfs/pins: restrict to the node operator, or filter the index to objects the caller may read; at minimum stop exposing a node-wide CID index anonymously. Best handled together with GET /ipfs/{cid} serves any git object by raw hash with no visibility check, leaking withheld blobs #110./arweave/anchors: gate per?repo=on the caller's read visibility (deny -> 404), rather than relying on the write-side invariant. Derive any count from the post-filter set.Found during a read-only egress audit; related to #110 (content-addressed blob egress).