map: adapter summaries — argument roles and narrow candidate families - #125
Merged
Conversation
|
Comprehensive adapter summaries integrate argument roles into dataflow analysis. 🎯 Quality: 82% Excellent · 📦 Size: Extra Large — strongly consider breaking this down 🛡️ Standards: no pre-flight fit check ran for this change — wire 📈 This month: Your 57th PR — above team average · Averaging Good |
Contributor
Author
|
/review |
daniloradovic
approved these changes
Aug 13, 2026
patchstackdave
force-pushed
the
feat/map-argument-roles
branch
from
August 13, 2026 13:13
1e08458 to
7ef18c9
Compare
Track 2, step 1: the review's recommendation to use small per-library summaries BEFORE
building a whole-program dataflow engine. Which ARGUMENT received the value decides which
mitigation class applies, so this is the gate that lets `ruleGeneratable` ever be true.
Every flow now reports `argumentRole` (command | file | args | url | init | body | options |
path | content | sql | values | columns | column | value | code | unknown), taken from a
per-sink-kind summary table so an overloaded name (`get`) can't be read as the wrong thing:
exec(command) · execFile(file, args) · spawn(command, args)
fetch(url, init) · axios.get(url, options) · axios.post(url, body)
fs.readFile(path) · fs.writeFile(path, content)
pool.query(sql, values) · .insert(values) · .eq(column, value)
eval(code)
`candidateFamily` is then assigned only for pairs where a request value arriving is
inherently dangerous AND a rule can express it: http+url → ssrf, exec+command →
command-injection, fs+path → path-traversal, db+sql → sql-injection, eval+code →
code-injection. `ruleGeneratable` becomes true only with precise evidence, a non-null
runtime parameter, a local sink call site, a modelled role, and such a family.
The refusals matter as much as the acceptances, and are tested explicitly:
fs.writeFileSync("/tmp/x", req.body.contents) role=content → NOT generatable
db.from(t).insert({ title: req.body.title }) role=values → NOT generatable
axios.post(url, req.body.payload) role=body → NOT generatable
pool.query(sql, [req.body.id]) role=values → NOT generatable
Each is a PROVEN flow — real reachability signal — but not a blockable pattern on its own,
which is exactly the over-reach the review warned against (path vs contents, generic db
values). The reason is reported rather than the flow being dropped.
Illustrative: the real reference app yields 0 candidates (all its flows are db `values`),
while an Express fixture with the high-risk sinks yields exactly ssrf + path-traversal +
command-injection.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… under tests/map/
The corpus is what makes candidate generation measurable rather than merely tested. Six
declarative cases across the stacks AI builders actually emit — Lovable/TanStack+Supabase
server fns, Express+axios+fs+child_process, Next App Router + a server action,
Fastify+pg (raw sql vs bound values), a Supabase Edge Function, and a case of shapes that
must yield nothing (route param, dynamic computed key, spread into sink).
Each case declares the candidates it expects as `family @ runtimeParameter` plus the proven
flows that must be REFUSED. Two failure modes are measured separately:
- WRONG-INPUT: a candidate nobody declared — the rule would pin the wrong parameter.
Asserted to be ZERO. This is the metric that governs auto-promotion.
- MISSED: a declared candidate absent — a recall gap; loud, but a lesser sin than a
wrong pin.
Plus two invariants across every case: a candidate never exists without a runtime
coordinate, and every refusal carries a reason (silence is what makes a map untrustworthy).
The corpus immediately earned its keep: a Next SERVER ACTION produced no inputs at all,
because its first parameter IS the payload (`export async function report(input) {
exec(input.job) }`) rather than a Request — so it could never yield a candidate. Payload-
style entries now treat that parameter as the input container (source `server-fn-data`),
which is also the shape TanStack server fns use without a validator.
Also groups the nine map test files under tests/map/, mirroring tests/protect/.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
patchstackdave
force-pushed
the
feat/map-argument-roles
branch
from
August 13, 2026 13:16
7ef18c9 to
aeba7a7
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Track 2, step 1. The review recommended small per-library adapter summaries before any whole-program dataflow engine, because which argument received the value decides which mitigation class applies. This is the gate that lets
ruleGeneratableever be true.Argument roles
Every flow now reports
argumentRole, from a table keyed by sink kind so an overloaded name (get) can't be read as the wrong thing:Narrow candidate families
candidateFamilyis assigned only where a request value arriving is inherently dangerous and a rule can express the mitigation:ssrfcommand-injectionpath-traversalsql-injectioncode-injectionruleGeneratable: truerequires all of: precise evidence · non-nullruntimeParameter· a local sink call site · a modelled role · a candidate family.The refusals matter as much as the acceptances
Each of these is a proven flow — genuine reachability signal — but not a blockable pattern by itself. All are tested explicitly:
That's the over-reach the review warned about by name (path vs contents; generic database values). The reason is reported, not the flow dropped — so it stays useful for reachability while being ineligible for a blocking rule.
Illustrative
The real reference app yields 0 candidates (every flow lands in a db
valuesargument). An Express fixture with the high-risk sinks yields exactly ssrf + path-traversal + command-injection. A generatable candidate carries everything a compiler needs: route + method,runtimeParameter, sink package, evidence span, and the endpointfingerprintfor staleness.811 tests green; typecheck clean.
Still ahead (Track 2)
The finer confidence taxonomy (
exact/transformed/interprocedural), the candidate lifecycle (dry-run → observe → promote → retire on map drift), and corpus metrics — plan §10.