cognium-dev 3.62.0 → 3.64.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/cli.js +146 -17
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -11633,7 +11633,8 @@ function findSources(calls, types, patterns, sourceLines, language) {
|
|
|
11633
11633
|
location: formatCallLocation(call),
|
|
11634
11634
|
severity: pattern.severity,
|
|
11635
11635
|
line: call.location.line,
|
|
11636
|
-
confidence: 1
|
|
11636
|
+
confidence: 1,
|
|
11637
|
+
in_method: call.in_method ?? undefined
|
|
11637
11638
|
});
|
|
11638
11639
|
}
|
|
11639
11640
|
}
|
|
@@ -11650,7 +11651,8 @@ function findSources(calls, types, patterns, sourceLines, language) {
|
|
|
11650
11651
|
location: `@${pattern.annotation} ${param.name} in ${method.name}`,
|
|
11651
11652
|
severity: pattern.severity,
|
|
11652
11653
|
line: paramLine,
|
|
11653
|
-
confidence: 1
|
|
11654
|
+
confidence: 1,
|
|
11655
|
+
in_method: method.name
|
|
11654
11656
|
});
|
|
11655
11657
|
}
|
|
11656
11658
|
}
|
|
@@ -11672,7 +11674,8 @@ function findSources(calls, types, patterns, sourceLines, language) {
|
|
|
11672
11674
|
location: `@${pattern.method_annotation} ${param.name} in ${method.name}`,
|
|
11673
11675
|
severity: pattern.severity,
|
|
11674
11676
|
line: paramLine,
|
|
11675
|
-
confidence: 1
|
|
11677
|
+
confidence: 1,
|
|
11678
|
+
in_method: method.name
|
|
11676
11679
|
});
|
|
11677
11680
|
}
|
|
11678
11681
|
}
|
|
@@ -11701,7 +11704,8 @@ function findSources(calls, types, patterns, sourceLines, language) {
|
|
|
11701
11704
|
severity: "high",
|
|
11702
11705
|
line: paramLine,
|
|
11703
11706
|
confidence: 1,
|
|
11704
|
-
variable: param.name
|
|
11707
|
+
variable: param.name,
|
|
11708
|
+
in_method: method.name
|
|
11705
11709
|
});
|
|
11706
11710
|
}
|
|
11707
11711
|
}
|
|
@@ -11722,7 +11726,8 @@ function findSources(calls, types, patterns, sourceLines, language) {
|
|
|
11722
11726
|
location: `${param.type || "any"} ${param.name} in ${method.name}`,
|
|
11723
11727
|
severity: "medium",
|
|
11724
11728
|
line: paramLine,
|
|
11725
|
-
confidence: param.type ? 0.7 : 0.5
|
|
11729
|
+
confidence: param.type ? 0.7 : 0.5,
|
|
11730
|
+
in_method: method.name
|
|
11726
11731
|
});
|
|
11727
11732
|
}
|
|
11728
11733
|
}
|
|
@@ -11742,7 +11747,8 @@ function findSources(calls, types, patterns, sourceLines, language) {
|
|
|
11742
11747
|
location: `${arg.expression} in ${call.in_method || "anonymous"}`,
|
|
11743
11748
|
severity: "high",
|
|
11744
11749
|
line: call.location.line,
|
|
11745
|
-
confidence: 1
|
|
11750
|
+
confidence: 1,
|
|
11751
|
+
in_method: call.in_method ?? undefined
|
|
11746
11752
|
});
|
|
11747
11753
|
}
|
|
11748
11754
|
}
|
|
@@ -11762,7 +11768,8 @@ function findSources(calls, types, patterns, sourceLines, language) {
|
|
|
11762
11768
|
location: `${arg.expression} in ${call.in_method || "anonymous"}`,
|
|
11763
11769
|
severity: "high",
|
|
11764
11770
|
line: call.location.line,
|
|
11765
|
-
confidence: 1
|
|
11771
|
+
confidence: 1,
|
|
11772
|
+
in_method: call.in_method ?? undefined
|
|
11766
11773
|
});
|
|
11767
11774
|
}
|
|
11768
11775
|
break;
|
|
@@ -11795,6 +11802,21 @@ function findSources(calls, types, patterns, sourceLines, language) {
|
|
|
11795
11802
|
s.variable = m[1];
|
|
11796
11803
|
}
|
|
11797
11804
|
}
|
|
11805
|
+
if (language === "java" && sourceLines) {
|
|
11806
|
+
const JAVA_ASSIGN_LHS = /^\s*(?:(?:final|public|private|protected|static|synchronized|volatile|transient)\s+)*(?:[A-Za-z_][\w.]*(?:\s*<[^=]*>)?(?:\s*\[\s*\])*\s+)?([A-Za-z_]\w*)\s*=(?!=)/;
|
|
11807
|
+
for (const s of result) {
|
|
11808
|
+
if (s.variable && s.variable.length > 0)
|
|
11809
|
+
continue;
|
|
11810
|
+
const lineText = sourceLines[s.line - 1] ?? "";
|
|
11811
|
+
const m = JAVA_ASSIGN_LHS.exec(lineText);
|
|
11812
|
+
if (!m)
|
|
11813
|
+
continue;
|
|
11814
|
+
const rhs = lineText.slice(m[0].length).trimStart();
|
|
11815
|
+
if (/^new\b/.test(rhs))
|
|
11816
|
+
continue;
|
|
11817
|
+
s.variable = m[1];
|
|
11818
|
+
}
|
|
11819
|
+
}
|
|
11798
11820
|
return result;
|
|
11799
11821
|
}
|
|
11800
11822
|
function isInterproceduralTaintableType(typeName) {
|
|
@@ -22200,6 +22222,8 @@ function findInitialTaint(sources, callsByLine, defsByLine) {
|
|
|
22200
22222
|
for (const def of defsNextLine) {
|
|
22201
22223
|
const callsOnSourceLine = callsByLine.get(source.line) ?? [];
|
|
22202
22224
|
if (callsOnSourceLine.length > 0) {
|
|
22225
|
+
if (source.variable && def.variable !== source.variable)
|
|
22226
|
+
continue;
|
|
22203
22227
|
tainted.push({
|
|
22204
22228
|
variable: def.variable,
|
|
22205
22229
|
defId: def.id,
|
|
@@ -22402,13 +22426,13 @@ class TaintPropagationPass {
|
|
|
22402
22426
|
confidence: flow.confidence,
|
|
22403
22427
|
sanitized: flow.sanitized
|
|
22404
22428
|
}));
|
|
22405
|
-
const arrayFlows = detectArrayElementFlows(calls, sources, sinks, constProp.taintedArrayElements, constProp.unreachableLines) ?? [];
|
|
22429
|
+
const arrayFlows = detectArrayElementFlows(calls, sources, sinks, constProp.taintedArrayElements, constProp.unreachableLines, types) ?? [];
|
|
22406
22430
|
for (const f of arrayFlows) {
|
|
22407
22431
|
if (!flows.some((x) => x.source_line === f.source_line && x.sink_line === f.sink_line)) {
|
|
22408
22432
|
flows.push(f);
|
|
22409
22433
|
}
|
|
22410
22434
|
}
|
|
22411
|
-
const collectionFlows = detectCollectionFlows(calls, sources, sinks, constProp.tainted, constProp.unreachableLines, ctx.code) ?? [];
|
|
22435
|
+
const collectionFlows = detectCollectionFlows(calls, sources, sinks, constProp.tainted, constProp.unreachableLines, ctx.code, types) ?? [];
|
|
22412
22436
|
for (const f of collectionFlows) {
|
|
22413
22437
|
if (flows.some((x) => x.source_line === f.source_line && x.sink_line === f.sink_line))
|
|
22414
22438
|
continue;
|
|
@@ -22459,7 +22483,7 @@ class TaintPropagationPass {
|
|
|
22459
22483
|
flows.push(f);
|
|
22460
22484
|
}
|
|
22461
22485
|
const sanitizedNames = constProp.sanitizedVars;
|
|
22462
|
-
|
|
22486
|
+
let finalFlows = sanitizedNames.size === 0 ? flows : flows.filter((f) => {
|
|
22463
22487
|
if (f.path.length === 0)
|
|
22464
22488
|
return true;
|
|
22465
22489
|
const sourceVar = f.path[0].variable;
|
|
@@ -22473,10 +22497,60 @@ class TaintPropagationPass {
|
|
|
22473
22497
|
}
|
|
22474
22498
|
return true;
|
|
22475
22499
|
});
|
|
22500
|
+
if (ctx.language === "java" && typeof ctx.code === "string") {
|
|
22501
|
+
finalFlows = finalFlows.filter((f) => {
|
|
22502
|
+
if (f.sink_type !== "path_traversal" && f.sink_type !== "xxe")
|
|
22503
|
+
return true;
|
|
22504
|
+
if (!isInJavaSanitizedMethod(ctx.code, types, f.sink_line, f.sink_type))
|
|
22505
|
+
return true;
|
|
22506
|
+
return false;
|
|
22507
|
+
});
|
|
22508
|
+
}
|
|
22476
22509
|
return { flows: finalFlows };
|
|
22477
22510
|
}
|
|
22478
22511
|
}
|
|
22479
|
-
function
|
|
22512
|
+
function isInJavaSanitizedMethod(code, types, sinkLine, sinkType) {
|
|
22513
|
+
if (!types || types.length === 0)
|
|
22514
|
+
return false;
|
|
22515
|
+
let methodStart = -1;
|
|
22516
|
+
let methodEnd = -1;
|
|
22517
|
+
for (const t of types) {
|
|
22518
|
+
for (const m of t.methods) {
|
|
22519
|
+
if (sinkLine >= m.start_line && sinkLine <= m.end_line) {
|
|
22520
|
+
methodStart = m.start_line;
|
|
22521
|
+
methodEnd = m.end_line;
|
|
22522
|
+
break;
|
|
22523
|
+
}
|
|
22524
|
+
}
|
|
22525
|
+
if (methodStart > 0)
|
|
22526
|
+
break;
|
|
22527
|
+
}
|
|
22528
|
+
if (methodStart < 0)
|
|
22529
|
+
return false;
|
|
22530
|
+
const lines = code.split(`
|
|
22531
|
+
`);
|
|
22532
|
+
const body2 = lines.slice(methodStart - 1, methodEnd).join(`
|
|
22533
|
+
`);
|
|
22534
|
+
if (sinkType === "path_traversal") {
|
|
22535
|
+
if (!/\.getCanonicalPath\s*\(/.test(body2))
|
|
22536
|
+
return false;
|
|
22537
|
+
if (!/\.startsWith\s*\([^)]*getCanonicalPath/.test(body2))
|
|
22538
|
+
return false;
|
|
22539
|
+
if (!/\bthrow\s+new\b/.test(body2))
|
|
22540
|
+
return false;
|
|
22541
|
+
return true;
|
|
22542
|
+
}
|
|
22543
|
+
if (sinkType === "xxe") {
|
|
22544
|
+
const setFeatureRe = /\.setFeature\s*\(\s*"(?:[^"]*disallow-doctype-decl|[^"]*external-general-entities|[^"]*external-parameter-entities|[^"]*load-external-dtd)"/;
|
|
22545
|
+
if (setFeatureRe.test(body2))
|
|
22546
|
+
return true;
|
|
22547
|
+
if (/\.setProperty\s*\([^,]*SUPPORT_DTD[^,]*,\s*false\s*\)/.test(body2))
|
|
22548
|
+
return true;
|
|
22549
|
+
return false;
|
|
22550
|
+
}
|
|
22551
|
+
return false;
|
|
22552
|
+
}
|
|
22553
|
+
function detectCollectionFlows(calls, sources, sinks, taintedVars, unreachableLines, code, types) {
|
|
22480
22554
|
const flows = [];
|
|
22481
22555
|
const callsByLine = new Map;
|
|
22482
22556
|
for (const call of calls) {
|
|
@@ -22497,8 +22571,11 @@ function detectCollectionFlows(calls, sources, sinks, taintedVars, unreachableLi
|
|
|
22497
22571
|
const varName = arg.variable;
|
|
22498
22572
|
const scopedName = call.in_method ? `${call.in_method}:${varName}` : varName;
|
|
22499
22573
|
if (taintedVars.has(varName) || taintedVars.has(scopedName)) {
|
|
22500
|
-
const source = sources
|
|
22574
|
+
const source = pickScopedSource(sources, sink.line, call.in_method ?? null, types, varName);
|
|
22501
22575
|
if (source) {
|
|
22576
|
+
if (source.variable && source.variable !== varName && source.in_method && call.in_method && source.in_method !== call.in_method) {
|
|
22577
|
+
continue;
|
|
22578
|
+
}
|
|
22502
22579
|
if (typeof code === "string" && isReassignedToLiteralBetween(code, varName, source.line, sink.line)) {
|
|
22503
22580
|
continue;
|
|
22504
22581
|
}
|
|
@@ -22534,8 +22611,11 @@ function detectCollectionFlows(calls, sources, sinks, taintedVars, unreachableLi
|
|
|
22534
22611
|
const collectionVar = match[1];
|
|
22535
22612
|
const scopedCollection = call.in_method ? `${call.in_method}:${collectionVar}` : collectionVar;
|
|
22536
22613
|
if (taintedVars.has(collectionVar) || taintedVars.has(scopedCollection)) {
|
|
22537
|
-
const source = sources
|
|
22614
|
+
const source = pickScopedSource(sources, sink.line, call.in_method ?? null, types, collectionVar);
|
|
22538
22615
|
if (source) {
|
|
22616
|
+
if (source.variable && source.variable !== collectionVar && source.in_method && call.in_method && source.in_method !== call.in_method) {
|
|
22617
|
+
continue;
|
|
22618
|
+
}
|
|
22539
22619
|
if (typeof code === "string" && isReassignedToLiteralBetween(code, collectionVar, source.line, sink.line)) {
|
|
22540
22620
|
continue;
|
|
22541
22621
|
}
|
|
@@ -22561,7 +22641,7 @@ function detectCollectionFlows(calls, sources, sinks, taintedVars, unreachableLi
|
|
|
22561
22641
|
}
|
|
22562
22642
|
return flows;
|
|
22563
22643
|
}
|
|
22564
|
-
function detectArrayElementFlows(calls, sources, sinks, taintedArrayElements, unreachableLines) {
|
|
22644
|
+
function detectArrayElementFlows(calls, sources, sinks, taintedArrayElements, unreachableLines, types) {
|
|
22565
22645
|
const flows = [];
|
|
22566
22646
|
const callsByLine = new Map;
|
|
22567
22647
|
for (const call of calls) {
|
|
@@ -22586,7 +22666,7 @@ function detectArrayElementFlows(calls, sources, sinks, taintedArrayElements, un
|
|
|
22586
22666
|
if (taintedIndices) {
|
|
22587
22667
|
const isTainted = taintedIndices.has(indexStr) || taintedIndices.has("*");
|
|
22588
22668
|
if (isTainted) {
|
|
22589
|
-
const source = sources
|
|
22669
|
+
const source = pickScopedSource(sources, sink.line, call.in_method ?? null, types, arrayName);
|
|
22590
22670
|
if (source) {
|
|
22591
22671
|
flows.push({
|
|
22592
22672
|
source_line: source.line,
|
|
@@ -22690,11 +22770,12 @@ function isReassignedToLiteralBetween(code, variable, srcLine, sinkLine) {
|
|
|
22690
22770
|
const strLit = `(?:"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"|'[^'\\\\]*(?:\\\\.[^'\\\\]*)*'|\`[^\`\\\\]*(?:\\\\.[^\`\\\\]*)*\`)`;
|
|
22691
22771
|
const reNaked = new RegExp(`^\\s*${variable}\\s*(?::?=)\\s*${strLit}\\s*;?\\s*$`);
|
|
22692
22772
|
const reGuarded = new RegExp(`^\\s*if\\b.*\\b${variable}\\s*=\\s*${strLit}\\s*;?\\s*$`);
|
|
22773
|
+
const reSwitchCase = new RegExp(`^\\s*(?:case\\b.*?|default\\s*):\\s*${variable}\\s*=\\s*${strLit}\\s*;?\\s*(?:break\\s*;?)?\\s*$`);
|
|
22693
22774
|
for (let i2 = lo;i2 < hi; i2++) {
|
|
22694
22775
|
const line = lines[i2];
|
|
22695
22776
|
if (!line)
|
|
22696
22777
|
continue;
|
|
22697
|
-
if (reNaked.test(line) || reGuarded.test(line))
|
|
22778
|
+
if (reNaked.test(line) || reGuarded.test(line) || reSwitchCase.test(line))
|
|
22698
22779
|
return true;
|
|
22699
22780
|
}
|
|
22700
22781
|
return false;
|
|
@@ -22821,6 +22902,9 @@ function detectExpressionScanFlows(calls, sources, sinks, sanitizers, unreachabl
|
|
|
22821
22902
|
for (const source of sourcesWithVar) {
|
|
22822
22903
|
if (source.line >= sink.line)
|
|
22823
22904
|
continue;
|
|
22905
|
+
if (source.in_method && call.in_method && source.in_method !== call.in_method) {
|
|
22906
|
+
continue;
|
|
22907
|
+
}
|
|
22824
22908
|
const re = reCache.get(source.variable);
|
|
22825
22909
|
if (!re || !re.test(expr))
|
|
22826
22910
|
continue;
|
|
@@ -22887,6 +22971,51 @@ function detectExpressionScanFlows(calls, sources, sinks, sanitizers, unreachabl
|
|
|
22887
22971
|
}
|
|
22888
22972
|
return flows;
|
|
22889
22973
|
}
|
|
22974
|
+
function pickScopedSource(sources, sinkLine, methodName, types, taintedVar) {
|
|
22975
|
+
if (sources.length === 0)
|
|
22976
|
+
return;
|
|
22977
|
+
const closestPreceding = (cands) => {
|
|
22978
|
+
let best;
|
|
22979
|
+
for (const s of cands) {
|
|
22980
|
+
if (s.line >= sinkLine)
|
|
22981
|
+
continue;
|
|
22982
|
+
if (!best || s.line > best.line)
|
|
22983
|
+
best = s;
|
|
22984
|
+
}
|
|
22985
|
+
return best;
|
|
22986
|
+
};
|
|
22987
|
+
if (taintedVar) {
|
|
22988
|
+
const byVar = sources.filter((s) => s.variable === taintedVar);
|
|
22989
|
+
const pick = closestPreceding(byVar);
|
|
22990
|
+
if (pick)
|
|
22991
|
+
return pick;
|
|
22992
|
+
}
|
|
22993
|
+
if (methodName && types && types.length > 0) {
|
|
22994
|
+
let methodStart = -1;
|
|
22995
|
+
let methodEnd = -1;
|
|
22996
|
+
for (const t of types) {
|
|
22997
|
+
for (const m of t.methods) {
|
|
22998
|
+
if (m.name === methodName) {
|
|
22999
|
+
methodStart = m.start_line;
|
|
23000
|
+
methodEnd = m.end_line;
|
|
23001
|
+
break;
|
|
23002
|
+
}
|
|
23003
|
+
}
|
|
23004
|
+
if (methodStart > 0)
|
|
23005
|
+
break;
|
|
23006
|
+
}
|
|
23007
|
+
if (methodStart > 0 && methodEnd >= methodStart) {
|
|
23008
|
+
const inScope = sources.filter((s) => s.line >= methodStart && s.line <= methodEnd);
|
|
23009
|
+
const pick = closestPreceding(inScope);
|
|
23010
|
+
if (pick)
|
|
23011
|
+
return pick;
|
|
23012
|
+
}
|
|
23013
|
+
}
|
|
23014
|
+
const globalPick = closestPreceding(sources);
|
|
23015
|
+
if (globalPick)
|
|
23016
|
+
return globalPick;
|
|
23017
|
+
return sources[0];
|
|
23018
|
+
}
|
|
22890
23019
|
|
|
22891
23020
|
// ../circle-ir/dist/analysis/interprocedural.js
|
|
22892
23021
|
function analyzeInterprocedural2(graphOrTypes, callsOrSources, dfgOrSinks, sourcesOrSanitizers, sinksOrOptions, sanitizersArg, optionsArg = {}) {
|
|
@@ -30904,7 +31033,7 @@ var colors = {
|
|
|
30904
31033
|
};
|
|
30905
31034
|
|
|
30906
31035
|
// src/version.ts
|
|
30907
|
-
var version = "3.
|
|
31036
|
+
var version = "3.64.0";
|
|
30908
31037
|
|
|
30909
31038
|
// src/formatters.ts
|
|
30910
31039
|
var SINK_SEVERITY = {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cognium-dev",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.64.0",
|
|
4
4
|
"description": "Static Application Security Testing CLI for detecting security vulnerabilities via taint tracking",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
"registry": "https://registry.npmjs.org/"
|
|
66
66
|
},
|
|
67
67
|
"dependencies": {
|
|
68
|
-
"circle-ir": "^3.
|
|
68
|
+
"circle-ir": "^3.64.0"
|
|
69
69
|
},
|
|
70
70
|
"devDependencies": {
|
|
71
71
|
"@types/node": "^25.5.0",
|