Skip to content

fix: make the HTTP client SSRF sinks read the request destination only - #129

Merged
SizzleUnrlsd merged 2 commits into
mainfrom
fix/ssrf-url-argument
Sep 24, 2026
Merged

SizzleUnrlsd merged 2 commits into
mainfrom
fix/ssrf-url-argument

Conversation

@SizzleUnrlsd

Copy link
Copy Markdown
Contributor

Closes #128. This is the first of the model fixes that come before the stricter guard rule from the #98 measurement.

Change

  • Destination by signature. Each Requests/httpx request function now names its destination the way its signature does. The URL is first (get, post, put, patch, delete, head, options), or second after the method (request, stream), or passed as url=. For send, the destination is the prepared request (request=). An argument that is not the destination (the body, JSON, headers, query parameters or method) no longer produces ssrf. Responses stay http-response sources for every function.
  • Sink.keywords. A kind restricted by positions or keywords reaches exactly the arguments those restrictions list. A starred argument or **kwargs still reaches no restricted kind, as before. merged and SafeArgument guarding keep the keywords.
  • Named keywords in summaries. A summary used to merge all keyword arguments of a reached call, so requests.post(url=target) inside a project function would have been lost. ExternalCall.keyword_dependencies now keeps each keyword's name, ((name, dependencies), …), with None for *args and **kwargs. The cache format moves from 8 to 9.

Snapshot changes, reviewed

  • pygoat introduction/apis.py:83: ssrf removed. The call is requests.request("PATCH", url, data=payload) with a constant URL; only the POSTed CSRF token in the body is tainted. It was a false positive.
  • httpie httpie/client.py:114: one of the two ssrf records at this line is gone, the other stays. The line is requests_session.send(request=prepared_request, **send_kwargs_merged, **send_kwargs). The removed flow reached send only through the **kwargs dicts of environment settings, not through the prepared request. The remaining flow is the prepared request itself, which stays a sink.

Verification

  • pytest: 1032 passed.
  • pytest -m regression under Python 3.12: 19 passed after re-recording only the two snapshots above.
  • ruff check src tests and mypy are clean.
  • Mutation checks. Each of these changes fails at least one test: a sink ignoring keywords, a direct call dropping keyword names, a summary call dropping them, and a summary merging keywords into unnamed ones.
  • Changed test expectations:
    • test_keyword_arguments_are_conservatively_merged becomes test_keyword_arguments_keep_their_names. Merging was exactly what made url= undecidable through a summary.
    • The codec round-trip fixture uses the new keyword shape.

@SizzleUnrlsd SizzleUnrlsd self-assigned this Sep 24, 2026
@SizzleUnrlsd
SizzleUnrlsd merged commit 0e564b3 into main Sep 24, 2026
10 checks passed
@SizzleUnrlsd
SizzleUnrlsd deleted the fix/ssrf-url-argument branch September 24, 2026 21:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Precision: SSRF sinks of HTTP clients read the destination only

1 participant