Skip to content

feat(taint): sink-side focus-metavariable + pattern-inside (→ 95.7%)#534

Merged
peaktwilight merged 1 commit into
mainfrom
feat/taint-sink-focus-inside
Jun 18, 2026
Merged

feat(taint): sink-side focus-metavariable + pattern-inside (→ 95.7%)#534
peaktwilight merged 1 commit into
mainfrom
feat/taint-sink-focus-inside

Conversation

@peaktwilight

@peaktwilight peaktwilight commented Jun 18, 2026

Copy link
Copy Markdown
Collaborator

Taint: sink-side focus-metavariable + pattern-inside (call-context) → load rate 94.6% → 95.7%

The symmetric sink-side analog of #533: focus-metavariable: $ARG + pattern-inside: <call-context> inside pattern-sinks/pattern-sanitizers → the focused argument of a concrete call (gated by the inside-context) is a sink. Reuses the focus-binding/signature infra from the parameter-source work; no engine changes (compiles to existing Call/MethodName matchers).

+23 rules → 95.7% (2051/2144), taint unsupported-shape 102 → 80. Step-1 upper bound was 12 distinct flippable; the realized +23 includes co-blocked rules whose source was already expressible and only the sink was missing (node-mysql-sqli, node-knex-sqli, ruby-pg-sqli, tainted-deserialization, tainted-filename, check-unsafe-reflection, express-ssrf, express-res-sendfile, file-inclusion, the dangerous-*-tainted-env-args family, etc.).

Precision (guarded)

Only concrete-callee call-context sinks compile (Call{assert}, MethodName{query,execute}). A free metavar callee with no metavariable-regex pin emits nothing — guard test unpinned_metavar_callee_sink_is_not_compiled. Per-language firing + safe near-miss bridge tests: PHP assert($tainted) fires / untainted literal doesn't; JS pool.query(tainted) fires / pool.format(...) (method outside pinned set) doesn't.

Verification (re-run on branch)

95.7% re-measured · both dogfood exit 0 · cargo test 0 failed · clippy -D warnings clean · fmt clean · 3-file additive diff (+547 in semgrep_taint.rs), no engine/Cargo/baseline changes · COMPATIBILITY.md updated.

Summary by CodeRabbit

  • New Features

    • Added support for "focus-on-call-argument" sink patterns in taint rules, enabling more precise detection of security vulnerabilities when tainted values flow into specific function or method calls.
  • Documentation

    • Updated compatibility documentation with guidance on the new call-argument sink pattern capability and usage constraints.

…e + call-context pattern-inside)

The sink-side analog of the parameter-as-source shape (PR #533). A common
rejected sink shape names a focused metavariable that is an ARGUMENT of a call
given by a pattern-inside/pattern context, e.g.

  pattern-sinks:
    - patterns:
        - pattern: assert($SINK, ...)
        - pattern: $SINK            # focus

  pattern-sinks:
    - patterns:
        - focus-metavariable: $QUERY
        - pattern-either:
            - pattern-inside: $POOL.query($QUERY, ...)
            - pattern-inside: $POOL.execute($QUERY, ...)

Semgrep means "the focused argument, when it appears inside this call, is a
sink." The dropped-constraint fallback empties the sink role (no bare metavar
is a usable sink), rejecting the rule.

Recognise this shape and compile the call context to the existing Call /
MethodName sink matchers for the call's callee:
- a concrete callee (assert, redirect_to, YAML.load) -> one Call;
- a $RECV.$METH(...) callee pinned by an anchored-alternation metavariable-regex
  (^(query|execute)$, \b(include|require)\b) -> one MethodName per listed name;
- a $FUNC(...) pure-metavariable callee pinned by such a regex -> one Call per name.

These reuse the existing taint-gated call sinks, which fire only when a tracked
value reaches the call's arguments, so the compiled sink is bounded to the
concrete callee/method name AND tainted data -- never an over-broad bare-node
sink. Recognition is bounded: the focused metavariable must be in the call's
argument list (or the arg list is a wildcard), and a free metavariable callee
with no pinning regex produces no matcher (we never invent a name), falling
through to the normal graceful-degradation extraction. No engine changes: the
new logic emits only existing Call/MethodName GenericMatchers.

registry coverage: 94.6% (2028) -> 95.7% (2051), +23 rules; taint
unsupported-shape 102 -> 80. Adds firing + clean safe-near-miss bridge tests
for PHP (concrete Call) and JS (regex-pinned MethodName), plus an over-broad
guard test that an unpinned metavariable callee is not compiled.
@coderabbitai

