Skip to content

fix(atlas): list R2 buckets in every jurisdiction, not just the default - #2096

Open
timrutzen wants to merge 1 commit into
danielmiessler:mainfrom
timrutzen:fix/atlas-r2-jurisdiction
Open

fix(atlas): list R2 buckets in every jurisdiction, not just the default#2096
timrutzen wants to merge 1 commit into
danielmiessler:mainfrom
timrutzen:fix/atlas-r2-jurisdiction

Conversation

@timrutzen

Copy link
Copy Markdown

The bug

The Cloudflare collector lists R2 buckets once, without a jurisdiction:

const r2 = (await cfSoft(`/accounts/${acct.id}/r2/buckets`, tok, "R2 buckets", ))

That listing returns only default-jurisdiction buckets. An account whose buckets were created with --jurisdiction eu gets a 200 and an empty array, which is indistinguishable from owning no buckets at all. The asset graph then reports zero R2 buckets, permanently and silently — no error, no partial run, nothing to notice.

Why the obvious fix does not work

Jurisdiction travels as a request header, cf-r2-jurisdiction, never as a query parameter. ?jurisdiction=eu is accepted and silently ignored, so it returns exactly the same empty list as the bug it was meant to repair.

Measured against one token, in the same second:

request buckets returned
GET /r2/buckets 0
GET /r2/buckets?jurisdiction=eu 0
GET /r2/buckets + cf-r2-jurisdiction: eu 3

The change

  • cf() and cfSoft() take an optional extra headers argument.
  • The R2 listing loops default and eu, and records which jurisdiction a bucket came from as an asset attribute.

Two decisions worth calling out, both in code comments:

  • Buckets stay keyed by name alone. A worker's r2_bucket binding carries only bucket_name, so a jurisdiction-qualified key could not be resolved by datastoreTarget() and every DEPENDS_ON edge to a bucket would break. Jurisdiction rides as an attribute instead.
  • fnv is not probed. It is FedRAMP-only and 403s for ordinary accounts, which would set partial on every run and stop the collector ever sweeping.

Testing

Run against a live account with three eu buckets.

  • Before: 0 R2 assets.
  • After: 3, each carrying jurisdiction: eu; collector reports complete over 22 assets and 16 edges.

Default-jurisdiction accounts are unaffected beyond one extra listing call that returns empty.

The Cloudflare collector lists R2 buckets once, without a jurisdiction. That
listing returns only default-jurisdiction buckets, so an account whose buckets
were created with `--jurisdiction eu` gets a 200 and an empty array, which is
indistinguishable from owning no buckets at all. The asset graph then reports
zero R2 buckets, permanently and silently.

Jurisdiction is a request HEADER, `cf-r2-jurisdiction`, not a query parameter.
`?jurisdiction=eu` is accepted and ignored, so the intuitive fix produces the
same empty list as the bug. Measured against one token in the same second:
header -> 3 buckets, query parameter -> 0.

This threads an optional headers argument through `cf()` and `cfSoft()`, then
loops the R2 listing over `default` and `eu`, recording which jurisdiction a
bucket came from as an asset attribute.

Buckets stay keyed by name alone. A worker's `r2_bucket` binding carries only
`bucket_name`, so a jurisdiction-qualified key would break every DEPENDS_ON
edge `datastoreTarget()` resolves.

`fnv` is deliberately not probed: it is FedRAMP-only and 403s for ordinary
accounts, which would set `partial` on every run and stop the collector ever
sweeping.

Tested against a live account with three `eu` buckets. Before: 0 R2 assets.
After: 3, each carrying `jurisdiction: eu`, with the collector reporting
complete over 22 assets and 16 edges. Default-jurisdiction accounts are
unaffected beyond one extra listing call that returns empty.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant