fix(cache): gate capsule landing on request provenance, not loopback (#1654) - #141
Merged
Merged
Conversation
…1654) First failing-test anchor for gating capsule-landing side effects on request provenance (Sec-Fetch-Site) rather than a loopback peer address. Refs #1654 Co-Authored-By: Claude <noreply@anthropic.com>
…(#1654) A loopback peer address proves the connection is local, not that the operator authorized the request. A cross-site browser navigation (GET dig.local/s/<capsule>) would otherwise LAND the attacker's chosen capsule (warm + reshare + DHT holder-announce, SPEC §14.3/§21.3) — a remotely-triggerable amplification. The bytes are harmless (public content); the durable holder side effect is the door. - New RequestProvenance axis parsed from Sec-Fetch-Site (download.rs): only an explicit cross-site denies landing; absence (CLI/SDK) and every other value are first-party. Threaded into ContentServer::serve_content_plaintext + collapsed once via landing_origin(origin, provenance) so a cross-site read serves but folds landing to Peer. Reads are never blocked. - cache.fetchAndCache over HTTP POST / now requires the control/paired token (it is an explicit become-a-holder call); the in-process FFI cache.* path stays open. - SPEC (service §21.8/§21.9/§21.10 + core seam note), README, DEVELOPMENT_LOG. Version bumped 0.74.3 -> 0.74.4. Closes #1654 Co-Authored-By: Claude <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What changed
Closes DIG-Network/dig_ecosystem#1654.
A capsule landing (cache write) is what makes this node a discoverable DHT holder (
refresh_dht_inventory, SPEC §14.3/§21.3) — a durable side effect. Those side effects were gated on the peer address being loopback, but "the peer address is loopback" is not the same predicate as "the operator asked for this." Two doors let an unauthorized caller trigger a landing:/s/*path. A cross-originno-corsGET tohttp://localhost:<port>/s/...runs server-side regardless of CORS;http://localhostis potentially-trustworthy (not mixed-content-blocked). Chrome's PNA rejects it, but Firefox/Safari have no PNA — so a drive-by page could make the visitor's node fetch, cache, and DHT-announce attacker-chosen capsules.cache.fetchAndCachehad no token gate on the HTTP surface (it is neithercontrol.*norwallet.*, so it fell through to dispatch untokened) — the same landing primitive, explicit.The fix — provenance is a second, orthogonal axis over
ReadOriginRequestProvenance { FirstParty, CrossSite }, derived fromSec-Fetch-Site. The effective landing predicate becomesorigin == Local && provenance == FirstParty. The read always serves (consumption stays frictionless per §6.0); only the durable side effect (backfill / reshare / announce) is skipped for a cross-site request. Parser rule (load-bearing):cross-site ⇒ CrossSite;same-origin | same-site | none ⇒ FirstParty; absent header ⇒ FirstParty — CLI/SDK send noSec-Fetch-*, so absence must never be treated as cross-site or frictionless reads would break. Only an explicitcross-sitedenies the side effect.cache.fetchAndCachenow requires the local control token (master or paired), gated inserver.rs::rpcalongsidecontrol.*. The in-process FFI path bypassesrpc(), so the DIG Browser's opencache.*contract is unaffected.Locallabel trusts the loopback boundary;X-Forwarded-Foris explicitly not trusted; a trusted-proxy deployment must not expose the loopback port to untrusted networks.ReadOrigin::{Local,Peer}(PR#108) stays as the transport axis; this is a second axis (provenance within a local request).Verification
download.rs):sec_fetch_site_cross_site_is_cross_site,..first_party_values_are_first_party,..absent_is_first_party(the frictionless-CLI guard),..is_case_insensitive_and_trims,..unknown_value_is_first_party.content_serve.rs+105,server.rs+40): a cross-site/s/read serves bytes but performs no warm and no announce; a same-site / header-absent read still serves and lands; an untokenedcache.fetchAndCacheis refused (Unauthorized) while a tokened one dispatches.SPEC.md §21.8("Landing has a SECOND axis — request provenance") added; the door-3 trusted-proxy caveat + the honest residuals (pre-Sec-FetchUAs; shared-origin store-to-store landing) documented.Version
fix:(security patch — tightens thecache.fetchAndCacheHTTP contract; authorized callers via paired token / control token / FFI are unaffected) →[workspace.package].version0.74.3 → 0.74.4.Generated by Claude Code