coderabbitai Bot commented Jun 18, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 776792d9-cd1e-4db8-b90b-bf06f3286c99

📥 Commits

Reviewing files that changed from the base of the PR and between ede13a7 and f9c2fe2.

📒 Files selected for processing (3)
  • COMPATIBILITY.md
  • docs/parity/registry-coverage.md
  • src/rules/semgrep_taint.rs

📝 Walkthrough

Walkthrough

The PR implements a new "focus-on-call-argument" sink/sanitizer shape in the Semgrep taint bridge (semgrep_taint.rs). When a patterns: block focuses a metavariable that appears inside a pinned call expression, it now compiles into concrete Call/MethodName matchers instead of falling back to generic extraction. Documentation and registry coverage stats are updated to reflect the improvement.

Changes

Focus-on-call-argument sink shape

Layer / File(s) Summary
compile_entry recognition path and helper functions
src/rules/semgrep_taint.rs
compile_entry gains an early-return path for the new shape. New helpers extract focused metavariables, parse call-context patterns (pattern:/pattern-inside:), validate argument-list membership with token-boundary checks, and compile anchored-alternation regexes into concrete Call/MethodName matchers; unresolvable callees produce no matchers and fall back to existing extraction.
End-to-end tests
src/rules/semgrep_taint.rs
Tests added for PHP assert-use (concrete Call sink firing on tainted argument), JS node-mysql-sqli-style (pinned MethodName sinks query/execute with near-miss rejection), and a negative guard confirming unpinned metavariable callees cause rule skip rather than an overly-broad sink.
Documentation and coverage stats
COMPATIBILITY.md, docs/parity/registry-coverage.md
COMPATIBILITY.md documents the new shape's behavior, pinning requirements, and non-call non-recognition. Registry coverage report updated with a new 95.7% headline load rate, refreshed skip-reason histogram, per-language metrics, and top-skip-reason counts.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • 0sec-labs/foxguard#497: Extends the same semgrep_taint.rs sink/sanitizer compilation to emit MethodName-based matchers from $METAVAR.method($X) receiver patterns, sharing the same matcher types targeted here.
  • 0sec-labs/foxguard#506: Modifies semgrep_taint.rs and COMPATIBILITY.md around pattern:/patterns: handling in taint sinks/sanitizers, directly adjacent to the focus-metavariable call-argument shape added in this PR.

Poem

🐇 A metavariable hops into a call,
The sink sniffs the callee and pins it — that's all.
assert($X, ...) fires, query too,
Wild callees get skipped (as they ought to do).
The registry cheers: 95.7% now loads clean,
The sharpest taint bridge this warren has seen! 🎉

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/taint-sink-focus-inside

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@peaktwilight peaktwilight merged commit d3bf4a9 into main Jun 18, 2026
17 of 18 checks passed
@peaktwilight peaktwilight deleted the feat/taint-sink-focus-inside branch June 18, 2026 17:55
assert($x);
}
"#;
let tree = parse_file(src, Language::Php).expect("php fixture should parse");

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

foxguard · MEDIUM · rs/no-unwrap-in-lib (CWE-248)

.expect() can panic at runtime — use proper error handling with ? or match

assert($x);
}
"#;
let tree = parse_file(src, Language::Php).expect("php fixture should parse");

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

foxguard · MEDIUM · rs/no-unwrap-in-lib (CWE-248)

.expect() can panic at runtime — use proper error handling with ? or match

pool.query(q);
}
"#;
let tree = parse_file(src, Language::JavaScript).expect("js fixture should parse");

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

foxguard · MEDIUM · rs/no-unwrap-in-lib (CWE-248)

.expect() can panic at runtime — use proper error handling with ? or match

pool.format(q);
}
"#;
let tree = parse_file(src, Language::JavaScript).expect("js fixture should parse");

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

foxguard · MEDIUM · rs/no-unwrap-in-lib (CWE-248)

.expect() can panic at runtime — use proper error handling with ? or match

/// name), so the rule is rejected rather than producing an any-call sink.
#[test]
fn unpinned_metavar_callee_sink_is_not_compiled() {
let v: YamlValue = serde_yaml_ng::from_str(

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

foxguard · MEDIUM · rs/no-unwrap-in-lib (CWE-248)

.unwrap() can panic at runtime — use proper error handling with ? or match

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