Review: Bug: contract signatures publicly exposed via MinIO CDN
Verdict: NEEDS_REFINEMENT
Template Completeness
- [x] Type — Bug
- [x] Lineage — Standalone, discovered during westside playground asset audit 2026-03-26
- [x] Repo — forgejo_admin/pal-e-platform
- [x] What Broke — detailed, includes curl verification
- [x] Repro Steps — 2 steps, clear
- [x] Expected Behavior — two options given (separate bucket or prefix-deny policy)
- [x] Environment — cluster/namespace, service, related PR
- [x] Acceptance Criteria — 3 criteria, all testable
- [x] Related — project-westside-basketball, arch-deployment-westside-basketball, WS-S4
All required sections present. Template is complete.
Traceability
- [ ] story:X label — MISSING. Issue body references WS-S4 (static assets via public CDN) but the board item labels do not include a story: label. This should be story:WS-S4 since the bug is a direct consequence of the WS-S4 CDN work.
- [x] arch:minio label — present on board item
- [x] Forgejo issue — forgejo_admin/pal-e-platform#186, open
File Targets
- [x]
terraform/modules/storage/main.tflines 75-86 — verified:minio_s3_bucket_policy.assets_public_readappliess3:GetObjecttoarn:aws:s3:::assets/*with Principal*. This is the root cause — the wildcard grants public read to ALL objects in the assets bucket, includingwestside/signatures/. - [x]
westside-contracts/src/lib/minio.tsline 24 — verified: signatures upload towestside/signatures/${playerId}_${timestamp}.pngin theassetsbucket. Not referenced in the issue but confirms the upload path.
Note: The issue does not list specific file targets. For a bug ticket, the root cause file (
terraform/modules/storage/main.tf) should be explicitly called out so the implementing agent knows exactly where to make the change.Repo Placement
Correct. The bucket policy lives in
forgejo_admin/pal-e-platform at terraform/modules/storage/main.tf. The fix is a Terraform change in this repo.Alternative approach (move signatures to private bucket) would also be contained to this repo + a small change in westside-contracts. The issue correctly identifies both options. However, the simpler fix (prefix-deny policy) is single-repo.
Dependencies
- Board item #233 (issue #126, "Public CDN: MinIO assets bucket public-read + public funnel") — DONE — is the originating feature that created this exposure. The fix must not regress that feature.
- Board item #435 (issue #196, "tofu apply blocked by MinIO provider refresh") — BACKLOG — has arch:minio label. If the MinIO provider is broken, applying a policy change could fail. Not a direct blocker but worth noting.
- No other in_progress or next_up items with arch:minio.
Acceptance Criteria
All 3 criteria are testable by an agent:
- "curl to any signature URL returns 403 or 404" — directly testable
- "Branding/coaches/sponsors images remain publicly accessible" — testable via curl to known URLs (e.g.,
westside/branding/logo-transparent.png) - "No regression in email image delivery" — testable by verifying
assets/email-templates/prefix URLs still resolve (pal-e-mail usesminio-api.tail5b443a.ts.net/assets/email-templates)
MISSING criterion: The issue should verify that
westside-contracts can still WRITE signatures (the app uses service-account credentials, not the public policy, so writes should be unaffected — but this should be an explicit acceptance criterion).Blast Radius
- westside-app: 25+ hardcoded CDN URLs under
assets/westside/branding/,westside/coaches/,westside/jerseys/,westside/sponsors/— all must remain publicly readable. - pal-e-mail: uses
assets/email-templates/prefix for email images — must remain publicly readable. - minio-playground and minio-api: browse the assets bucket but use authenticated access, not affected.
- The fix MUST be prefix-scoped. A blanket removal of the public-read policy would break the entire westside public site and email delivery.
Recommendation
Two minor issues before READY:
- Add story:WS-S4 label to the board item to complete traceability. The issue body references WS-S4 but the board item labels are missing it.
- Add file target to the issue body:
terraform/modules/storage/main.tflines 75-86 (minio_s3_bucket_policy.assets_public_read). This tells the implementing agent exactly where to make the change. - Add acceptance criterion: "westside-contracts can still upload signatures via service account credentials" — confirms write path is unaffected.
Once these are addressed, the ticket is READY for a single-agent pass.