verify_chain (crates/gitlawb-core/src/ucan.rs:252-292) proves only self-consistency. A presenter can self-issue a root token (iss=me, aud=me, att=[git/push on Alice's repo]) and delegate from it; every check passes because every key is the attacker's. The node's validate_ucan_chain (auth/mod.rs:269-309) adds only iss == HTTP signer and aud == node, both of which the attacker controls.
Status: latent. ucan.can() gates nothing in the node today and push is owner-only, but the doc comment reserves exactly the extension that would activate it.
Distinct from #425 (constraints are ignored) and #467 (verification budget): this is about the chain root not being anchored to the resource owner.
Fix: before any can()-gated route ships, require the chain root's issuer to be the server-recorded repo owner (or a node-issued bootstrap token). Also check the ucan version field on verify, which is never read, and cap proof-chain recursion.
Found in the Oct 2 2026 audit (A6) at bfc44f9.
verify_chain(crates/gitlawb-core/src/ucan.rs:252-292) proves only self-consistency. A presenter can self-issue a root token (iss=me, aud=me, att=[git/push on Alice's repo]) and delegate from it; every check passes because every key is the attacker's. The node'svalidate_ucan_chain(auth/mod.rs:269-309) adds onlyiss == HTTP signerandaud == node, both of which the attacker controls.Status: latent.
ucan.can()gates nothing in the node today and push is owner-only, but the doc comment reserves exactly the extension that would activate it.Distinct from #425 (constraints are ignored) and #467 (verification budget): this is about the chain root not being anchored to the resource owner.
Fix: before any
can()-gated route ships, require the chain root's issuer to be the server-recorded repo owner (or a node-issued bootstrap token). Also check theucanversion field on verify, which is never read, and cap proof-chain recursion.Found in the Oct 2 2026 audit (A6) at bfc44f9.