fix: fetch_url must not follow redirects past the approval gate (desktop audit #4490) - #4522
Conversation
|
@coderabbitai review |
|
/review |
Qodo reviews are paused for this user.Troubleshooting steps vary by plan Learn more → On a Teams plan? Using GitHub Enterprise Server, GitLab Self-Managed, or Bitbucket Data Center? |
Greptile SummaryThe PR prevents
Confidence Score: 5/5The PR appears safe to merge. No blocking failure remains.
|
| Filename | Overview |
|---|---|
| src/praisonai-desktop/engine/server.py | Adds a redirect handler that rejects 3xx traversal after approval while retaining ordinary direct fetches. |
| src/praisonai-desktop/engine/test_chat_stream.py | The revised regression test records requests at the redirect target and now directly verifies the previously requested no-contact invariant. |
Reviews (3): Last reviewed commit: "test: assert unapproved redirect target ..." | Re-trigger Greptile
|
@claude You are the FINAL architecture reviewer. If the branch is under MervinPraison/PraisonAI (not a fork), you are able to make modifications to this branch and push directly. SCOPE: Review changes in this PR. Python SDK: praisonaiagents, praisonai. TypeScript SDK: src/praisonai-ts/. Do NOT modify src/praisonai-rust. Read ALL comments above from Gemini, Qodo, CodeRabbit, and Copilot carefully before responding. Phase 1: Review per AGENTS.md
Phase 2: FIX Valid Issues Phase 3: Final Verdict |
|
Claude finished @MervinPraison's task —— View job Final Architecture Review — PR #4522
Phase 1 — Architecture reviewThe production change in
Protocol-driven ✅ · Backward compatible ✅ · No perf regression ✅ · Genuine security value (closes a real human-in-the-loop bypass) ✅ · No Agent bloat ✅ Phase 2 — Reviewer feedbackGreptile (P2, valid): the regression test asserted only that the secret body was absent from the result — so a broken impl that contacts the unapproved target and discards its response would still pass. Since the request itself is a side effect (can trigger actions, logging, rate limits), this needed to assert zero reachability. Fix applied to
Mutation-verified: reverting the one-line Test results:
Files modified on this branch
Skipped: Phase 3 — Verdict✅ Approve (informally). The security fix is sound and the one valid reviewer concern is resolved with a mutation-verified test. No blocking issues remain. (I can't submit a formal GitHub approval for security reasons — please merge when ready.) Job run · Branch |
|
@claude this PR has merge conflicts with |
|
Claude finished @MervinPraison's task —— View job Rebase & Conflict Resolution — PR #4522 ✅
Key finding during rebaseWhile this PR was open, main merged #4506 (
Verification
ScopeDesktop-engine only ( Skipped: no logic merged in from main's duplicate — it was strictly a subset of this PR's work, so there was nothing newer to fold in. Job run · Branch |
…#4490) The approval card authorises one URL, but urllib.request.urlopen follows 3xx by default, so a 302 could send the fetch to any host or port -- including this engine on loopback -- and return its body to the model under an approval that never named it. Fetch through an opener that refuses redirects and reports the unapproved target instead. Part of the #4504 desktop audit tracking issue (finding #1 of 16, high). Co-authored-by: MervinPraison <MervinPraison@users.noreply.github.com>
Strengthen the fetch_url redirect regression test per reviewer feedback (Greptile). The test previously only checked the secret body was absent from the result -- an implementation that contacts the unapproved loopback target and discards its response would still pass. Record requests to server B and assert it receives none, since the request itself is a side effect (actions, logging, rate limits). Mutation-verified: reverting the one-line server.py fix makes the strengthened test fail on both the leaked body and the recorded hit. Co-authored-by: Mervin Praison <MervinPraison@users.noreply.github.com>
071f802 to
8eeb595
Compare
|
Merge gate scan — eligible for assessment. Claude merge gate will assess and may auto-merge if |
|
Merged by Claude PR merge gate ( |
|
Merge gate scan — not eligible for auto-merge.
Actions: wait for CI and the Claude review chain, or add label |
Summary
Addresses the highest-severity, fully self-contained finding from the desktop audit tracking issue #4504: #4490 —
fetch_urlfollows redirects, so the approval card authorises one URL and the engine fetches another (audit rank 1 of 16, high).urllib.request.urlopenfollows 3xx redirects by default. After the user approveshttp://example.com/blog, a302 Location: http://127.0.0.1:<engine port>/chats/<id>sends the fetch to an unapproved loopback service and returns its body into the model's context — bypassing the app's only human-in-the-loop control.Change
engine/server.py— fetch throughbuild_opener(_NoRedirect); a redirect now raises a clearHTTPError("redirect to … was not approved") that reaches the model, so it can re-request the new URL and get a second approval card.Test
engine/test_chat_stream.py::FetchUrlDoesNotFollowRedirects— stands up two loopback servers (A302→ B, B returns a secret), setsapproval_mode: neverto allow the gate, fetches A, and asserts the secret does not come back. A second test asserts a direct (non-redirecting) fetch still works.test_chat_stream,test_portability): 64 passed, 0 regressions.Scope note
#4504 tracks 14 separate audit defects. Per the repo's "minimal, focused fix" guidance, this PR fixes only the #1-ranked security finding. The remaining 13 remain tracked in their own issues.
Fixes #4490. Part of #4504.
Generated with Claude Code