circle-ir 4.16.0 → 4.17.0
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/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 +11 -0
- package/dist/analysis/constant-propagation/propagator.d.ts.map +1 -1
- package/dist/analysis/constant-propagation/propagator.js +125 -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/interprocedural.d.ts.map +1 -1
- package/dist/analysis/interprocedural.js +37 -8
- package/dist/analysis/interprocedural.js.map +1 -1
- package/dist/analysis/passes/cross-file-pass.d.ts.map +1 -1
- package/dist/analysis/passes/cross-file-pass.js +3 -1
- package/dist/analysis/passes/cross-file-pass.js.map +1 -1
- package/dist/analysis/passes/csharp-sanitizer-credit.d.ts +13 -0
- package/dist/analysis/passes/csharp-sanitizer-credit.d.ts.map +1 -1
- package/dist/analysis/passes/csharp-sanitizer-credit.js +48 -0
- package/dist/analysis/passes/csharp-sanitizer-credit.js.map +1 -1
- package/dist/analysis/passes/language-sources-pass.d.ts.map +1 -1
- package/dist/analysis/passes/language-sources-pass.js +80 -8
- 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 +17 -1
- package/dist/analysis/passes/param-source-flow-gate-pass.js.map +1 -1
- package/dist/analysis/passes/sink-filter-pass.d.ts.map +1 -1
- package/dist/analysis/passes/sink-filter-pass.js +19 -3
- 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 +205 -22
- package/dist/analysis/passes/taint-propagation-pass.js.map +1 -1
- 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/taint-matcher.d.ts.map +1 -1
- package/dist/analysis/taint-matcher.js +1 -0
- package/dist/analysis/taint-matcher.js.map +1 -1
- package/dist/browser/circle-ir.js +561 -41
- package/dist/core/circle-ir-core.cjs +149 -17
- package/dist/core/circle-ir-core.js +149 -17
- package/dist/core/extractors/types.js +53 -9
- package/dist/core/extractors/types.js.map +1 -1
- package/dist/navigation/navigation-index.d.ts +40 -1
- package/dist/navigation/navigation-index.d.ts.map +1 -1
- package/dist/navigation/navigation-index.js +300 -45
- package/dist/navigation/navigation-index.js.map +1 -1
- package/dist/resolution/cross-file.d.ts +9 -0
- package/dist/resolution/cross-file.d.ts.map +1 -1
- package/dist/resolution/cross-file.js +134 -0
- package/dist/resolution/cross-file.js.map +1 -1
- package/dist/types/index.d.ts +18 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -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';
|
|
@@ -369,25 +369,74 @@ function resolveJavaDerivedAnchor(varName, derived, realSourceByVar, codeLines,
|
|
|
369
369
|
* const-prop never seeds those derived names. Argument-style derivations
|
|
370
370
|
* (`shown = encodeForPage(nick)`) must keep the veto so sanitizer credit holds.
|
|
371
371
|
*/
|
|
372
|
-
function javaReceiverChainDerivedVar(
|
|
372
|
+
function javaReceiverChainDerivedVar(rhsByLocal, localVar, seedVar) {
|
|
373
373
|
if (!seedVar || localVar === seedVar)
|
|
374
374
|
return false;
|
|
375
|
+
const rhsList = rhsByLocal.get(localVar);
|
|
376
|
+
if (!rhsList)
|
|
377
|
+
return false;
|
|
375
378
|
const esc = seedVar.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
376
379
|
const receiverOnRhs = new RegExp(`(?:^|[^\\w.$])${esc}\\s*\\.`, 'u');
|
|
380
|
+
return rhsList.some(rhs => receiverOnRhs.test(rhs));
|
|
381
|
+
}
|
|
382
|
+
/**
|
|
383
|
+
* cognium-dev#806 — the assignment RHS texts of each Java local, indexed once
|
|
384
|
+
* per file. `javaReceiverChainDerivedVar` used to re-run its declaration regex
|
|
385
|
+
* over every line for every (source, derived local) pair, which made large
|
|
386
|
+
* files take seconds. Same per-line matching, done once.
|
|
387
|
+
*/
|
|
388
|
+
/** Line whose block scope owns `varName` as read on `useLine` (#476 / #597). */
|
|
389
|
+
function defLineForUse(graph, varName, useLine) {
|
|
390
|
+
const own = (graph.defsByLine.get(useLine) ?? []).find(d => d.variable === varName);
|
|
391
|
+
if (own)
|
|
392
|
+
return own.line;
|
|
393
|
+
const use = (graph.usesByLine.get(useLine) ?? []).find(u => u.variable === varName && u.def_id != null);
|
|
394
|
+
const d = use ? graph.defById.get(use.def_id) : undefined;
|
|
395
|
+
return d?.line ?? useLine;
|
|
396
|
+
}
|
|
397
|
+
/** Const-prop key for a flow step: block locals vs method-body locals (#476 / #597). */
|
|
398
|
+
function constPropFlowLookup(language, method, varName, blockLine, constProp, preferMethodScoped = false) {
|
|
399
|
+
if (language === 'go') {
|
|
400
|
+
return constPropScopedVar(method, varName, blockLine, constProp);
|
|
401
|
+
}
|
|
402
|
+
if (!method || varName.includes(':'))
|
|
403
|
+
return varName;
|
|
404
|
+
const blockScoped = constPropScopedVar(method, varName, blockLine, constProp);
|
|
405
|
+
const hasTaintBinding = (name) => constProp.tainted.has(name) || constProp.sanitizedVars.has(name);
|
|
406
|
+
if (blockScoped.includes(':b') && hasTaintBinding(blockScoped)) {
|
|
407
|
+
return blockScoped;
|
|
408
|
+
}
|
|
409
|
+
const methodScoped = `${method}:${varName}`;
|
|
410
|
+
if (hasTaintBinding(methodScoped)) {
|
|
411
|
+
return methodScoped;
|
|
412
|
+
}
|
|
413
|
+
if (preferMethodScoped && method) {
|
|
414
|
+
return methodScoped;
|
|
415
|
+
}
|
|
416
|
+
return constProp.lineBlockScopes?.has(blockLine) ? blockScoped : varName;
|
|
417
|
+
}
|
|
418
|
+
function indexJavaLocalAssignments(code) {
|
|
377
419
|
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*$/;
|
|
378
420
|
const assignRe = /^\s*([A-Za-z_]\w*)\s*=\s*(.+?);\s*$/;
|
|
421
|
+
const index = new Map();
|
|
379
422
|
for (const line of code.split('\n')) {
|
|
380
423
|
const trimmed = line.trimStart();
|
|
381
424
|
if (trimmed.startsWith('//') || trimmed.startsWith('*'))
|
|
382
425
|
continue;
|
|
426
|
+
// Cheap pre-check: neither regex can match a line without `=` and `;`.
|
|
427
|
+
if (!line.includes('=') || !line.includes(';'))
|
|
428
|
+
continue;
|
|
383
429
|
const m = declRe.exec(line) ?? assignRe.exec(line);
|
|
384
|
-
if (!m
|
|
430
|
+
if (!m)
|
|
385
431
|
continue;
|
|
432
|
+
const list = index.get(m[1]);
|
|
386
433
|
const rhs = stripJavaTrailingComment(m[2]);
|
|
387
|
-
if (
|
|
388
|
-
|
|
434
|
+
if (list)
|
|
435
|
+
list.push(rhs);
|
|
436
|
+
else
|
|
437
|
+
index.set(m[1], [rhs]);
|
|
389
438
|
}
|
|
390
|
-
return
|
|
439
|
+
return index;
|
|
391
440
|
}
|
|
392
441
|
function makeConstPropVeto(constProp, graph, sources, code, language, sanitizers) {
|
|
393
442
|
const lines = typeof code === 'string' ? code.split('\n') : [];
|
|
@@ -423,15 +472,26 @@ function makeConstPropVeto(constProp, graph, sources, code, language, sanitizers
|
|
|
423
472
|
};
|
|
424
473
|
return (step, sourceLine, sinkLine, sinkType) => {
|
|
425
474
|
const method = methodAtLine(graph, step.line);
|
|
426
|
-
const
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
// languages keep the unscoped lookup (#614).
|
|
430
|
-
const r = isFalsePositive(constProp, step.line, language === 'go' ? scopedVar : step.variable);
|
|
475
|
+
const blockLine = defLineForUse(graph, step.variable, step.line);
|
|
476
|
+
const fpLookup = constPropFlowLookup(language, method, step.variable, blockLine, constProp);
|
|
477
|
+
const r = isFalsePositive(constProp, step.line, fpLookup);
|
|
431
478
|
if (!r.isFalsePositive)
|
|
432
479
|
return false;
|
|
433
480
|
if (r.reason !== 'variable_not_tainted')
|
|
434
481
|
return true;
|
|
482
|
+
if (r.reason === 'variable_not_tainted' &&
|
|
483
|
+
language === 'java' &&
|
|
484
|
+
method &&
|
|
485
|
+
fpLookup === step.variable &&
|
|
486
|
+
!step.variable.includes('.')) {
|
|
487
|
+
const v = step.variable;
|
|
488
|
+
if (constProp.tainted.has(`${method}:${v}`))
|
|
489
|
+
return false;
|
|
490
|
+
for (const t of constProp.tainted) {
|
|
491
|
+
if (t.startsWith(`${method}:b`) && t.endsWith(`:${v}`))
|
|
492
|
+
return false;
|
|
493
|
+
}
|
|
494
|
+
}
|
|
435
495
|
// #589: the step variable came from a context-specific encoder
|
|
436
496
|
// (`escapeHtml`, `encodeForHTML`, …), which does not clean `trust_boundary`
|
|
437
497
|
// (a `session.setAttribute` sink). It is "not tainted" only for the other
|
|
@@ -447,8 +507,9 @@ function makeConstPropVeto(constProp, graph, sources, code, language, sanitizers
|
|
|
447
507
|
else {
|
|
448
508
|
const method = methodAtLine(graph, step.line);
|
|
449
509
|
if (method) {
|
|
450
|
-
if (unc.get(
|
|
510
|
+
if (unc.get(fpLookup)?.has(sinkType)) {
|
|
451
511
|
return false;
|
|
512
|
+
}
|
|
452
513
|
}
|
|
453
514
|
else if (unc.get(step.variable)?.has(sinkType)) {
|
|
454
515
|
return false;
|
|
@@ -524,7 +585,8 @@ function makeConstPropVeto(constProp, graph, sources, code, language, sanitizers
|
|
|
524
585
|
// cognium-dev#751: sink use after `uid := constantHelper(tainted)` must veto
|
|
525
586
|
// when the helper is in `constantReturningMethods`, even if the flow's
|
|
526
587
|
// source_line is the assignment line (rule E would otherwise keep it).
|
|
527
|
-
if (step.line === sinkLine &&
|
|
588
|
+
if (step.line === sinkLine &&
|
|
589
|
+
!constProp.tainted.has(fpLookup)) {
|
|
528
590
|
for (let ln = sourceLine; ln <= sinkLine; ln++) {
|
|
529
591
|
const calleeMatch = (lines[ln - 1] ?? '').match(/:=\s*(\w+)\s*\(/);
|
|
530
592
|
if (calleeMatch && constProp.constantReturningMethods?.has(calleeMatch[1])) {
|
|
@@ -553,16 +615,24 @@ export class TaintPropagationPass {
|
|
|
553
615
|
// into const-prop's tainted set so DFG + expression-scan vetoes keep them.
|
|
554
616
|
// Per-source seeding + receiver filter avoids argument-style derivations
|
|
555
617
|
// (`shown = encodeForPage(nick)`) that must stay vetoed for sanitizer credit.
|
|
618
|
+
// cognium-dev#806: index the assignments once, visit each seed name once,
|
|
619
|
+
// and skip the file-wide derivation when no local not already tainted has
|
|
620
|
+
// a `seed.` receiver on its RHS (only such locals can be added).
|
|
556
621
|
if (ctx.language === 'java' && typeof ctx.code === 'string') {
|
|
622
|
+
const rhsByLocal = indexJavaLocalAssignments(ctx.code);
|
|
623
|
+
const seen = new Set();
|
|
557
624
|
for (const s of sources) {
|
|
558
|
-
if (!s.variable)
|
|
625
|
+
if (!s.variable || seen.has(s.variable))
|
|
559
626
|
continue;
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
627
|
+
seen.add(s.variable);
|
|
628
|
+
const seedVar = s.variable;
|
|
629
|
+
const candidates = [...rhsByLocal.keys()].filter(v => !constProp.tainted.has(v) && javaReceiverChainDerivedVar(rhsByLocal, v, seedVar));
|
|
630
|
+
if (candidates.length === 0)
|
|
631
|
+
continue;
|
|
632
|
+
const derived = buildJavaTaintedVars(ctx.code, new Set([seedVar]));
|
|
633
|
+
for (const v of candidates) {
|
|
634
|
+
if (derived.has(v))
|
|
564
635
|
constProp.tainted.add(v);
|
|
565
|
-
}
|
|
566
636
|
}
|
|
567
637
|
}
|
|
568
638
|
}
|
|
@@ -814,7 +884,7 @@ export class TaintPropagationPass {
|
|
|
814
884
|
pushIfNew(f);
|
|
815
885
|
}
|
|
816
886
|
// Supplement: collection/iterator flows — with FP filtering
|
|
817
|
-
const collectionFlows = detectCollectionFlows(calls, sources, sinks, constProp
|
|
887
|
+
const collectionFlows = detectCollectionFlows(calls, sources, sinks, constProp, ctx.code, types, graph) ?? [];
|
|
818
888
|
for (const f of collectionFlows) {
|
|
819
889
|
if (flowKeys.has(flowKey(f)))
|
|
820
890
|
continue;
|
|
@@ -896,6 +966,15 @@ export class TaintPropagationPass {
|
|
|
896
966
|
pushIfNew(f);
|
|
897
967
|
}
|
|
898
968
|
}
|
|
969
|
+
// cognium-dev#686 — JS/TS module-scope `let`/`var` store-then-read across
|
|
970
|
+
// route handlers (variant xss stored). Mirrors #243 Go package globals.
|
|
971
|
+
if ((ctx.language === 'javascript' || ctx.language === 'typescript') &&
|
|
972
|
+
typeof ctx.code === 'string') {
|
|
973
|
+
const jsModuleFlows = detectJsModuleScopeFlows(ctx.code, sources, sinks, constProp.unreachableLines) ?? [];
|
|
974
|
+
for (const f of jsModuleFlows) {
|
|
975
|
+
pushIfNew(f);
|
|
976
|
+
}
|
|
977
|
+
}
|
|
899
978
|
// Sprint 9 #58.1 — final pass: drop any flow whose source variable was
|
|
900
979
|
// explicitly marked sanitized by a guard (e.g. regex-allowlist).
|
|
901
980
|
// Applied to ALL flow generators (DFG-built and the four supplements)
|
|
@@ -1620,6 +1699,102 @@ function detectGoPackageGlobalFlows(code, _calls, sources, sinks, unreachableLin
|
|
|
1620
1699
|
}
|
|
1621
1700
|
return flows;
|
|
1622
1701
|
}
|
|
1702
|
+
/**
|
|
1703
|
+
* cognium-dev#686 — module-level `let`/`var` written in one handler and read
|
|
1704
|
+
* in another. Per-function DFG cannot link these; text-scan supplement.
|
|
1705
|
+
*/
|
|
1706
|
+
function detectJsModuleScopeFlows(code, sources, sinks, unreachableLines) {
|
|
1707
|
+
const flows = [];
|
|
1708
|
+
if (!code || sinks.length === 0)
|
|
1709
|
+
return flows;
|
|
1710
|
+
const lines = code.split('\n');
|
|
1711
|
+
const moduleVars = new Set();
|
|
1712
|
+
for (const line of lines) {
|
|
1713
|
+
const stripped = line.replace(/\s+$/, '');
|
|
1714
|
+
if (!stripped || /^[ \t]/.test(line))
|
|
1715
|
+
continue;
|
|
1716
|
+
const m = stripped.match(/^(?:let|var|const)\s+([A-Za-z_$][\w$]*)\b/);
|
|
1717
|
+
if (m)
|
|
1718
|
+
moduleVars.add(m[1]);
|
|
1719
|
+
}
|
|
1720
|
+
if (moduleVars.size === 0)
|
|
1721
|
+
return flows;
|
|
1722
|
+
const sourceVarNames = new Set();
|
|
1723
|
+
for (const s of sources) {
|
|
1724
|
+
if (typeof s.variable === 'string' && s.variable.length > 0) {
|
|
1725
|
+
sourceVarNames.add(s.variable);
|
|
1726
|
+
}
|
|
1727
|
+
}
|
|
1728
|
+
const JS_HTTP_SOURCE_RE = /\b(?:req|request)\.(?:body|query|params|headers|cookies|get)\b|\breq\.(?:param|query|body)\(/;
|
|
1729
|
+
const writes = [];
|
|
1730
|
+
for (let i = 0; i < lines.length; i++) {
|
|
1731
|
+
const line = lines[i];
|
|
1732
|
+
if (unreachableLines.has(i + 1))
|
|
1733
|
+
continue;
|
|
1734
|
+
for (const lhs of moduleVars) {
|
|
1735
|
+
const declRe = new RegExp(`\\b(?:let|const|var)\\s+${lhs.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\b`);
|
|
1736
|
+
if (declRe.test(line))
|
|
1737
|
+
continue;
|
|
1738
|
+
const m = line.match(new RegExp(`\\b${lhs.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\s*=\\s*([^;]+)`));
|
|
1739
|
+
if (!m)
|
|
1740
|
+
continue;
|
|
1741
|
+
const rhs = m[1];
|
|
1742
|
+
let taintedBy = null;
|
|
1743
|
+
const onLine = sources.filter(s => s.line === i + 1);
|
|
1744
|
+
if (onLine.length > 0) {
|
|
1745
|
+
taintedBy = { line: onLine[0].line, type: onLine[0].type };
|
|
1746
|
+
}
|
|
1747
|
+
else if (JS_HTTP_SOURCE_RE.test(rhs)) {
|
|
1748
|
+
taintedBy = { line: i + 1, type: 'http_body' };
|
|
1749
|
+
}
|
|
1750
|
+
else {
|
|
1751
|
+
for (const v of sourceVarNames) {
|
|
1752
|
+
const re = new RegExp(`(?<![A-Za-z0-9_$])${v.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}(?![A-Za-z0-9_$])`);
|
|
1753
|
+
if (re.test(rhs)) {
|
|
1754
|
+
const src = sources.find(s => s.variable === v);
|
|
1755
|
+
if (src) {
|
|
1756
|
+
taintedBy = { line: src.line, type: src.type };
|
|
1757
|
+
break;
|
|
1758
|
+
}
|
|
1759
|
+
}
|
|
1760
|
+
}
|
|
1761
|
+
}
|
|
1762
|
+
if (!taintedBy)
|
|
1763
|
+
continue;
|
|
1764
|
+
writes.push({ varName: lhs, line: i + 1, sourceLine: taintedBy.line, sourceType: taintedBy.type });
|
|
1765
|
+
}
|
|
1766
|
+
}
|
|
1767
|
+
if (writes.length === 0)
|
|
1768
|
+
return flows;
|
|
1769
|
+
for (const sink of sinks) {
|
|
1770
|
+
if (unreachableLines.has(sink.line))
|
|
1771
|
+
continue;
|
|
1772
|
+
const sinkLineText = lines[sink.line - 1] ?? '';
|
|
1773
|
+
if (!sinkLineText)
|
|
1774
|
+
continue;
|
|
1775
|
+
for (const w of writes) {
|
|
1776
|
+
if (w.line >= sink.line)
|
|
1777
|
+
continue;
|
|
1778
|
+
const re = new RegExp(`(?<![A-Za-z0-9_$])${w.varName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}(?![A-Za-z0-9_$])`);
|
|
1779
|
+
if (!re.test(sinkLineText))
|
|
1780
|
+
continue;
|
|
1781
|
+
flows.push({
|
|
1782
|
+
source_line: w.sourceLine,
|
|
1783
|
+
sink_line: sink.line,
|
|
1784
|
+
source_type: w.sourceType,
|
|
1785
|
+
sink_type: sink.type,
|
|
1786
|
+
path: [
|
|
1787
|
+
{ variable: w.varName, line: w.sourceLine, type: 'source' },
|
|
1788
|
+
{ variable: w.varName, line: w.line, type: 'assignment' },
|
|
1789
|
+
{ variable: w.varName, line: sink.line, type: 'sink' },
|
|
1790
|
+
],
|
|
1791
|
+
confidence: 0.9,
|
|
1792
|
+
sanitized: false,
|
|
1793
|
+
});
|
|
1794
|
+
}
|
|
1795
|
+
}
|
|
1796
|
+
return flows;
|
|
1797
|
+
}
|
|
1623
1798
|
/**
|
|
1624
1799
|
* #614 — is `varName` tainted in the sink's own method?
|
|
1625
1800
|
*
|
|
@@ -1726,7 +1901,9 @@ function isTaintedInMethod(taintedVars, varName, scopedName, fieldNames, isField
|
|
|
1726
1901
|
}
|
|
1727
1902
|
return true;
|
|
1728
1903
|
}
|
|
1729
|
-
function detectCollectionFlows(calls, sources, sinks,
|
|
1904
|
+
function detectCollectionFlows(calls, sources, sinks, constProp, code, types, graph) {
|
|
1905
|
+
const taintedVars = constProp.tainted;
|
|
1906
|
+
const unreachableLines = constProp.unreachableLines;
|
|
1730
1907
|
const flows = [];
|
|
1731
1908
|
const fieldNames = new Set();
|
|
1732
1909
|
for (const t of types ?? [])
|
|
@@ -1751,7 +1928,13 @@ function detectCollectionFlows(calls, sources, sinks, taintedVars, unreachableLi
|
|
|
1751
1928
|
}
|
|
1752
1929
|
if (arg.variable) {
|
|
1753
1930
|
const varName = arg.variable;
|
|
1754
|
-
|
|
1931
|
+
let scopedName = varName;
|
|
1932
|
+
if (call.in_method) {
|
|
1933
|
+
const blockLine = graph
|
|
1934
|
+
? defLineForUse(graph, varName, call.location.line)
|
|
1935
|
+
: call.location.line;
|
|
1936
|
+
scopedName = constPropFlowLookup('java', call.in_method, varName, blockLine, constProp, true);
|
|
1937
|
+
}
|
|
1755
1938
|
// Bare `name` that is a field read (no local of that name in this
|
|
1756
1939
|
// method) still counts as a field use — Inter4-style. A shadowing
|
|
1757
1940
|
// local declaration in the method does not.
|