Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,13 @@ aggregate instead: an italic *Catalog* line at the end of the version section an
were served as bare `text/plain`, which a strict client decodes as Latin-1 mojibake. nginx now
declares `charset utf-8` on text responses in both server blocks, and the daily bot-serving
monitor asserts it on `llms.txt` (#10492).
- **The image bucket is no longer publicly listable** — `allUsers` held
`roles/storage.objectViewer` on `anyplot-images`, whose bundled `storage.objects.list` let
anyone enumerate the whole bucket, including the `staging/` prefix where unreviewed pre-merge
renders sit indistinguishable from promoted ones. Swapped to `roles/storage.legacyObjectReader`
(get-only): every render URL keeps working (verified live incl. CORS), only the directory
listing is gone. Applied 2026-08-19 via gcloud — bucket IAM does not deploy with the repo; the
policy and re-apply procedure are recorded in the project guide (#10493).

### Changed

Expand Down
24 changes: 24 additions & 0 deletions agentic/docs/project-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,30 @@ page under `COEP: require-corp` must load the renders with a `crossorigin`
attribute (or through its own proxy); plain same-tab fetches and `<img>` tags
are unaffected.

### Bucket public-access policy

Objects are publicly fetchable by URL, but the bucket is NOT publicly listable:
`allUsers` holds `roles/storage.legacyObjectReader` (grants `storage.objects.get`
only). It previously held `roles/storage.objectViewer`, whose bundled
`storage.objects.list` let anyone enumerate the whole bucket — including the
`staging/` prefix, where unreviewed pre-merge renders sit indistinguishable
from promoted ones (AI-access audit 2026-08-19; changed 2026-08-19). Like the
CORS policy above, this is bucket metadata that does not deploy with the repo —
re-apply after a bucket rebuild. Both commands are needed: adding the get-only
role does not remove public listability if an `objectViewer` binding exists
(the removal is a no-op when the binding is already absent):

```bash
gcloud storage buckets add-iam-policy-binding gs://anyplot-images \
--member=allUsers --role=roles/storage.legacyObjectReader
Comment thread
MarkusNeusinger marked this conversation as resolved.
gcloud storage buckets remove-iam-policy-binding gs://anyplot-images \
--member=allUsers --role=roles/storage.objectViewer
```

Never grant `allUsers` a role that includes `storage.objects.list`. To verify:
an object URL must return 200 anonymously, while
`https://storage.googleapis.com/storage/v1/b/anyplot-images/o` must return 401.

## Tech Stack

- **Backend**: FastAPI, SQLAlchemy (async), PostgreSQL, Python 3.13+
Expand Down
Loading