Most CI scanners match a pattern on a line. The Firewall follows the value — through variables, helper functions, across files, and down import chains — until it reaches a dangerous sink. Five analysis layers, 124 gates, and a calibration that means it doesn't cry wolf.
A user-controlled value rarely reaches the sink on the same line it entered. The Firewall tracks it the whole way — the 11 dataflow gates run all five layers.
The user value is the sink argument, inline — requests.get(request.args['url']).
The value flows through variables before the sink — the two-step a regex misses.
The value is passed to a local helper whose parameter reaches the sink.
The helper is imported from another module — resolved through the import graph.
A helper that calls a helper that calls the sink — followed to a fixpoint, intra-file and across imports.
# utils.py def fetch(u): requests.get(u) # the sink def load(u): fetch(u) # chain, level 2 # routes.py from .utils import load def handler(): load(request.args['url']) # user → load → fetch → SSRF · CAUGHT
Each gate is calibrated to the unambiguous vulnerability shape and scoped out of the adjacent safe pattern. The 11 dataflow gates go all five layers deep.
A gate that cries wolf gets disabled within a week. Every gate is calibrated to zero false alarms on real production code — and the classification brain never leaves your runner.
Across 5,000+ real repos — the injection dataflow layers fire on the vulnerable shape and stay silent on the sanitized one (a parameterized query, a validated URL, a basenamed path).
The open-source runner extracts a structural fingerprint in place and sends only its shape. Your source never leaves the CI runner; the classification rules never ship to it.
Add the gate to CI, protect a private repo for $29/mo, and block on a reliable cross-tenant leak — while every failure path fails open, so an outage never breaks your build.