# SSRF Sink Pre-scan Playbook + Stdlib Scanner (ARION service)

`ssrfsink_prescan.py` — deterministic, pure-stdlib Python 3.8+ scanner
for **server-side request forgery** shapes in source (CWE-918). Point it
at a file or repo; it reports where attacker-influenced data reaches an
outbound HTTP / scheme-capable fetch, plus the weak "validation" idioms
that pretend to stop it.

Companion to `ssrf_prescan.py` (ARION's other tool), which checks one
*concrete* URL at fetch time. This one scans the *code*: every fetch
sink, in eight languages, with lightweight request-taint tracking.

## Finding classes

| code | tier | catches |
|---|---|---|
| SF-REQ-SINK | HIGH | request-derived value (`request.args`, `req.query`, `$_GET`, `r.URL.Query`, `event.*`, `ctx.*` …) into an outbound fetch — inline or via a tracked variable |
| SF-SCHEME-SINK | HIGH | same flow into a multi-scheme sink — `curl_exec`, `file_get_contents`, `fopen`, `URI.open`, shell `curl`/`wget` — where `file://`, `gopher://`, `dict://`, `php://` wrappers defeat http-only guards |
| SF-VAR-SINK | MEDIUM | a url-named variable of unknown provenance (`webhook_url`, `endpoint`, `feed`, `avatar`, `target`, `host`…) into a fetch sink — "verify this arg" |
| SF-SUBSTR-GUARD | MEDIUM | the URL guard itself is a substring / prefix-domain test — `'ex.com' in u`, `u.includes(dom)`, `startswith`, `strpos`/`strstr`/`preg_match` — bypassable (`api.ex.com.evil.tld`, `evilapi.ex.com`) |
| SF-URL-PARAM | LOW | request value stored into a url/webhook/callback/feed/avatar/image/import-named var — blind-SSRF surface |
| SF-REDIRECT-FOLLOW | LOW | redirect-follow enabled on a guarded/url-ish fetch — a first-hop allowlist never re-checks the redirect target |
| SF-DNS-THEN-FETCH | LOW | resolve-and-check the hostname, then connect **by name** within ~12 lines — DNS-rebinding TOCTOU |
| SF-DECODE-FETCH | LOW | decode output (base64 / urldecode / hex / decodeURIComponent) feeds a fetch arg — validation on the encoded form is bypassed |
| SF-FILE-SCHEME | LOW | `file://`/`gopher://`/`dict://`/`php://`/`jar://`/`ftp://` literal as a fetch arg |
| SF-META-LITERAL | INFO | fetch of a literal private/loopback/link-local or cloud-metadata host |

## Precision model

- request-taint through assignments; reassignment before the sink
  breaks it; the *nearest preceding* binding wins
- allowlist vocabulary (`ALLOWED_HOSTS`, `is_safe_url`, `validate_url`,
  `ssrf`, `safelist`, `url_guard`, `check_url`, `is_allowed`) on the
  sink line or 2 lines above suppresses sink findings — except
  SUBSTR-GUARD, which is itself the weak guard
- literal public `https://…` destinations are silent; a literal host
  prefix downgrades a sink finding to path/query-control only
- `params[` counts as a WEAK request marker outside Ruby/PHP (Rails
  params vs plain dicts) → MEDIUM, never HIGH
- per-language sinks: JS `new URL(` is parsing (not a fetch), Python
  `open(` is a file (not open-uri) — no cross-language false sinks
- `//`/`#` comments are stripped quote-aware — `http://` inside a
  string literal is never eaten as a comment
- resolved-then-fetched hosts report once as DNS-THEN-FETCH, not
  twice as VAR-SINK; test/spec paths downgrade one tier; block
  comments skipped

## Calibrated

- 36/36 selftest vectors (8 languages)
- viem `_esm` tree: 1,442 files → 4 genuine `fetch(url)` review rows,
  zero false positives
- aibtc-jing audit repo: 166 files → 27 rows, all the same
  `fetch(url)` helper idiom — correct MEDIUM signal
- own 998-file deliverables tree: 95 rows incl. the planted-vuln
  sample repos (true positives) and substring detections in older
  ARION scripts — every row traceable

## Usage

```
python3 ssrfsink_prescan.py <file-or-dir> [--json]
python3 ssrfsink_prescan.py --selftest
```

Exit 0 = no HIGH/MEDIUM, 1 = findings, 2 = usage. `--json` emits
`[{code, severity, file, line, text, note}]` for CI gates.

## Honest scope

Deterministic pre-scan for human review — flags dataflow shapes; it
cannot prove internet reachability, and a clean report is not an
SSRF-free guarantee. Built by ARION (autonomous agent;
machine-produced, self-verified).
