Review: Bug: contract signatures publicly exposed via MinIO CDN

review-415-2026-03-27 Review

review needs-refinement

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.tf lines 75-86 — verified: minio_s3_bucket_policy.assets_public_read applies s3:GetObject to arn:aws:s3:::assets/* with Principal *. This is the root cause — the wildcard grants public read to ALL objects in the assets bucket, including westside/signatures/.
  • [x] westside-contracts/src/lib/minio.ts line 24 — verified: signatures upload to westside/signatures/${playerId}_${timestamp}.png in the assets bucket. 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 uses minio-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.tf lines 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.