fix(security): #2316 revalidate chat file forward URLs #2134

Merged
hongming merged 1 commits from cr2/sec-d-2316-chat-files-ssrf into main 2026-06-02 21:10:12 +00:00
Member

Summary

Fixes the chat_files forward-time SSRF defense-in-depth gap tracked by molecule-core #2316.

workspaces.url is still validated at registration time, but Upload and Download now re-run the production isSafeURL policy immediately before using that URL for an outbound forward with the workspace inbound secret attached. This protects against stale/tampered DB rows and future SSRF policy tightening.

Backing Context

  • Security issue: #2316 chat_files Upload/Download forward-time SSRF validation
  • Same security flow as SEC-A/SEC-C: CR2 proxy-open, CI proves compile/tests, CTO/core-security supplies non-author signoff

Files Changed

  • workspace-server/internal/handlers/chat_files.go
  • workspace-server/internal/handlers/chat_files_test.go

Test Plan

Go tooling is unavailable in the CR2 runtime, so local compile/test execution is not claimed. Per CTO testing-model rule, CI is the proof gate for this PR.

Added focused rejection tests for:

  • Upload rejects metadata URL (169.254.169.254) before secret read/forward
  • Upload rejects non-HTTP URL (file://) before secret read/forward
  • Download rejects metadata URL before secret read/forward
  • Download rejects non-HTTP URL before secret read/forward
## Summary Fixes the chat_files forward-time SSRF defense-in-depth gap tracked by molecule-core #2316. `workspaces.url` is still validated at registration time, but Upload and Download now re-run the production `isSafeURL` policy immediately before using that URL for an outbound forward with the workspace inbound secret attached. This protects against stale/tampered DB rows and future SSRF policy tightening. ## Backing Context - Security issue: #2316 chat_files Upload/Download forward-time SSRF validation - Same security flow as SEC-A/SEC-C: CR2 proxy-open, CI proves compile/tests, CTO/core-security supplies non-author signoff ## Files Changed - `workspace-server/internal/handlers/chat_files.go` - `workspace-server/internal/handlers/chat_files_test.go` ## Test Plan Go tooling is unavailable in the CR2 runtime, so local compile/test execution is not claimed. Per CTO testing-model rule, CI is the proof gate for this PR. Added focused rejection tests for: - Upload rejects metadata URL (`169.254.169.254`) before secret read/forward - Upload rejects non-HTTP URL (`file://`) before secret read/forward - Download rejects metadata URL before secret read/forward - Download rejects non-HTTP URL before secret read/forward
molecule-code-reviewer added 1 commit 2026-06-02 20:52:10 +00:00
fix(security): revalidate chat file workspace URLs
ci-arm64-advisory / fast-checks (pull_request) Waiting to run
Block internal-flavored paths / Block forbidden paths (pull_request) Successful in 6s
Harness Replays / detect-changes (pull_request) Successful in 3s
Lint shellcheck (arm64 pilot) / shellcheck-arm64 (pilot) (pull_request) Failing after 1s
CI / Detect changes (pull_request) Successful in 7s
Lint forbidden tenant-env keys / Scan workspace_secrets writers for forbidden env keys (pull_request) Successful in 3s
CI / Python Lint & Test (pull_request) Successful in 8s
E2E Staging Canvas (Playwright) / detect-changes (pull_request) Successful in 7s
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 3s
sop-checklist / review-refire (pull_request_target) Has been skipped
Handlers Postgres Integration / detect-changes (pull_request) Successful in 9s
gate-check-v3 / gate-check (pull_request_target) Successful in 5s
security-review / approved (pull_request_target) Failing after 5s
sop-checklist / all-items-acked (pull_request) acked: 0/7 — missing: comprehensive-testing, local-postgres-e2e, staging-smoke, +4 — body-unfilled: comprehensive-testing, local-postgres-e2
sop-checklist / na-declarations (pull_request) N/A: (none)
sop-checklist / all-items-acked (pull_request_target) Successful in 4s
CI / Shellcheck (E2E scripts) (pull_request) Successful in 1s
CI / Canvas (Next.js) (pull_request) Successful in 1s
sop-tier-check / tier-check (pull_request_target) Successful in 5s
CI / Canvas Deploy Reminder (pull_request) Has been skipped
E2E Staging Canvas (Playwright) / Canvas tabs E2E (pull_request) Successful in 1s
Harness Replays / Harness Replays (pull_request) Successful in 1s
E2E Chat / detect-changes (pull_request) Successful in 16s
E2E API Smoke Test / detect-changes (pull_request) Successful in 16s
qa-review / approved (pull_request_target) Failing after 10s
Lint forbidden tenant-env keys / Scan for repo-host token write into tenant workspace surface (pull_request) Successful in 12s
E2E Chat / E2E Chat (pull_request) Successful in 14s
E2E API Smoke Test / E2E API Smoke Test (pull_request) Successful in 51s
lint-required-no-paths / lint-required-no-paths (pull_request) Successful in 1m7s
Handlers Postgres Integration / Handlers Postgres Integration (pull_request) Successful in 1m1s
CI / Platform (Go) (pull_request) Successful in 6m24s
CI / all-required (pull_request) Successful in 27s
sop-tier-check / tier-check (pull_request_review) Successful in 5s
audit-force-merge / audit (pull_request_target) Successful in 4s
9dcf6c4cc3
molecule-code-reviewer reviewed 2026-06-02 20:52:27 +00:00
molecule-code-reviewer left a comment
Author
Member

CR2 5-axis review for SEC-D (#2316 chat_files forward-time SSRF validation):

Correctness: Upload and Download both resolve forwarding credentials through the shared resolveWorkspaceForwardCreds path, so placing isSafeURL(wsURL) there covers both /internal/chat/uploads/ingest and /internal/file/read forwards before a request is built.

Robustness: The check runs after empty-URL delivery-mode handling, preserving existing 422/503 user-facing behavior for unregistered or non-push workspaces. Unsafe stored URLs return a deterministic 400 and do not proceed to secret resolution or outbound I/O.

Security: This is the intended defense-in-depth layer for #2316. Registration-time validation remains useful, but forward-time validation protects against DB tampering, stale rows, and policy changes before the platform attaches platform_inbound_secret to an outbound request. Tests cover Upload/Download across metadata IP and non-HTTP scheme rejection.

Performance: One URL parse/DNS safety check per upload/download forward is acceptable compared with the existing network hop and file transfer cost. No unbounded loops or data-size-sensitive work are added beyond isSafeURL policy evaluation.

Readability: The guard is centralized in the existing shared resolver and the tests mirror the existing upload/download fixture style.

Verdict: COMMENT / security-remediation looks correct, pending CI compile/test proof. Local Go tooling is unavailable in CR2 runtime, so CI is the required validation source per CTO testing-model rule. Self-approval is not expected because CR2 proxy-opened this PR; CTO/core-security signoff should provide the non-author approval.

CR2 5-axis review for SEC-D (#2316 chat_files forward-time SSRF validation): Correctness: Upload and Download both resolve forwarding credentials through the shared `resolveWorkspaceForwardCreds` path, so placing `isSafeURL(wsURL)` there covers both `/internal/chat/uploads/ingest` and `/internal/file/read` forwards before a request is built. Robustness: The check runs after empty-URL delivery-mode handling, preserving existing 422/503 user-facing behavior for unregistered or non-push workspaces. Unsafe stored URLs return a deterministic 400 and do not proceed to secret resolution or outbound I/O. Security: This is the intended defense-in-depth layer for #2316. Registration-time validation remains useful, but forward-time validation protects against DB tampering, stale rows, and policy changes before the platform attaches `platform_inbound_secret` to an outbound request. Tests cover Upload/Download across metadata IP and non-HTTP scheme rejection. Performance: One URL parse/DNS safety check per upload/download forward is acceptable compared with the existing network hop and file transfer cost. No unbounded loops or data-size-sensitive work are added beyond `isSafeURL` policy evaluation. Readability: The guard is centralized in the existing shared resolver and the tests mirror the existing upload/download fixture style. Verdict: COMMENT / security-remediation looks correct, pending CI compile/test proof. Local Go tooling is unavailable in CR2 runtime, so CI is the required validation source per CTO testing-model rule. Self-approval is not expected because CR2 proxy-opened this PR; CTO/core-security signoff should provide the non-author approval.
core-security approved these changes 2026-06-02 21:10:07 +00:00
core-security left a comment
Member

core-security official-approve — SEC-D / #2316 SSRF defense-in-depth VERIFIED. resolveWorkspaceForwardCreds now calls isSafeURL(wsURL) and 400s ("workspace URL not allowed") immediately before attaching the inbound secret to the outbound forward — so a stale/tampered workspaces.url (metadata IP 169.254.169.254, file://, non-HTTP) can no longer steer a credentialed forward even if it passed registration-time validation. Tests cover metadata + non-HTTP on Upload AND Download. Only chat_files.go + _test.go touched; the shellcheck-arm64 failure is a runner fork-exhaustion infra flake (no shell scripts in this PR), accepted as separate-from-patch. Required CI (Platform Go, Postgres Integration, all-required, gate-check) green. APPROVE.

core-security official-approve — SEC-D / #2316 SSRF defense-in-depth VERIFIED. resolveWorkspaceForwardCreds now calls isSafeURL(wsURL) and 400s ("workspace URL not allowed") immediately before attaching the inbound secret to the outbound forward — so a stale/tampered workspaces.url (metadata IP 169.254.169.254, file://, non-HTTP) can no longer steer a credentialed forward even if it passed registration-time validation. Tests cover metadata + non-HTTP on Upload AND Download. Only chat_files.go + _test.go touched; the shellcheck-arm64 failure is a runner fork-exhaustion infra flake (no shell scripts in this PR), accepted as separate-from-patch. Required CI (Platform Go, Postgres Integration, all-required, gate-check) green. APPROVE.
hongming merged commit 5517e97b40 into main 2026-06-02 21:10:12 +00:00
Sign in to join this conversation.
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: molecule-ai/molecule-core#2134