Repository navigation
src2sink 2.0.0
Detection correctness. Where 1.1.0 recovered callers that were missing, this work
is mostly about output that was wrong — findings the tool stated confidently
and should not have. Issues are tracked as OI-n in
docs/issues/src2sink-open-issues.md;
each is reproduced by a named regression test.
Why a major bump. SCHEMA_VERSION is unchanged at 2 and existing
metabases still load, but detail.parameterised — a documented field — changed
from a boolean to a posture string. Anything reading it as a boolean breaks, and
docs/releasing.md makes a change in a documented field's
meaning a major bump regardless of whether the file still parses.
Before you upgrade: the first build after installing this version rescans
every repository, because records now record which detector produced them and
none of the existing ones do (OI-16). Budget for a full fleet scan once.
Fixed
-
The
sqlfamily matched on method name alone (OI-7).execute,query
andupdateare ordinary method names, and the receiver — available on every
grammar's AST node — was read and discarded. SohttpClient.execute(request),
messageDigest.update(data)andcall.execute()were catalogued as
unparameterised SQL execution sinks athighconfidence. Worse, an execution
sink feedslink_raw_code_payload_endpoints, so an HTTP proxy carrying a field
namedsqlmanufactured araw-code-payloadfinding — a fabricated
injection endpoint that sends an analyst to audit code that was never
vulnerable. Asqlnode now requires positive evidence: a database receiver, a
library name in the call text, or file-level SQL evidence. -
Version prefixes outranked real route names (
OI-1). Confidence graded by
which structural rule fired rather than how much meaning matched, so a repo
exposing a bare/v1beat the service actually exposing/stock— and the
correct candidate was discarded, not merely ranked lower. Segments naming a
version or a layer are now dropped before comparison, and equal-confidence
candidates are ranked by specificity. -
SQL assembled by formatting produced no node at all (
OI-8).
String.format,.formatted,MessageFormat.format, Python%and.format
were uncovered. Two adjacent holes mattered more: the concatenation patterns
excluded both quote characters, so"… WHERE ref = '" + ref + "'"— the
canonical injection shape — could not be spanned; and the template pattern
required interpolation before the SQL keyword, so"SELECT … ${id}"rarely
fired. -
parameterisedclaimed a safety property it could not establish (OI-10).
A placeholder does not undo a concatenation in the same statement, and the
value was computed from any SQL-shaped literal anywhere in the file — so an
unrelated safe constant certified an injectable call site while the scan
simultaneously reported that statement as asqlsource. -
A base-query constant hid the concatenation appended to it (
OI-11). In
SAFE + " AND ref = '" + ref + "'"the keyword lives in the constant and the
concatenated fragments carry none, so nothing matched: no finding was emitted
and the call site was reported safe. Constants are now resolved through a
per-file symbol table, the same mechanism already used for constant-mediated
URLs. -
Tracing rebuilt the whole fleet service-call graph for every target
(OI-14). The graph is fleet-wide and target-independent, so a batch of N
traces built the identical graph N times and then filtered it N times. It is
now built once per run: 10 traces over a 200-repo fleet fell from 22.05s to
0.04s. Separately, the graph build itself re-normalised every candidate route
string on every comparison — 4.5M calls for 150 repos — which memoisation cuts
by 4.2x (400 repos: 38.28s to 9.15s). Reports are unchanged. -
A detection fix never reached a repository that had not changed
(OI-16). The incremental scan skipped a repo whose git sha matched the sha
in its existing record — keying the cache on what was scanned, but not on what
scanned it. So every fix in this release reached only the repositories that
happened to commit afterwards, and nothing said so, because a record did not
record which detector produced it. Records now carry adetection_versionand
are rebuilt when it differs. The first build after upgrading rescans the
whole fleet, since no existing record carries the field; after that, findings
from superseded detectors disappear. A new gate fails the build when an
extractor changes without a version bump.
Changed
detail.parameterisedis now a posture, not a boolean. Values are
parameterised,mixed,raw,staticorunknown.mixed— a placeholder
in a statement that is also concatenated — is the case the boolean could not
express.SCHEMA_VERSIONis unchanged at2and existing metabases still
load: a pre-2.0.0true/falseis reported asunknownrather than
translated, because that boolean was produced by the heuristic this release
removes.sqlandraw-code-payloadcounts fall. This is withdrawn false
positives, not lost coverage — expect the drop on any dashboard tracking them.- Service-call edges resolved through a bare
/v1or/apidisappear, and
edges that previously rankedlowthrough a version prefix are nowmedium.
trace --pathfiltering is deliberately unchanged: filtering and routing are
different questions and now use different predicates. detail.receiveradded tosqlsink nodes.
Added
- A mutation gate (
make mutation,scripts/mutation_check.py, in theci
chain). Coverage says the tests ran; this says they would notice. It
reintroduces each catalogued defect in a sandboxed copy of the tree and
requires the suite to fail. On its first runs it found two vacuous tests, two
pieces of dead logic, and one untested branch — see the commit history.