test: add SSRF guard coverage for URL credential bypass vectors#50523
test: add SSRF guard coverage for URL credential bypass vectors#50523vincentkoc merged 2 commits intoopenclaw:mainfrom
Conversation
There was a problem hiding this comment.
Pull request overview
Adds regression coverage to the SSRF fetch guard test suite to explicitly document/verify that URL “credential” (user[:pass]@host) parsing cannot be used to bypass private/loopback destination blocking.
Changes:
- Add a test that blocks an IPv4 loopback destination obscured via
attacker.com@127.0.0.1. - Add a test that blocks an IPv6 loopback destination with credentials (
user:pass@[::1]). - Add a test that blocks a redirect to a credential-obscured private host (
public@127.0.0.1).
You can also share your feedback on Copilot code review. Take the survey.
Greptile SummaryThis is a test-only PR that adds three new test cases to
Confidence Score: 4/5
Prompt To Fix All With AIThis is a comment left during a code review.
Path: src/infra/net/fetch-guard.ssrf.test.ts
Line: 304-312
Comment:
**Missing call-count assertion on redirect test**
The analogous existing test at line 136–145 captures the returned `fetchImpl` and asserts `expect(fetchImpl).toHaveBeenCalledTimes(1)`, confirming that the guard allowed the initial request to the public host before blocking the redirect. This new redirect test omits that assertion, making it slightly weaker: it would still pass even if the guard blocked the *first* request (i.e. if `fetchImpl` was never called at all).
Consider capturing the return value of `expectRedirectFailure` and adding `expect(fetchImpl).toHaveBeenCalledTimes(1)` after `await expectRedirectFailure(...)`, consistent with how the existing redirect-chain test is written.
How can I resolve this? If you propose a fix, please make it concise.Last reviewed commit: "security: add SSRF g..." |
| it("blocks redirect to a URL using credentials to obscure a private host", async () => { | ||
| const lookupFn = createPublicLookup(); | ||
| await expectRedirectFailure({ | ||
| url: "https://public.example/start", | ||
| responses: [redirectResponse("http://public@127.0.0.1:6379/")], | ||
| expectedError: /private|internal|blocked/i, | ||
| lookupFn, | ||
| }); | ||
| }); |
There was a problem hiding this comment.
Missing call-count assertion on redirect test
The analogous existing test at line 136–145 captures the returned fetchImpl and asserts expect(fetchImpl).toHaveBeenCalledTimes(1), confirming that the guard allowed the initial request to the public host before blocking the redirect. This new redirect test omits that assertion, making it slightly weaker: it would still pass even if the guard blocked the first request (i.e. if fetchImpl was never called at all).
Consider capturing the return value of expectRedirectFailure and adding expect(fetchImpl).toHaveBeenCalledTimes(1) after await expectRedirectFailure(...), consistent with how the existing redirect-chain test is written.
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/infra/net/fetch-guard.ssrf.test.ts
Line: 304-312
Comment:
**Missing call-count assertion on redirect test**
The analogous existing test at line 136–145 captures the returned `fetchImpl` and asserts `expect(fetchImpl).toHaveBeenCalledTimes(1)`, confirming that the guard allowed the initial request to the public host before blocking the redirect. This new redirect test omits that assertion, making it slightly weaker: it would still pass even if the guard blocked the *first* request (i.e. if `fetchImpl` was never called at all).
Consider capturing the return value of `expectRedirectFailure` and adding `expect(fetchImpl).toHaveBeenCalledTimes(1)` after `await expectRedirectFailure(...)`, consistent with how the existing redirect-chain test is written.
How can I resolve this? If you propose a fix, please make it concise.
WingedDragon
left a comment
There was a problem hiding this comment.
✅ Approved
Scope: Security — adds SSRF guard test coverage for URL credential bypass vectors (http://attacker.com@127.0.0.1, http://user:pass@[::1]).
Strengths:
- Tests verify that credentials in URLs don't obscure private host detection
- Covers both direct requests and redirect-to-credential-obscured-URL scenarios
- IPv6 loopback (
[::1]) is covered
Note: These tests only add coverage for existing behavior. If the SSRF guard already blocks these, great. If these tests fail, it means there's a real bypass that needs fixing. Verify CI is green.
No concerns. Good security test coverage addition. Ship it.
|
Added the missing redirect call-count assertion so the test now proves the initial public request is attempted before the redirect gets blocked. |
…claw#50523) * security: add SSRF guard tests for URL credential bypass vectors * test(security): strengthen SSRF redirect guard coverage --------- Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
…claw#50523) * security: add SSRF guard tests for URL credential bypass vectors * test(security): strengthen SSRF redirect guard coverage --------- Co-authored-by: Vincent Koc <vincentkoc@ieee.org> (cherry picked from commit a20ba74)
…claw#50523) * security: add SSRF guard tests for URL credential bypass vectors * test(security): strengthen SSRF redirect guard coverage --------- Co-authored-by: Vincent Koc <vincentkoc@ieee.org> (cherry picked from commit a20ba74)
…claw#50523) * security: add SSRF guard tests for URL credential bypass vectors * test(security): strengthen SSRF redirect guard coverage --------- Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
…claw#50523) * security: add SSRF guard tests for URL credential bypass vectors * test(security): strengthen SSRF redirect guard coverage --------- Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
…claw#50523) * security: add SSRF guard tests for URL credential bypass vectors * test(security): strengthen SSRF redirect guard coverage --------- Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
Summary
src/infra/net/fetch-guard.ssrf.test.tsdocumenting that the SSRF guard correctly handles URL credential bypass attempts:http://attacker.com@127.0.0.1:8080/internal— URL parser extracts hostname as127.0.0.1, correctly blockedhttp://user:pass@[::1]:8080/internal— IPv6 loopback with credentials, correctly blockedhttp://public@127.0.0.1:6379/— redirect chain credential bypass, correctly blockedMotivation: The SSRF guard correctly blocks these vectors via JavaScript's
URLconstructor (which extracts the real hostname regardless of credentials), but this behavior was not documented in the test suite. Explicit test coverage prevents regressions if the URL parsing or hostname extraction logic changes, and documents expected behavior for security review.Test plan