fix(security): pin cloud callbacks against DNS rebinding - #734
fix(security): pin cloud callbacks against DNS rebinding#734groupthinking wants to merge 4 commits into
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Important Review skippedAuto reviews are limited based on label configuration. 🏷️ Required labels (at least one) (1)
Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository YAML (base), Repository UI (inherited), Organization UI (inherited) Review profile: ASSERTIVE Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
✨ Simplify code
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Dependency Review✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.Snapshot WarningsEnsure that dependencies are being submitted on PR branches and consider enabling retry-on-snapshot-warnings. See the documentation for more information and troubleshooting advice. Scanned FilesNone |
There was a problem hiding this comment.
Pull request overview
Refactors cloud endpoints to a module-level router and addresses CodeQL findings.
Changes:
- Adds callback URL and log sanitization.
- Replaces exposed exception details with generic errors.
- Preserves
setup_cloud_api_endpointscompatibility.
Status — one decision needed to unblock this PRConsolidating the CodeQL, Vercel, Copilot, and Devin reviews rather than replying thread-by-thread, since they all converge on the same root issue. What's actually going on
Your call — pick one and I'll implement it in a single commit (chosen guard + the parameterized allow/deny and CR/LF regression tests Copilot asked for):
Until you choose, I won't push speculative security changes or merge to Generated by Claude Code |
…nitizer Covers the coverage gap flagged in review on #734, now that the guard's resolve-and-check approach is settled: - _sanitize_log_value: CR/LF stripping (log-injection prevention). - _is_safe_callback_url literal path: public IP allowed; loopback/private/ link-local/metadata literals, blocklisted hostnames, non-http schemes, and malformed URLs rejected — all without DNS. - _is_safe_callback_url resolution path (mocked getaddrinfo): obfuscated IPv4 encodings and DNS aliases resolving to internal addresses rejected; mixed A-records fail closed; unresolvable hostnames rejected; public resolution allowed. - End-to-end: process_video_task_handler never constructs the HTTP client for an unsafe callback_url, and does POST for a safe one. 105 passed (was 81). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018e5kEDnS1n4MKvoT6eQ6T4
|
Update — two changes since my last comment:
So the callback SSRF is now genuinely well-mitigated (resolution-based, fail-closed, tested) and the log values are sanitized. The only remaining step to merge is a human dismissal of the 3 pre-existing CodeQL alerts as accepted-risk in the code-scanning UI (or accepting the red check). I won't merge to Generated by Claude Code |
Agent Completion Truth Gate: BLOCKEDReasons: Machine-readable verdict{
"details": {
"collection_errors": [
"missing_intent_snapshot"
],
"identity_projection": {
"agent_login": "claude",
"issue_number": 911,
"run_id": "session_018e5kEDnS1n4MKvoT6eQ6T4"
}
},
"reasons": [
"evidence_collection_failed",
"missing_agent_result",
"missing_copilot_current_head_review",
"draft_pr"
],
"verdict": "blocked"
} |
|
Returned to draft because unique SSRF/log-sanitization scope remains, but this implementation is unsafe and incomplete.
Next action: reduce this same canonical branch to only the still-live SSRF/log scope on current |
920c397 to
f30f654
Compare
|
Done as directed — reduced this same branch to only the still-live SSRF/log scope, rebased on current What the single commit does (
Tests ( Two honest caveats (both need your call, not more code):
Left as a draft per your note. I won't merge. Generated by Claude Code |
|
@copilot Fix the code for all comments in this review thread. When a review comment includes a suggested change, apply the suggestion exactly. Do not make changes beyond what is described in the linked review thread. |
The address-attempt cap and overall deadline were already applied in commit
|
Security review: correct — merge after rebase, no code changes neededVulnerability confirmed live on The fix gets the hard part right. Most SSRF fixes validate the hostname and then hand the hostname to the HTTP client, which re-resolves it — leaving the TOCTOU window fully open. This one returns the numeric addresses from Verified the claim I was most skeptical of: TLS verification still targets the hostname. Also correct:
Completeness verified: No regressions. Retries limited to BlockingRebase only — |
…points Scoped to the still-live security gap on current main (the router refactor and exception-exposure fixes are already covered by #733/#831). Callback SSRF (user-supplied callback_url -> outbound POST): - _is_safe_callback_url() requires http(s)+hostname; rejects blocklisted internal hosts (trailing-dot / case normalized), and loopback/private/ link-local/reserved/multicast/unspecified IPs. With resolve=True it resolves via DNS and rejects if ANY resolved address is blocked, defeating obfuscated IPv4 encodings and DNS names that map to internal addresses. - Validated cheaply at request acceptance (400 on bad callback_url) and fully (DNS, off the event loop via asyncio.to_thread) immediately before dispatch; httpx client uses follow_redirects=False. - Residual DNS-rebinding TOCTOU documented (needs transport-level pinning). Log injection: - _sanitize_log_value() strips CR/LF from user-controlled values (video_url, task name, video_id, callback_url); lazy %-style logging. Tests: tests/unit/test_cloud_routes.py adds unit coverage for the guard (literal + DNS-resolution paths, obfuscated/rebinding/trailing-dot cases) and end-to-end (400 at acceptance; client never constructed for unsafe callback; safe callback POSTs with redirects disabled). 116 passed. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018e5kEDnS1n4MKvoT6eQ6T4
…mpts Addresses Copilot review on the current head: - _is_blocked_ip now rejects any non-global address (plus multicast and deprecated IPv6 site-local) instead of enumerating unsafe ranges. This closes shared CGNAT space (100.64.0.0/10) and fec0::/10, which Python reports as neither private nor global and which the enumerated form let through as callback targets into non-public networks. - The callback dispatch loop is now bounded: at most _MAX_CALLBACK_ADDRESS_ATTEMPTS addresses are tried under a single overall deadline (_CALLBACK_TOTAL_TIMEOUT), so a hostname resolving to many black-holing public addresses can no longer occupy a task worker for minutes instead of one timeout. Tests: CGNAT/site-local rejection cases + an attempt-cap test. 122 passed. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018e5kEDnS1n4MKvoT6eQ6T4
115088c to
7dc9cbe
Compare
|
Rebased onto current The only conflicts were import-block housekeeping (main's Verified post-rebase: no conflict markers, module imports cleanly, 122/122 tests pass in That clears the Generated by Claude Code |
Canonical issue
Closes #911. Progresses #898.
Outcome
Prevent Cloud Task callback SSRF, DNS rebinding, non-public destination bypasses, redirect pivots, unbounded callback retries, and log forging while preserving legitimate public HTTP(S) callbacks.
Scope
src/youtube_extension/backend/cloud_api_endpoints.pytests/unit/test_cloud_routes.pySecurity invariant
Resolve once off-loop, reject the complete DNS answer set when any address is non-global, connect only to validated numeric addresses, attempt at most three addresses under one bounded deadline, and never retry a POST that may have been transmitted. Preserve Host, port, TLS SNI, hostname certificate verification, redirects-disabled behavior, and async I/O.
Risk
The callback dispatcher now fails closed for mixed public/private DNS, CGNAT, site-local IPv6, resolution failures, and exhausted/deadline-bounded attempts. Exception values written to logs are CR/LF-sanitized; client-facing 500 details remain static.
Verification
115088c13fd518c1245cbecfd7f3e9a8a681d1d0.py_compileandgit diff --checkpassed.29817969888, Coverage29817969877, CodeQL29817969870, Security Scan29817969836, Secret Scan29817969826, and Dependency Review29817969916passed; E2E29817969857was repository-skipped.2026-07-21T09:27:35Z.Production evidence
dpl_4KK3dfL3RDqMAeojkhjRDnFpCqFkwas canceled before build because the controller-authored commit is unverified by Vercel's commit policy.4bf4e29c5dca31ca2ae027d9d6db993384c8679bproduced verified READY Previewdpl_3gqvTdCTMDY4iHmaASi1RXjWRN78.apps/web; this Python-only path is deployment-non-applicable. Production remains READY onmain@2d660c7e5382db1df5c5eba39537aa9d034376bbwith no runtime errors in the preceding 24 hours.Proven provenance blocker
PR #734 was created before focused issue #911. Trusted-main requires a frozen intent snapshot before PR creation and a terminal result authored by declared login
claude; neither can be retroactively manufactured by the connectedgroupthinkingcontroller. Keep draft pending an authorized legacy disposition.Draft exit checklist
Execution receipt
claude/session_018e5kEDnS1n4MKvoT6eQ6T42026-07-21T09:12:31Z/4bf4e29c5dca31ca2ae027d9d6db993384c8679bgroupthinking/eventrelay-blocker-watch-20260721-0912zclaude/determined-maxwell-j5mdyu/ fix(security): pin cloud callbacks against DNS rebinding #7342026-07-21T09:12:50Z2026-07-21T09:27:35Z115088c13fd518c1245cbecfd7f3e9a8a681d1d0