circle-ir 4.16.1 → 4.18.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/analysis/advisories.d.ts +186 -0
- package/dist/analysis/advisories.d.ts.map +1 -0
- package/dist/analysis/advisories.js +391 -0
- package/dist/analysis/advisories.js.map +1 -0
- package/dist/analysis/comment-lines.d.ts +14 -0
- package/dist/analysis/comment-lines.d.ts.map +1 -0
- package/dist/analysis/comment-lines.js +34 -0
- package/dist/analysis/comment-lines.js.map +1 -0
- package/dist/analysis/config-loader.d.ts.map +1 -1
- package/dist/analysis/config-loader.js +59 -5
- package/dist/analysis/config-loader.js.map +1 -1
- package/dist/analysis/constant-caller-gate.d.ts +2 -2
- package/dist/analysis/constant-caller-gate.d.ts.map +1 -1
- package/dist/analysis/constant-caller-gate.js +205 -29
- package/dist/analysis/constant-caller-gate.js.map +1 -1
- package/dist/analysis/constant-propagation/index.d.ts +2 -0
- package/dist/analysis/constant-propagation/index.d.ts.map +1 -1
- package/dist/analysis/constant-propagation/index.js +10 -0
- package/dist/analysis/constant-propagation/index.js.map +1 -1
- package/dist/analysis/constant-propagation/propagator.d.ts +15 -0
- package/dist/analysis/constant-propagation/propagator.d.ts.map +1 -1
- package/dist/analysis/constant-propagation/propagator.js +224 -20
- package/dist/analysis/constant-propagation/propagator.js.map +1 -1
- package/dist/analysis/constant-propagation/types.d.ts +2 -0
- package/dist/analysis/constant-propagation/types.d.ts.map +1 -1
- package/dist/analysis/constant-propagation.d.ts +1 -1
- package/dist/analysis/constant-propagation.d.ts.map +1 -1
- package/dist/analysis/constant-propagation.js +1 -1
- package/dist/analysis/constant-propagation.js.map +1 -1
- package/dist/analysis/entry-point-detection.d.ts.map +1 -1
- package/dist/analysis/entry-point-detection.js +72 -0
- package/dist/analysis/entry-point-detection.js.map +1 -1
- package/dist/analysis/interprocedural.d.ts.map +1 -1
- package/dist/analysis/interprocedural.js +75 -8
- package/dist/analysis/interprocedural.js.map +1 -1
- package/dist/analysis/maven-declared.d.ts +72 -0
- package/dist/analysis/maven-declared.d.ts.map +1 -0
- package/dist/analysis/maven-declared.js +216 -0
- package/dist/analysis/maven-declared.js.map +1 -0
- package/dist/analysis/maven-version.d.ts +28 -0
- package/dist/analysis/maven-version.d.ts.map +1 -0
- package/dist/analysis/maven-version.js +172 -0
- package/dist/analysis/maven-version.js.map +1 -0
- package/dist/analysis/passes/constant-propagation-pass.d.ts.map +1 -1
- package/dist/analysis/passes/constant-propagation-pass.js +29 -1
- package/dist/analysis/passes/constant-propagation-pass.js.map +1 -1
- package/dist/analysis/passes/cross-file-pass.d.ts.map +1 -1
- package/dist/analysis/passes/cross-file-pass.js +84 -2
- package/dist/analysis/passes/cross-file-pass.js.map +1 -1
- package/dist/analysis/passes/csharp-sanitizer-credit.d.ts +7 -0
- package/dist/analysis/passes/csharp-sanitizer-credit.d.ts.map +1 -1
- package/dist/analysis/passes/csharp-sanitizer-credit.js +44 -1
- package/dist/analysis/passes/csharp-sanitizer-credit.js.map +1 -1
- package/dist/analysis/passes/insecure-cookie-pass.d.ts +2 -0
- package/dist/analysis/passes/insecure-cookie-pass.d.ts.map +1 -1
- package/dist/analysis/passes/insecure-cookie-pass.js +25 -3
- package/dist/analysis/passes/insecure-cookie-pass.js.map +1 -1
- package/dist/analysis/passes/interprocedural-pass.d.ts +11 -14
- package/dist/analysis/passes/interprocedural-pass.d.ts.map +1 -1
- package/dist/analysis/passes/interprocedural-pass.js +562 -0
- package/dist/analysis/passes/interprocedural-pass.js.map +1 -1
- package/dist/analysis/passes/language-sources-pass.d.ts +18 -1
- package/dist/analysis/passes/language-sources-pass.d.ts.map +1 -1
- package/dist/analysis/passes/language-sources-pass.js +1079 -14
- package/dist/analysis/passes/language-sources-pass.js.map +1 -1
- package/dist/analysis/passes/param-source-flow-gate-pass.d.ts.map +1 -1
- package/dist/analysis/passes/param-source-flow-gate-pass.js +82 -3
- package/dist/analysis/passes/param-source-flow-gate-pass.js.map +1 -1
- package/dist/analysis/passes/sink-filter-pass.d.ts +44 -2
- package/dist/analysis/passes/sink-filter-pass.d.ts.map +1 -1
- package/dist/analysis/passes/sink-filter-pass.js +690 -12
- package/dist/analysis/passes/sink-filter-pass.js.map +1 -1
- package/dist/analysis/passes/taint-propagation-pass.d.ts.map +1 -1
- package/dist/analysis/passes/taint-propagation-pass.js +298 -19
- package/dist/analysis/passes/taint-propagation-pass.js.map +1 -1
- package/dist/analysis/passes/tls-verify-disabled-pass.d.ts +18 -0
- package/dist/analysis/passes/tls-verify-disabled-pass.d.ts.map +1 -1
- package/dist/analysis/passes/tls-verify-disabled-pass.js +161 -0
- package/dist/analysis/passes/tls-verify-disabled-pass.js.map +1 -1
- package/dist/analysis/passes/weak-random-pass.d.ts +6 -0
- package/dist/analysis/passes/weak-random-pass.d.ts.map +1 -1
- package/dist/analysis/passes/weak-random-pass.js +29 -0
- package/dist/analysis/passes/weak-random-pass.js.map +1 -1
- package/dist/analysis/promote-cross-file-flows.d.ts +17 -0
- package/dist/analysis/promote-cross-file-flows.d.ts.map +1 -0
- package/dist/analysis/promote-cross-file-flows.js +32 -0
- package/dist/analysis/promote-cross-file-flows.js.map +1 -0
- package/dist/analysis/rust-literal-guard.d.ts.map +1 -1
- package/dist/analysis/rust-literal-guard.js +100 -1
- package/dist/analysis/rust-literal-guard.js.map +1 -1
- package/dist/analysis/sbom.d.ts +100 -0
- package/dist/analysis/sbom.d.ts.map +1 -1
- package/dist/analysis/sbom.js +514 -3
- package/dist/analysis/sbom.js.map +1 -1
- package/dist/analysis/semver-version.d.ts +30 -0
- package/dist/analysis/semver-version.d.ts.map +1 -0
- package/dist/analysis/semver-version.js +76 -0
- package/dist/analysis/semver-version.js.map +1 -0
- package/dist/analysis/taint-matcher.d.ts +5 -0
- package/dist/analysis/taint-matcher.d.ts.map +1 -1
- package/dist/analysis/taint-matcher.js +226 -4
- package/dist/analysis/taint-matcher.js.map +1 -1
- package/dist/analyzer.d.ts +57 -0
- package/dist/analyzer.d.ts.map +1 -1
- package/dist/analyzer.js +69 -6
- package/dist/analyzer.js.map +1 -1
- package/dist/browser/circle-ir.js +3360 -314
- package/dist/core/circle-ir-core.cjs +783 -220
- package/dist/core/circle-ir-core.js +783 -220
- package/dist/core/extractors/dfg.js +205 -2
- package/dist/core/extractors/dfg.js.map +1 -1
- package/dist/core/extractors/ts-declaration-detail.d.ts +39 -0
- package/dist/core/extractors/ts-declaration-detail.d.ts.map +1 -0
- package/dist/core/extractors/ts-declaration-detail.js +266 -0
- package/dist/core/extractors/ts-declaration-detail.js.map +1 -0
- package/dist/core/extractors/types.js +53 -9
- package/dist/core/extractors/types.js.map +1 -1
- package/dist/graph/analysis-pass.d.ts +4 -1
- package/dist/graph/analysis-pass.d.ts.map +1 -1
- package/dist/graph/analysis-pass.js +4 -1
- package/dist/graph/analysis-pass.js.map +1 -1
- package/dist/graph-facts/dead-code.d.ts +62 -0
- package/dist/graph-facts/dead-code.d.ts.map +1 -0
- package/dist/graph-facts/dead-code.js +90 -0
- package/dist/graph-facts/dead-code.js.map +1 -0
- package/dist/graph-facts/dependency-calls.d.ts +86 -0
- package/dist/graph-facts/dependency-calls.d.ts.map +1 -0
- package/dist/graph-facts/dependency-calls.js +93 -0
- package/dist/graph-facts/dependency-calls.js.map +1 -0
- package/dist/graph-facts/index.d.ts +10 -0
- package/dist/graph-facts/index.d.ts.map +1 -0
- package/dist/graph-facts/index.js +10 -0
- package/dist/graph-facts/index.js.map +1 -0
- package/dist/graph-facts/maven-packages.d.ts +42 -0
- package/dist/graph-facts/maven-packages.d.ts.map +1 -0
- package/dist/graph-facts/maven-packages.js +133 -0
- package/dist/graph-facts/maven-packages.js.map +1 -0
- package/dist/graph-facts/reach.d.ts +203 -0
- package/dist/graph-facts/reach.d.ts.map +1 -0
- package/dist/graph-facts/reach.js +219 -0
- package/dist/graph-facts/reach.js.map +1 -0
- package/dist/graph-facts/removal-reach.d.ts +73 -0
- package/dist/graph-facts/removal-reach.d.ts.map +1 -0
- package/dist/graph-facts/removal-reach.js +64 -0
- package/dist/graph-facts/removal-reach.js.map +1 -0
- package/dist/index.d.ts +21 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +20 -3
- package/dist/index.js.map +1 -1
- package/dist/navigation/go-graph.d.ts +68 -0
- package/dist/navigation/go-graph.d.ts.map +1 -0
- package/dist/navigation/go-graph.js +753 -0
- package/dist/navigation/go-graph.js.map +1 -0
- package/dist/navigation/index.d.ts +7 -1
- package/dist/navigation/index.d.ts.map +1 -1
- package/dist/navigation/index.js +6 -0
- package/dist/navigation/index.js.map +1 -1
- package/dist/navigation/java-entry-tables.d.ts +43 -0
- package/dist/navigation/java-entry-tables.d.ts.map +1 -0
- package/dist/navigation/java-entry-tables.js +56 -0
- package/dist/navigation/java-entry-tables.js.map +1 -0
- package/dist/navigation/java-graph.d.ts +64 -0
- package/dist/navigation/java-graph.d.ts.map +1 -0
- package/dist/navigation/java-graph.js +466 -0
- package/dist/navigation/java-graph.js.map +1 -0
- package/dist/navigation/navigation-index.d.ts +91 -2
- package/dist/navigation/navigation-index.d.ts.map +1 -1
- package/dist/navigation/navigation-index.js +372 -45
- package/dist/navigation/navigation-index.js.map +1 -1
- package/dist/navigation/python-graph.d.ts +73 -0
- package/dist/navigation/python-graph.d.ts.map +1 -0
- package/dist/navigation/python-graph.js +867 -0
- package/dist/navigation/python-graph.js.map +1 -0
- package/dist/navigation/ts-call-edges.d.ts +193 -0
- package/dist/navigation/ts-call-edges.d.ts.map +1 -0
- package/dist/navigation/ts-call-edges.js +2089 -0
- package/dist/navigation/ts-call-edges.js.map +1 -0
- package/dist/navigation/ts-entry-tables.d.ts +88 -0
- package/dist/navigation/ts-entry-tables.d.ts.map +1 -0
- package/dist/navigation/ts-entry-tables.js +50 -0
- package/dist/navigation/ts-entry-tables.js.map +1 -0
- package/dist/path-set/branches.d.ts +52 -0
- package/dist/path-set/branches.d.ts.map +1 -0
- package/dist/path-set/branches.js +124 -0
- package/dist/path-set/branches.js.map +1 -0
- package/dist/path-set/finding-diff.d.ts +63 -0
- package/dist/path-set/finding-diff.d.ts.map +1 -0
- package/dist/path-set/finding-diff.js +150 -0
- package/dist/path-set/finding-diff.js.map +1 -0
- package/dist/path-set/index.d.ts +18 -0
- package/dist/path-set/index.d.ts.map +1 -0
- package/dist/path-set/index.js +12 -0
- package/dist/path-set/index.js.map +1 -0
- package/dist/path-set/line-diff.d.ts +19 -0
- package/dist/path-set/line-diff.d.ts.map +1 -0
- package/dist/path-set/line-diff.js +98 -0
- package/dist/path-set/line-diff.js.map +1 -0
- package/dist/path-set/path-set.d.ts +88 -0
- package/dist/path-set/path-set.d.ts.map +1 -0
- package/dist/path-set/path-set.js +716 -0
- package/dist/path-set/path-set.js.map +1 -0
- package/dist/path-set/templates.d.ts +42 -0
- package/dist/path-set/templates.d.ts.map +1 -0
- package/dist/path-set/templates.js +119 -0
- package/dist/path-set/templates.js.map +1 -0
- package/dist/path-set/types.d.ts +195 -0
- package/dist/path-set/types.d.ts.map +1 -0
- package/dist/path-set/types.js +8 -0
- package/dist/path-set/types.js.map +1 -0
- package/dist/remedy-class/index.d.ts +12 -0
- package/dist/remedy-class/index.d.ts.map +1 -0
- package/dist/remedy-class/index.js +9 -0
- package/dist/remedy-class/index.js.map +1 -0
- package/dist/remedy-class/match.d.ts +165 -0
- package/dist/remedy-class/match.d.ts.map +1 -0
- package/dist/remedy-class/match.js +705 -0
- package/dist/remedy-class/match.js.map +1 -0
- package/dist/remedy-class/output-context.d.ts +25 -0
- package/dist/remedy-class/output-context.d.ts.map +1 -0
- package/dist/remedy-class/output-context.js +150 -0
- package/dist/remedy-class/output-context.js.map +1 -0
- package/dist/remedy-class/sha256.d.ts +18 -0
- package/dist/remedy-class/sha256.d.ts.map +1 -0
- package/dist/remedy-class/sha256.js +89 -0
- package/dist/remedy-class/sha256.js.map +1 -0
- package/dist/remedy-class/table.d.ts +130 -0
- package/dist/remedy-class/table.d.ts.map +1 -0
- package/dist/remedy-class/table.js +457 -0
- package/dist/remedy-class/table.js.map +1 -0
- package/dist/resolution/cross-file.d.ts +18 -0
- package/dist/resolution/cross-file.d.ts.map +1 -1
- package/dist/resolution/cross-file.js +194 -5
- package/dist/resolution/cross-file.js.map +1 -1
- package/dist/stable-ids/builder.d.ts +20 -0
- package/dist/stable-ids/builder.d.ts.map +1 -0
- package/dist/stable-ids/builder.js +65 -0
- package/dist/stable-ids/builder.js.map +1 -0
- package/dist/stable-ids/diff.d.ts +18 -0
- package/dist/stable-ids/diff.d.ts.map +1 -0
- package/dist/stable-ids/diff.js +37 -0
- package/dist/stable-ids/diff.js.map +1 -0
- package/dist/stable-ids/go.d.ts +28 -0
- package/dist/stable-ids/go.d.ts.map +1 -0
- package/dist/stable-ids/go.js +160 -0
- package/dist/stable-ids/go.js.map +1 -0
- package/dist/stable-ids/hash.d.ts +22 -0
- package/dist/stable-ids/hash.d.ts.map +1 -0
- package/dist/stable-ids/hash.js +167 -0
- package/dist/stable-ids/hash.js.map +1 -0
- package/dist/stable-ids/index.d.ts +30 -0
- package/dist/stable-ids/index.d.ts.map +1 -0
- package/dist/stable-ids/index.js +59 -0
- package/dist/stable-ids/index.js.map +1 -0
- package/dist/stable-ids/java.d.ts +28 -0
- package/dist/stable-ids/java.d.ts.map +1 -0
- package/dist/stable-ids/java.js +224 -0
- package/dist/stable-ids/java.js.map +1 -0
- package/dist/stable-ids/moves.d.ts +87 -0
- package/dist/stable-ids/moves.d.ts.map +1 -0
- package/dist/stable-ids/moves.js +222 -0
- package/dist/stable-ids/moves.js.map +1 -0
- package/dist/stable-ids/nodes.d.ts +32 -0
- package/dist/stable-ids/nodes.d.ts.map +1 -0
- package/dist/stable-ids/nodes.js +92 -0
- package/dist/stable-ids/nodes.js.map +1 -0
- package/dist/stable-ids/normalize.d.ts +35 -0
- package/dist/stable-ids/normalize.d.ts.map +1 -0
- package/dist/stable-ids/normalize.js +174 -0
- package/dist/stable-ids/normalize.js.map +1 -0
- package/dist/stable-ids/parse-errors.d.ts +24 -0
- package/dist/stable-ids/parse-errors.d.ts.map +1 -0
- package/dist/stable-ids/parse-errors.js +46 -0
- package/dist/stable-ids/parse-errors.js.map +1 -0
- package/dist/stable-ids/python.d.ts +27 -0
- package/dist/stable-ids/python.d.ts.map +1 -0
- package/dist/stable-ids/python.js +212 -0
- package/dist/stable-ids/python.js.map +1 -0
- package/dist/stable-ids/ts-parse-errors.d.ts +27 -0
- package/dist/stable-ids/ts-parse-errors.d.ts.map +1 -0
- package/dist/stable-ids/ts-parse-errors.js +75 -0
- package/dist/stable-ids/ts-parse-errors.js.map +1 -0
- package/dist/stable-ids/types.d.ts +106 -0
- package/dist/stable-ids/types.d.ts.map +1 -0
- package/dist/stable-ids/types.js +16 -0
- package/dist/stable-ids/types.js.map +1 -0
- package/dist/stable-ids/typescript.d.ts +30 -0
- package/dist/stable-ids/typescript.d.ts.map +1 -0
- package/dist/stable-ids/typescript.js +362 -0
- package/dist/stable-ids/typescript.js.map +1 -0
- package/dist/types/config.d.ts +9 -0
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/index.d.ts +53 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js.map +1 -1
- package/package.json +3 -3
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
*/
|
|
13
13
|
import { findJavaSpringReturnXSSSinks } from './language-sources-pass.js';
|
|
14
14
|
import { expressionFullyWrappedBySanitizer, identifierOutsideSanitizerCalls, identifierOutsideSanitizerCallsInText, propagateTaint } from '../taint-propagation.js';
|
|
15
|
-
import { isFalsePositive, isCorrelatedPredicateFP } from '../constant-propagation.js';
|
|
15
|
+
import { isFalsePositive, isCorrelatedPredicateFP, constPropScopedVar } from '../constant-propagation.js';
|
|
16
16
|
import { buildJavaTaintedVars, buildPythonTaintedVars, buildRustTaintedVars, findPythonTrustBoundaryViolations, findPythonReturnXSSSinks, findJavaWikiPluginReturnXSSSinks } from './language-sources-pass.js';
|
|
17
17
|
import { parseJavaArrayAccessExpression } from '../java-array-element-keys.js';
|
|
18
18
|
import { canSourceReachSink, sourceSemanticsAllowed } from '../findings.js';
|
|
@@ -20,6 +20,7 @@ import { walkBackwardDefs } from '../dfg-walk.js';
|
|
|
20
20
|
import { sanitizerCoversSink } from '../sanitizer-index.js';
|
|
21
21
|
import { HTML_CONTEXT_ENCODER_METHODS } from '../constant-propagation/patterns.js';
|
|
22
22
|
import { javaCanonicalStartsWithAllBare } from '../java-canonical-prefix.js';
|
|
23
|
+
import { shouldGateInterproceduralParam } from '../entry-point-detection.js';
|
|
23
24
|
/**
|
|
24
25
|
* cognium-dev#328 — the const-prop per-step veto, with two scoped exemptions.
|
|
25
26
|
*
|
|
@@ -385,6 +386,36 @@ function javaReceiverChainDerivedVar(rhsByLocal, localVar, seedVar) {
|
|
|
385
386
|
* over every line for every (source, derived local) pair, which made large
|
|
386
387
|
* files take seconds. Same per-line matching, done once.
|
|
387
388
|
*/
|
|
389
|
+
/** Line whose block scope owns `varName` as read on `useLine` (#476 / #597). */
|
|
390
|
+
function defLineForUse(graph, varName, useLine) {
|
|
391
|
+
const own = (graph.defsByLine.get(useLine) ?? []).find(d => d.variable === varName);
|
|
392
|
+
if (own)
|
|
393
|
+
return own.line;
|
|
394
|
+
const use = (graph.usesByLine.get(useLine) ?? []).find(u => u.variable === varName && u.def_id != null);
|
|
395
|
+
const d = use ? graph.defById.get(use.def_id) : undefined;
|
|
396
|
+
return d?.line ?? useLine;
|
|
397
|
+
}
|
|
398
|
+
/** Const-prop key for a flow step: block locals vs method-body locals (#476 / #597). */
|
|
399
|
+
function constPropFlowLookup(language, method, varName, blockLine, constProp, preferMethodScoped = false) {
|
|
400
|
+
if (language === 'go') {
|
|
401
|
+
return constPropScopedVar(method, varName, blockLine, constProp);
|
|
402
|
+
}
|
|
403
|
+
if (!method || varName.includes(':'))
|
|
404
|
+
return varName;
|
|
405
|
+
const blockScoped = constPropScopedVar(method, varName, blockLine, constProp);
|
|
406
|
+
const hasTaintBinding = (name) => constProp.tainted.has(name) || constProp.sanitizedVars.has(name);
|
|
407
|
+
if (blockScoped.includes(':b') && hasTaintBinding(blockScoped)) {
|
|
408
|
+
return blockScoped;
|
|
409
|
+
}
|
|
410
|
+
const methodScoped = `${method}:${varName}`;
|
|
411
|
+
if (hasTaintBinding(methodScoped)) {
|
|
412
|
+
return methodScoped;
|
|
413
|
+
}
|
|
414
|
+
if (preferMethodScoped && method) {
|
|
415
|
+
return methodScoped;
|
|
416
|
+
}
|
|
417
|
+
return constProp.lineBlockScopes?.has(blockLine) ? blockScoped : varName;
|
|
418
|
+
}
|
|
388
419
|
function indexJavaLocalAssignments(code) {
|
|
389
420
|
const declRe = /^\s*(?:public|private|protected|static|final|volatile|transient|\s)*\s*(?:[A-Za-z_][\w.]*(?:\s*<[^>]*>)?(?:\s*\[\s*\])*)\s+([A-Za-z_]\w*)\s*=\s*(.+?);\s*$/;
|
|
390
421
|
const assignRe = /^\s*([A-Za-z_]\w*)\s*=\s*(.+?);\s*$/;
|
|
@@ -408,6 +439,40 @@ function indexJavaLocalAssignments(code) {
|
|
|
408
439
|
}
|
|
409
440
|
return index;
|
|
410
441
|
}
|
|
442
|
+
function goCalleeBaseName(callee) {
|
|
443
|
+
const dot = callee.lastIndexOf('.');
|
|
444
|
+
return dot === -1 ? callee : callee.slice(dot + 1);
|
|
445
|
+
}
|
|
446
|
+
/** cognium-dev#783: callee from a Go `:=` or `var x =` assignment line (unqualified or dotted). */
|
|
447
|
+
function goConstantHelperCalleeOnLine(line) {
|
|
448
|
+
const m = line.match(/:=\s*([\w.]+)\s*\(/) ?? line.match(/\bvar\s+\w+\s*=\s*([\w.]+)\s*\(/);
|
|
449
|
+
if (!m)
|
|
450
|
+
return null;
|
|
451
|
+
return goCalleeBaseName(m[1]);
|
|
452
|
+
}
|
|
453
|
+
/** Same as {@link goConstantHelperCalleeOnLine} when the call spans the next lines after `:=` / `var`. */
|
|
454
|
+
function goConstantHelperCalleeFromLines(lines, lineNumber) {
|
|
455
|
+
const direct = goConstantHelperCalleeOnLine(lines[lineNumber - 1] ?? '');
|
|
456
|
+
if (direct)
|
|
457
|
+
return direct;
|
|
458
|
+
const cur = lines[lineNumber - 1] ?? '';
|
|
459
|
+
const partial = cur.match(/:=\s*([\w.]+)\s*$/) ?? cur.match(/\bvar\s+\w+\s*=\s*([\w.]+)\s*$/);
|
|
460
|
+
if (partial) {
|
|
461
|
+
const next = (lines[lineNumber] ?? '').trimStart();
|
|
462
|
+
if (next.startsWith('('))
|
|
463
|
+
return goCalleeBaseName(partial[1]);
|
|
464
|
+
}
|
|
465
|
+
if (!cur.includes(':=') && !/\bvar\s+\w+\s*=/.test(cur))
|
|
466
|
+
return null;
|
|
467
|
+
if (cur.includes('('))
|
|
468
|
+
return null;
|
|
469
|
+
for (let i = lineNumber + 1; i <= Math.min(lines.length, lineNumber + 4); i++) {
|
|
470
|
+
const m = (lines[i - 1] ?? '').match(/^\s*([\w.]+)\s*\(/);
|
|
471
|
+
if (m)
|
|
472
|
+
return goCalleeBaseName(m[1]);
|
|
473
|
+
}
|
|
474
|
+
return null;
|
|
475
|
+
}
|
|
411
476
|
function makeConstPropVeto(constProp, graph, sources, code, language, sanitizers) {
|
|
412
477
|
const lines = typeof code === 'string' ? code.split('\n') : [];
|
|
413
478
|
const selfReassign = new Map();
|
|
@@ -442,15 +507,26 @@ function makeConstPropVeto(constProp, graph, sources, code, language, sanitizers
|
|
|
442
507
|
};
|
|
443
508
|
return (step, sourceLine, sinkLine, sinkType) => {
|
|
444
509
|
const method = methodAtLine(graph, step.line);
|
|
445
|
-
const
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
// languages keep the unscoped lookup (#614).
|
|
449
|
-
const r = isFalsePositive(constProp, step.line, language === 'go' ? scopedVar : step.variable);
|
|
510
|
+
const blockLine = defLineForUse(graph, step.variable, step.line);
|
|
511
|
+
const fpLookup = constPropFlowLookup(language, method, step.variable, blockLine, constProp);
|
|
512
|
+
const r = isFalsePositive(constProp, step.line, fpLookup);
|
|
450
513
|
if (!r.isFalsePositive)
|
|
451
514
|
return false;
|
|
452
515
|
if (r.reason !== 'variable_not_tainted')
|
|
453
516
|
return true;
|
|
517
|
+
if (r.reason === 'variable_not_tainted' &&
|
|
518
|
+
language === 'java' &&
|
|
519
|
+
method &&
|
|
520
|
+
fpLookup === step.variable &&
|
|
521
|
+
!step.variable.includes('.')) {
|
|
522
|
+
const v = step.variable;
|
|
523
|
+
if (constProp.tainted.has(`${method}:${v}`))
|
|
524
|
+
return false;
|
|
525
|
+
for (const t of constProp.tainted) {
|
|
526
|
+
if (t.startsWith(`${method}:b`) && t.endsWith(`:${v}`))
|
|
527
|
+
return false;
|
|
528
|
+
}
|
|
529
|
+
}
|
|
454
530
|
// #589: the step variable came from a context-specific encoder
|
|
455
531
|
// (`escapeHtml`, `encodeForHTML`, …), which does not clean `trust_boundary`
|
|
456
532
|
// (a `session.setAttribute` sink). It is "not tainted" only for the other
|
|
@@ -466,8 +542,9 @@ function makeConstPropVeto(constProp, graph, sources, code, language, sanitizers
|
|
|
466
542
|
else {
|
|
467
543
|
const method = methodAtLine(graph, step.line);
|
|
468
544
|
if (method) {
|
|
469
|
-
if (unc.get(
|
|
545
|
+
if (unc.get(fpLookup)?.has(sinkType)) {
|
|
470
546
|
return false;
|
|
547
|
+
}
|
|
471
548
|
}
|
|
472
549
|
else if (unc.get(step.variable)?.has(sinkType)) {
|
|
473
550
|
return false;
|
|
@@ -543,10 +620,11 @@ function makeConstPropVeto(constProp, graph, sources, code, language, sanitizers
|
|
|
543
620
|
// cognium-dev#751: sink use after `uid := constantHelper(tainted)` must veto
|
|
544
621
|
// when the helper is in `constantReturningMethods`, even if the flow's
|
|
545
622
|
// source_line is the assignment line (rule E would otherwise keep it).
|
|
546
|
-
if (step.line === sinkLine &&
|
|
623
|
+
if (step.line === sinkLine &&
|
|
624
|
+
!constProp.tainted.has(fpLookup)) {
|
|
547
625
|
for (let ln = sourceLine; ln <= sinkLine; ln++) {
|
|
548
|
-
const
|
|
549
|
-
if (
|
|
626
|
+
const callee = goConstantHelperCalleeFromLines(lines, ln);
|
|
627
|
+
if (callee && constProp.constantReturningMethods?.has(callee)) {
|
|
550
628
|
return true;
|
|
551
629
|
}
|
|
552
630
|
}
|
|
@@ -841,7 +919,7 @@ export class TaintPropagationPass {
|
|
|
841
919
|
pushIfNew(f);
|
|
842
920
|
}
|
|
843
921
|
// Supplement: collection/iterator flows — with FP filtering
|
|
844
|
-
const collectionFlows = detectCollectionFlows(calls, sources, sinks, constProp
|
|
922
|
+
const collectionFlows = detectCollectionFlows(calls, sources, sinks, constProp, ctx.code, types, graph) ?? [];
|
|
845
923
|
for (const f of collectionFlows) {
|
|
846
924
|
if (flowKeys.has(flowKey(f)))
|
|
847
925
|
continue;
|
|
@@ -865,7 +943,7 @@ export class TaintPropagationPass {
|
|
|
865
943
|
}
|
|
866
944
|
// Supplement: direct parameter-to-sink flows
|
|
867
945
|
// Sprint 82 (#189) — sink-type-aware dedup (see arrayFlows comment).
|
|
868
|
-
const paramFlows = detectParameterSinkFlows(types, calls, sources, sinks, constProp.unreachableLines, constProp.tainted, ctx.code) ?? [];
|
|
946
|
+
const paramFlows = detectParameterSinkFlows(types, calls, sources, sinks, constProp.unreachableLines, constProp.tainted, ctx.code, ctx.language, graph.ir.meta.file) ?? [];
|
|
869
947
|
for (const f of paramFlows) {
|
|
870
948
|
pushIfNew(f);
|
|
871
949
|
}
|
|
@@ -923,6 +1001,15 @@ export class TaintPropagationPass {
|
|
|
923
1001
|
pushIfNew(f);
|
|
924
1002
|
}
|
|
925
1003
|
}
|
|
1004
|
+
// cognium-dev#686 — JS/TS module-scope `let`/`var` store-then-read across
|
|
1005
|
+
// route handlers (variant xss stored). Mirrors #243 Go package globals.
|
|
1006
|
+
if ((ctx.language === 'javascript' || ctx.language === 'typescript') &&
|
|
1007
|
+
typeof ctx.code === 'string') {
|
|
1008
|
+
const jsModuleFlows = detectJsModuleScopeFlows(ctx.code, sources, sinks, constProp.unreachableLines) ?? [];
|
|
1009
|
+
for (const f of jsModuleFlows) {
|
|
1010
|
+
pushIfNew(f);
|
|
1011
|
+
}
|
|
1012
|
+
}
|
|
926
1013
|
// Sprint 9 #58.1 — final pass: drop any flow whose source variable was
|
|
927
1014
|
// explicitly marked sanitized by a guard (e.g. regex-allowlist).
|
|
928
1015
|
// Applied to ALL flow generators (DFG-built and the four supplements)
|
|
@@ -970,6 +1057,14 @@ export class TaintPropagationPass {
|
|
|
970
1057
|
return false;
|
|
971
1058
|
return true;
|
|
972
1059
|
});
|
|
1060
|
+
if (ctx.language === 'csharp') {
|
|
1061
|
+
finalFlows = finalFlows.filter((f) => !shouldGateCSharpPatPermsLibraryParamFlow(f, sources, sinks, types, {
|
|
1062
|
+
language: ctx.language,
|
|
1063
|
+
types,
|
|
1064
|
+
filePath: graph.ir.meta.file,
|
|
1065
|
+
calls,
|
|
1066
|
+
}));
|
|
1067
|
+
}
|
|
973
1068
|
// Sprint 24 (cognium-dev #102) — uniform line-keyed sanitizer
|
|
974
1069
|
// suppression. Drop any flow whose sink_line carries a registered
|
|
975
1070
|
// sanitizer that covers the flow's sink_type. Several supplementary
|
|
@@ -1647,6 +1742,102 @@ function detectGoPackageGlobalFlows(code, _calls, sources, sinks, unreachableLin
|
|
|
1647
1742
|
}
|
|
1648
1743
|
return flows;
|
|
1649
1744
|
}
|
|
1745
|
+
/**
|
|
1746
|
+
* cognium-dev#686 — module-level `let`/`var` written in one handler and read
|
|
1747
|
+
* in another. Per-function DFG cannot link these; text-scan supplement.
|
|
1748
|
+
*/
|
|
1749
|
+
function detectJsModuleScopeFlows(code, sources, sinks, unreachableLines) {
|
|
1750
|
+
const flows = [];
|
|
1751
|
+
if (!code || sinks.length === 0)
|
|
1752
|
+
return flows;
|
|
1753
|
+
const lines = code.split('\n');
|
|
1754
|
+
const moduleVars = new Set();
|
|
1755
|
+
for (const line of lines) {
|
|
1756
|
+
const stripped = line.replace(/\s+$/, '');
|
|
1757
|
+
if (!stripped || /^[ \t]/.test(line))
|
|
1758
|
+
continue;
|
|
1759
|
+
const m = stripped.match(/^(?:export\s+)?(?:let|var|const)\s+([A-Za-z_$][\w$]*)\b/);
|
|
1760
|
+
if (m)
|
|
1761
|
+
moduleVars.add(m[1]);
|
|
1762
|
+
}
|
|
1763
|
+
if (moduleVars.size === 0)
|
|
1764
|
+
return flows;
|
|
1765
|
+
const sourceVarNames = new Set();
|
|
1766
|
+
for (const s of sources) {
|
|
1767
|
+
if (typeof s.variable === 'string' && s.variable.length > 0) {
|
|
1768
|
+
sourceVarNames.add(s.variable);
|
|
1769
|
+
}
|
|
1770
|
+
}
|
|
1771
|
+
const JS_HTTP_SOURCE_RE = /\b(?:req|request)\.(?:body|query|params|headers|cookies|get)\b|\breq\.(?:param|query|body)\(/;
|
|
1772
|
+
const writes = [];
|
|
1773
|
+
for (let i = 0; i < lines.length; i++) {
|
|
1774
|
+
const line = lines[i];
|
|
1775
|
+
if (unreachableLines.has(i + 1))
|
|
1776
|
+
continue;
|
|
1777
|
+
for (const lhs of moduleVars) {
|
|
1778
|
+
const declRe = new RegExp(`\\b(?:export\\s+)?(?:let|const|var)\\s+${lhs.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\b`);
|
|
1779
|
+
if (declRe.test(line))
|
|
1780
|
+
continue;
|
|
1781
|
+
const m = line.match(new RegExp(`\\b${lhs.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\s*=\\s*([^;]+)`));
|
|
1782
|
+
if (!m)
|
|
1783
|
+
continue;
|
|
1784
|
+
const rhs = m[1];
|
|
1785
|
+
let taintedBy = null;
|
|
1786
|
+
const onLine = sources.filter(s => s.line === i + 1);
|
|
1787
|
+
if (onLine.length > 0) {
|
|
1788
|
+
taintedBy = { line: onLine[0].line, type: onLine[0].type };
|
|
1789
|
+
}
|
|
1790
|
+
else if (JS_HTTP_SOURCE_RE.test(rhs)) {
|
|
1791
|
+
taintedBy = { line: i + 1, type: 'http_body' };
|
|
1792
|
+
}
|
|
1793
|
+
else {
|
|
1794
|
+
for (const v of sourceVarNames) {
|
|
1795
|
+
const re = new RegExp(`(?<![A-Za-z0-9_$])${v.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}(?![A-Za-z0-9_$])`);
|
|
1796
|
+
if (re.test(rhs)) {
|
|
1797
|
+
const src = sources.find(s => s.variable === v);
|
|
1798
|
+
if (src) {
|
|
1799
|
+
taintedBy = { line: src.line, type: src.type };
|
|
1800
|
+
break;
|
|
1801
|
+
}
|
|
1802
|
+
}
|
|
1803
|
+
}
|
|
1804
|
+
}
|
|
1805
|
+
if (!taintedBy)
|
|
1806
|
+
continue;
|
|
1807
|
+
writes.push({ varName: lhs, line: i + 1, sourceLine: taintedBy.line, sourceType: taintedBy.type });
|
|
1808
|
+
}
|
|
1809
|
+
}
|
|
1810
|
+
if (writes.length === 0)
|
|
1811
|
+
return flows;
|
|
1812
|
+
for (const sink of sinks) {
|
|
1813
|
+
if (unreachableLines.has(sink.line))
|
|
1814
|
+
continue;
|
|
1815
|
+
const sinkLineText = lines[sink.line - 1] ?? '';
|
|
1816
|
+
if (!sinkLineText)
|
|
1817
|
+
continue;
|
|
1818
|
+
for (const w of writes) {
|
|
1819
|
+
if (w.line >= sink.line)
|
|
1820
|
+
continue;
|
|
1821
|
+
const re = new RegExp(`(?<![A-Za-z0-9_$])${w.varName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}(?![A-Za-z0-9_$])`);
|
|
1822
|
+
if (!re.test(sinkLineText))
|
|
1823
|
+
continue;
|
|
1824
|
+
flows.push({
|
|
1825
|
+
source_line: w.sourceLine,
|
|
1826
|
+
sink_line: sink.line,
|
|
1827
|
+
source_type: w.sourceType,
|
|
1828
|
+
sink_type: sink.type,
|
|
1829
|
+
path: [
|
|
1830
|
+
{ variable: w.varName, line: w.sourceLine, type: 'source' },
|
|
1831
|
+
{ variable: w.varName, line: w.line, type: 'assignment' },
|
|
1832
|
+
{ variable: w.varName, line: sink.line, type: 'sink' },
|
|
1833
|
+
],
|
|
1834
|
+
confidence: 0.9,
|
|
1835
|
+
sanitized: false,
|
|
1836
|
+
});
|
|
1837
|
+
}
|
|
1838
|
+
}
|
|
1839
|
+
return flows;
|
|
1840
|
+
}
|
|
1650
1841
|
/**
|
|
1651
1842
|
* #614 — is `varName` tainted in the sink's own method?
|
|
1652
1843
|
*
|
|
@@ -1753,7 +1944,21 @@ function isTaintedInMethod(taintedVars, varName, scopedName, fieldNames, isField
|
|
|
1753
1944
|
}
|
|
1754
1945
|
return true;
|
|
1755
1946
|
}
|
|
1756
|
-
function
|
|
1947
|
+
function mapGetLiteralKeyFromExpr(expr) {
|
|
1948
|
+
const m = expr.match(/\b([A-Za-z_]\w*)\s*\.\s*get\s*\(\s*"([^"]*)"\s*\)/);
|
|
1949
|
+
return m ? { map: m[1], key: m[2] } : null;
|
|
1950
|
+
}
|
|
1951
|
+
function isMapLiteralKeyTainted(constProp, mapName, key, inMethod) {
|
|
1952
|
+
const scoped = inMethod ? `${inMethod}:${mapName}` : mapName;
|
|
1953
|
+
const keys = constProp.taintedCollections.get(mapName) ??
|
|
1954
|
+
constProp.taintedCollections.get(scoped);
|
|
1955
|
+
if (!keys || keys.size === 0)
|
|
1956
|
+
return false;
|
|
1957
|
+
return keys.has(key) || keys.has('*');
|
|
1958
|
+
}
|
|
1959
|
+
function detectCollectionFlows(calls, sources, sinks, constProp, code, types, graph) {
|
|
1960
|
+
const taintedVars = constProp.tainted;
|
|
1961
|
+
const unreachableLines = constProp.unreachableLines;
|
|
1757
1962
|
const flows = [];
|
|
1758
1963
|
const fieldNames = new Set();
|
|
1759
1964
|
for (const t of types ?? [])
|
|
@@ -1778,7 +1983,13 @@ function detectCollectionFlows(calls, sources, sinks, taintedVars, unreachableLi
|
|
|
1778
1983
|
}
|
|
1779
1984
|
if (arg.variable) {
|
|
1780
1985
|
const varName = arg.variable;
|
|
1781
|
-
|
|
1986
|
+
let scopedName = varName;
|
|
1987
|
+
if (call.in_method) {
|
|
1988
|
+
const blockLine = graph
|
|
1989
|
+
? defLineForUse(graph, varName, call.location.line)
|
|
1990
|
+
: call.location.line;
|
|
1991
|
+
scopedName = constPropFlowLookup('java', call.in_method, varName, blockLine, constProp, true);
|
|
1992
|
+
}
|
|
1782
1993
|
// Bare `name` that is a field read (no local of that name in this
|
|
1783
1994
|
// method) still counts as a field use — Inter4-style. A shadowing
|
|
1784
1995
|
// local declaration in the method does not.
|
|
@@ -1788,7 +1999,12 @@ function detectCollectionFlows(calls, sources, sinks, taintedVars, unreachableLi
|
|
|
1788
1999
|
&& expr === varName
|
|
1789
2000
|
&& !methodHasLocalDecl(code, call.in_method, varName, call.location.line);
|
|
1790
2001
|
const isFieldUse = explicitField || bareFieldRead;
|
|
1791
|
-
|
|
2002
|
+
const mapKeyRead = mapGetLiteralKeyFromExpr(expr);
|
|
2003
|
+
let argTainted = isTaintedInMethod(taintedVars, varName, scopedName, fieldNames, isFieldUse);
|
|
2004
|
+
if (mapKeyRead && mapKeyRead.map === varName) {
|
|
2005
|
+
argTainted = isMapLiteralKeyTainted(constProp, varName, mapKeyRead.key, call.in_method);
|
|
2006
|
+
}
|
|
2007
|
+
if (argTainted) {
|
|
1792
2008
|
// Sprint 13 #70 — pick a source in the same method scope as the
|
|
1793
2009
|
// sink, not blanket sources[0]. The varName-based match is
|
|
1794
2010
|
// primary; method-scope is the tiebreaker; sources[0] is the
|
|
@@ -1843,12 +2059,19 @@ function detectCollectionFlows(calls, sources, sinks, taintedVars, unreachableLi
|
|
|
1843
2059
|
{ method: 'peek', re: /(\w+)\.peek\(/ },
|
|
1844
2060
|
{ method: 'toArray', re: /(\w+)\.toArray\(/ },
|
|
1845
2061
|
];
|
|
1846
|
-
for (const { re } of collectionPatterns) {
|
|
2062
|
+
for (const { method, re } of collectionPatterns) {
|
|
1847
2063
|
const match = expr.match(re);
|
|
1848
2064
|
if (match) {
|
|
1849
2065
|
const collectionVar = match[1];
|
|
1850
2066
|
const scopedCollection = call.in_method ? `${call.in_method}:${collectionVar}` : collectionVar;
|
|
1851
|
-
|
|
2067
|
+
const mapKey = method === 'get' ? mapGetLiteralKeyFromExpr(expr) : null;
|
|
2068
|
+
let collectionTainted = taintedVars.has(collectionVar) || taintedVars.has(scopedCollection);
|
|
2069
|
+
if (mapKey && mapKey.map === collectionVar) {
|
|
2070
|
+
collectionTainted = isMapLiteralKeyTainted(constProp, collectionVar, mapKey.key, call.in_method);
|
|
2071
|
+
}
|
|
2072
|
+
if (!collectionTainted)
|
|
2073
|
+
continue;
|
|
2074
|
+
{
|
|
1852
2075
|
// Sprint 13 #70 — same method-scoped picker as above.
|
|
1853
2076
|
const source = pickScopedSource(sources, sink.line, call.in_method ?? null, types, collectionVar);
|
|
1854
2077
|
if (source) {
|
|
@@ -1934,6 +2157,40 @@ function detectArrayElementFlows(calls, sources, sinks, taintedArrayElements, un
|
|
|
1934
2157
|
}
|
|
1935
2158
|
return flows;
|
|
1936
2159
|
}
|
|
2160
|
+
/**
|
|
2161
|
+
* cognium-dev #816 — PAT-PERMS-01: suppress only direct library-parameter →
|
|
2162
|
+
* FileStream path_traversal on non-entry methods. Derived paths (Path.Combine,
|
|
2163
|
+
* GetFullPath chains) and other sinks stay gated only by sanitizer / DFG logic.
|
|
2164
|
+
*/
|
|
2165
|
+
function csharpPatPermsDirectParamSink(sink, paramVar) {
|
|
2166
|
+
const method = (sink.method ?? '').replace(/^.*\./, '');
|
|
2167
|
+
if (method !== 'FileStream')
|
|
2168
|
+
return false;
|
|
2169
|
+
const code = sink.code ?? '';
|
|
2170
|
+
if (!code)
|
|
2171
|
+
return true;
|
|
2172
|
+
const escaped = paramVar.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
2173
|
+
return new RegExp(`\\bFileStream\\s*\\(\\s*${escaped}\\b`).test(code);
|
|
2174
|
+
}
|
|
2175
|
+
function shouldGateCSharpPatPermsLibraryParamFlow(flow, sources, sinks, types, ctx) {
|
|
2176
|
+
if (flow.source_type !== 'interprocedural_param' || flow.sink_type !== 'path_traversal') {
|
|
2177
|
+
return false;
|
|
2178
|
+
}
|
|
2179
|
+
const srcVar = flow.path?.[0]?.variable;
|
|
2180
|
+
const sinkVar = flow.path?.[flow.path.length - 1]?.variable;
|
|
2181
|
+
if (!srcVar || srcVar !== sinkVar)
|
|
2182
|
+
return false;
|
|
2183
|
+
const sink = sinks.find((s) => s.line === flow.sink_line && s.type === 'path_traversal');
|
|
2184
|
+
if (!sink || !csharpPatPermsDirectParamSink(sink, srcVar))
|
|
2185
|
+
return false;
|
|
2186
|
+
const src = sources.find((s) => s.line === flow.source_line && s.type === 'interprocedural_param');
|
|
2187
|
+
const methodName = src?.in_method;
|
|
2188
|
+
if (!methodName)
|
|
2189
|
+
return false;
|
|
2190
|
+
const enclosingType = types.find((t) => t.methods.some((m) => m.name === methodName));
|
|
2191
|
+
const enclosingMethod = enclosingType?.methods.find((m) => m.name === methodName);
|
|
2192
|
+
return shouldGateInterproceduralParam(flow.source_type, enclosingMethod, enclosingType, ctx);
|
|
2193
|
+
}
|
|
1937
2194
|
/** Parameter names referenced as identifiers inside a compound sink argument (#685). */
|
|
1938
2195
|
function compoundArgParamNames(expression, paramNames) {
|
|
1939
2196
|
const stripped = expression.replace(/(['"`])(?:\\.|(?!\1).)*\1/g, (m) => ' '.repeat(m.length));
|
|
@@ -1948,7 +2205,7 @@ function compoundArgParamNames(expression, paramNames) {
|
|
|
1948
2205
|
}
|
|
1949
2206
|
return matched;
|
|
1950
2207
|
}
|
|
1951
|
-
function detectParameterSinkFlows(types, calls, sources, sinks, unreachableLines, tainted, code) {
|
|
2208
|
+
function detectParameterSinkFlows(types, calls, sources, sinks, unreachableLines, tainted, code, language, filePath) {
|
|
1952
2209
|
const flows = [];
|
|
1953
2210
|
const paramSourcesByMethod = new Map();
|
|
1954
2211
|
for (const source of sources) {
|
|
@@ -1998,6 +2255,16 @@ function detectParameterSinkFlows(types, calls, sources, sinks, unreachableLines
|
|
|
1998
2255
|
for (const varName of varNames) {
|
|
1999
2256
|
const paramSource = methodParamSources.get(varName);
|
|
2000
2257
|
if (paramSource) {
|
|
2258
|
+
if (language === 'csharp' &&
|
|
2259
|
+
paramSource.type === 'interprocedural_param' &&
|
|
2260
|
+
sink.type === 'path_traversal' &&
|
|
2261
|
+
csharpPatPermsDirectParamSink(sink, varName)) {
|
|
2262
|
+
const enclosingType = types.find((t) => t.methods.some((m) => m.name === methodName));
|
|
2263
|
+
const enclosingMethod = enclosingType?.methods.find((m) => m.name === methodName);
|
|
2264
|
+
if (shouldGateInterproceduralParam(paramSource.type, enclosingMethod, enclosingType, { language, types, filePath, calls })) {
|
|
2265
|
+
continue;
|
|
2266
|
+
}
|
|
2267
|
+
}
|
|
2001
2268
|
// Sprint 82 (#189) — sink-type-aware dedup so a sink registered
|
|
2002
2269
|
// under multiple types at the same call site (e.g. `redirect` as
|
|
2003
2270
|
// both `open_redirect` and `crlf`) emits a flow for each type.
|
|
@@ -3504,10 +3771,22 @@ function detectExpressionScanFlows(calls, sources, sinks, sanitizers, unreachabl
|
|
|
3504
3771
|
realSourceByVar.set(s.variable, s);
|
|
3505
3772
|
}
|
|
3506
3773
|
for (const [varName, derivedLine] of derived) {
|
|
3507
|
-
if (!varName
|
|
3774
|
+
if (!varName)
|
|
3508
3775
|
continue;
|
|
3509
3776
|
const owner = methodAt(derivedLine);
|
|
3510
3777
|
const fieldAlias = isDeclaredField(derivedLine, varName);
|
|
3778
|
+
if (existingVars.has(varName)) {
|
|
3779
|
+
if (!fieldAlias)
|
|
3780
|
+
continue;
|
|
3781
|
+
// cognium-dev#626: a bean setter's `interprocedural_param` stub must not
|
|
3782
|
+
// block the declared-field alias derived from a tainted `setFoo(...)` /
|
|
3783
|
+
// `this.foo =` write — otherwise sinks in other methods never see `foo`.
|
|
3784
|
+
const stubIdx = sourcesWithVar.findIndex(s => s.variable === varName && s.type === 'interprocedural_param');
|
|
3785
|
+
if (stubIdx < 0)
|
|
3786
|
+
continue;
|
|
3787
|
+
sourcesWithVar.splice(stubIdx, 1);
|
|
3788
|
+
existingVars.delete(varName);
|
|
3789
|
+
}
|
|
3511
3790
|
let scopedAnchor = anchor;
|
|
3512
3791
|
if (owner) {
|
|
3513
3792
|
let best;
|