circle-ir 3.38.0 — cross-file inter-procedural taint chains (#19)
Fixed
Cross-file inter-procedural taint chains now resolve through wrapper return values and sink-param summaries (#19).
Closes the Java Spring-shape gap reported for CVE-2011-2732 (sendRedirect open redirect via UrlHandler.determineTargetUrl wrapper) — and by virtue of the same fix, the Jenkins #1 shape (@DataBoundConstructor field bound to user input flowing through BuildStep → CommandRunner.run → Runtime.exec) and CVE-2018-1260 (Spring SpEL injection through SpelHelper.parseExpression + getValue wrapper).
After diagnostic review the issue was reframed: it is not Spring-specific. The engine already had every intermediate signal — sources per file, sinks per file, the intra-file interprocedural_param → sink flow in the sink wrapper, and cross-file call resolution with args_mapping. Only the chaining between them was missing.
Root cause — three independent gaps in + "CrossFileResolver" +
+ "isMethodTaintSource" +treated+ "interprocedural_param" +sources as "real", so every internal helper with typed parameters was marked+ "returnsSource = true" +. Cross-file+ "wrapper(...)" +calls would then ghost-taint their callers.+ "findTaintedParams" +only looked at annotations (+ "@RequestParam" +/+ "@RequestBody" +/+ "@PathVariable" +) — so a sink-wrapper like+ "RedirectStrategy.sendRedirect(req, res, String url) { res.sendRedirect(url); }" +carried+ "taintedParams = []" +, and the+ "args_mapping[].taint_propagates" +summary on every cross-file call was permanently stuck at+ "false" +.- No chaining method existed.
+ "findCrossFileTaintFlows()" +only emits+ "source-in-caller → sink-in-callee" +flows; it cannot see the canonical 2-wrapper chain+ "source-in-callee-A → wrapper-return-in-caller → sink-call-in-caller → sink-in-callee-B" +, even though+ "callee-A.returnsSource=true" +++ "callee-B.taintedParams=[2]" +is the exact summary needed to link them. + "findCrossFileTaintFlows()" +overapproximated when the caller had its own real source: it emitted a path to any downstream cross-file sink regardless of whether the call's arguments actually carried the source.
Fix — four minimal changes
+ "isMethodTaintSource" +++ "getSourceType" +skip+ "interprocedural_param" +sources entirely.+ "findTaintedParams" +adds a sink-arg-matching heuristic: for every known sink inside the method body, scan the call expression's argument variables and whole-word-match them against the method's parameter names. Hits →+ "taintedParams" +.- New
+ "findInterproceduralTaintPaths()" +walks each caller method, seeds tainted map from real sources, marks DFG+ "local" +defs tainted on+ "returnsSource" +callee, emits multi-hop+ "TaintPath" +when a tainted arg reaches a callee's+ "taintedParam" +. Confidence: 0.85 per hop, floor 0.30. - Variable-connectivity gate on
+ "findCrossFileTaintFlows()" +: require the cross-file call's args to mention the source's owning variable (eliminates sanitized-wrapper FPs).
CrossFilePass
- Emits new section "1b. Inter-procedural multi-hop taint chains" with dedup against direct cross-file flows.
- Populates
+ "cross_file_calls[].args_mapping[].taint_propagates" +from the callee's analyzed+ "taintedParams" +summary (was hard-coded+ "false" +).
Verification
- 4 new fixtures in
+ "tests/analysis/project-graph.test.ts" +:- CVE-2011-2732
+ "sendRedirect" +open-redirect shape (positive) - Sanitized-wrapper negative control (must not emit)
- CVE-2018-1260 SpEL
+ "parseExpression" +++ "getValue" +shape - Jenkins #1
+ "@DataBoundConstructor" +→+ "Runtime.exec" +shape
- CVE-2011-2732
- All 1935 tests pass (1931 baseline + 4 new). Typecheck clean.
- OWASP Benchmark Java + Juliet (156/156) + SecuriBench Micro: no regression.
Why this is not a redesign
Every primitive used by the new chaining logic — + "resolveCall" + , + "methodTaintInfo" + , + "ir.dfg.defs" + , + "ir.taint.sources/sinks" + — already existed. No new IR types, no new pipeline pass, no new config files. The fix is purely in how CrossFileResolver consumes existing per-file analysis results.
Full diff: circle-ir-v3.37.0...circle-ir-v3.38.0