Skip to content

fix(scan): a request to a literal host is not an SSRF - #207

Merged
ralyodio merged 1 commit into
masterfrom
fix/py-ssrf-constant-host
Sep 25, 2026
Merged

ralyodio merged 1 commit into
masterfrom
fix/py-ssrf-constant-host

Conversation

@ralyodio

Copy link
Copy Markdown
Contributor

Third false positive in the same family as #203.

py-ssrf-outbound-request fires on urlopen(req) for any variable req, so a Python tool that talks to exactly two fixed APIs was reported twice at high severity:

req = urllib.request.Request("https://backboard.railway.com/graphql/v2", data=body)
req = urllib.request.Request(f"https://api.supabase.com/v1/{path}")

SSRF is control of the destination, not of the path. Neither can be steered to the metadata endpoint or to localhost for any input, and the first has no interpolation in it at all.

constantRequestHostGuard resolves the variable to its assignments in the file and exonerates only when every one of them names a literal host. Still reported, and asserted in the tests:

  • an interpolated authority — f"https://{host}/v1"
  • a bare variable — Request(url)
  • a name with no assignment in sight
  • a name assigned in two functions where either destination is dynamic

Measured

On profullstack/cli-tools#103, with the language resolved the way the CLI resolves it (by shebang — the file is extensionless): 4 findings → 2, and the two that go are exactly the two GitHub reported at dev2/dev2-site:136 and :1132. The two that remain are the real interpolated SQL statements.

358 tests pass.

Third false positive in the same family as #203: a pattern matching the
shape of a dangerous call without asking whether the danger is reachable.

py-ssrf-outbound-request fires on `urlopen(req)` for any variable `req`,
so a Python tool that talks to exactly two fixed APIs was reported twice
at high severity:

    req = urllib.request.Request("https://backboard.railway.com/graphql/v2", data=body)
    req = urllib.request.Request(f"https://api.supabase.com/v1/{path}")

SSRF is control of the destination, not of the path. Neither of those can
be steered to the metadata endpoint or to localhost for any input, and
the first has no interpolation in it at all.

`constantRequestHostGuard` resolves the variable to its assignments in
the file and exonerates only when every one of them names a literal
host. An interpolated authority (`f"https://{host}/v1"`), a bare
variable (`Request(url)`), or a name with no assignment in sight all
leave the finding standing, and a name assigned in two functions is
exonerated only if both destinations are fixed.

Measured on profullstack/cli-tools#103 with the language resolved the
way the CLI resolves it -- by shebang, since the file is extensionless:
4 findings -> 2, and the two that go are exactly the two GitHub reported
at dev2/dev2-site:136 and :1132. The two that remain are the real
interpolated SQL statements.
@github-actions

Copy link
Copy Markdown

ThreatCrush Security Scan

12 finding(s)

HIGH/CRITICAL: 1 | MEDIUM: 6 | LOW: 5

Severity Rule Location
HIGH secret-aws-access-key prd/0003-detect-hardcoded-secrets-before-they-are-committed-or-served.md:126
MEDIUM js-open-redirect apps/web/src/app/auth/login/page.tsx:50
MEDIUM js-unescaped-html-sink apps/web/src/app/hire/page.tsx:104
MEDIUM js-unescaped-html-sink apps/web/src/app/hire/page.tsx:108
MEDIUM js-open-redirect apps/web/src/components/funding/FundingClient.tsx:97
MEDIUM js-unescaped-html-sink apps/web/src/components/GuideReader.tsx:265
MEDIUM js-uninitialized-buffer packages/scan/src/node-rules.ts:456
LOW secret-generic-credential PRD.md:269
LOW tls-verification-disabled prd/0004-find-dangerous-code-patterns-without-pretending-to-be-a-compiler.md:121
LOW tls-verification-disabled prd/0004-find-dangerous-code-patterns-without-pretending-to-be-a-compiler.md:122
LOW sh-remote-script-execution scripts/smoke-test.sh:47
LOW secret-aws-access-key scripts/smoke-test.sh:112

Snippets are redacted; ThreatCrush never prints matched credential material.

@ralyodio
ralyodio merged commit 02f200a into master Sep 25, 2026
11 checks passed
@ralyodio
ralyodio deleted the fix/py-ssrf-constant-host branch September 25, 2026 13:12
@ralyodio ralyodio mentioned this pull request Sep 25, 2026
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.

1 participant