Repository navigation
GET /ipfs/{cid} serves tree/commit objects of withheld subtrees, leaking structure get_tree protects (KTD3) #135
Copy link
Copy link
Closed
Labels
crate:nodegitlawb-node — the serving node and REST APIgitlawb-node — the serving node and REST APIkind:securityVulnerability fix or hardeningVulnerability fix or hardeningsev:mediumDegraded but workaround existsDegraded but workaround existssubsystem:apiNode REST API request/response surfaceNode REST API request/response surfacesubsystem:visibilityPath-scoped visibility and content withholdingPath-scoped visibility and content withholding
Description
Activity
- addedsev:mediumDegraded but workaround existsDegraded but workaround existskind:securityVulnerability fix or hardeningVulnerability fix or hardeningcrate:nodegitlawb-node — the serving node and REST APIgitlawb-node — the serving node and REST APIsubsystem:visibilityPath-scoped visibility and content withholdingPath-scoped visibility and content withholdingsubsystem:apiNode REST API request/response surfaceNode REST API request/response surface
on Jun 30, 2026 - added a commit that references this issue
on Jun 30, 2026 - added 5 commits that reference this issue
on Jul 2, 2026 - added 11 commits that reference this issue
on Jul 10, 2026 - added 3 commits that reference this issue
on Jul 22, 2026 - added a commit that references this issue
on Aug 25, 2026
Metadata
Metadata
Assignees
Labels
crate:nodegitlawb-node — the serving node and REST APIgitlawb-node — the serving node and REST APIkind:securityVulnerability fix or hardeningVulnerability fix or hardeningsev:mediumDegraded but workaround existsDegraded but workaround existssubsystem:apiNode REST API request/response surfaceNode REST API request/response surfacesubsystem:visibilityPath-scoped visibility and content withholdingPath-scoped visibility and content withholding
Summary
GET /ipfs/{cid}(get_by_cid) serves tree, commit, and tag objects unconditionally under path-scoped visibility rules, disclosing the structure of a withheld subtree (every child filename and child object id) to a caller who is denied that subtree. Blob content stays protected; this is a metadata/structure leak, not a content leak.This is the KTD3 behavior that #126 explicitly scoped out, surfaced during review of #133 (which correctly fixes the blob-content half of the same endpoint). Filing so the divergence is tracked rather than buried in a PR thread.
Where
crates/gitlawb-node/src/api/ipfs.rs(the per-object gate):Under a path-scoped rule the allowed-set gate applies only to
obj_type == "blob". Atree/commit/tagobject falls through and is served. A git tree object's raw body is<mode> <filename>\0<raw-oid>per entry, so fetching the tree CID of a withheld subtree returns every child filename and child oid in cleartext, recursively. The leaf blobs themselves still require allowed-set membership, so their content is protected.Why it matters
This is the same confidentiality boundary that
get_tree(crates/gitlawb-node/src/api/repos.rs:444, the N3 fix) deliberately defends: it gates on the requested subtree path precisely so "a caller denied a withheld subtree can still enumerate its names/SHAs" cannot happen. The two read surfaces over the same git data disagree: the REST tree-read path protects subtree names/oids, the IPFS CID path does not.Bounded by the attacker needing the tree object's sha256 CID to request it. That is not free, but CID-enumeration surfaces exist (see #121), so it should not be relied on as the only barrier.
Scope / not a regression
Pre-existing. The pre-#133 deny-set (
withheld_blob_oids) also enumerated only blob oids, so trees were never withheld there either. #133 preserves the behavior (and the merged test attest_support.rsasserts "tree object is served to anon (KTD3)"). This issue is about closing the divergence, not about #133.Suggested direction
Gate non-blob objects under path-scoped rules against their own reachable path the way
get_treedoes, or make a deliberate, documented decision that tree/commit structure is non-confidential and reconcileget_treeto match. Either way the two surfaces should agree. A handler test should pin the chosen behavior by asserting the served body contents, not just the status code.