cognium-dev 3.62.0 → 3.65.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 +160 -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,74 @@ 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
|
+
}
|
|
22509
|
+
if (finalFlows.length > 1) {
|
|
22510
|
+
const bestByKey = new Map;
|
|
22511
|
+
for (const f of finalFlows) {
|
|
22512
|
+
const key = `${f.source_line}|${f.sink_line}|${f.sink_type}`;
|
|
22513
|
+
const cur = bestByKey.get(key);
|
|
22514
|
+
if (!cur || f.confidence > cur.confidence) {
|
|
22515
|
+
bestByKey.set(key, f);
|
|
22516
|
+
}
|
|
22517
|
+
}
|
|
22518
|
+
finalFlows = finalFlows.filter((f) => {
|
|
22519
|
+
const key = `${f.source_line}|${f.sink_line}|${f.sink_type}`;
|
|
22520
|
+
return bestByKey.get(key) === f;
|
|
22521
|
+
});
|
|
22522
|
+
}
|
|
22476
22523
|
return { flows: finalFlows };
|
|
22477
22524
|
}
|
|
22478
22525
|
}
|
|
22479
|
-
function
|
|
22526
|
+
function isInJavaSanitizedMethod(code, types, sinkLine, sinkType) {
|
|
22527
|
+
if (!types || types.length === 0)
|
|
22528
|
+
return false;
|
|
22529
|
+
let methodStart = -1;
|
|
22530
|
+
let methodEnd = -1;
|
|
22531
|
+
for (const t of types) {
|
|
22532
|
+
for (const m of t.methods) {
|
|
22533
|
+
if (sinkLine >= m.start_line && sinkLine <= m.end_line) {
|
|
22534
|
+
methodStart = m.start_line;
|
|
22535
|
+
methodEnd = m.end_line;
|
|
22536
|
+
break;
|
|
22537
|
+
}
|
|
22538
|
+
}
|
|
22539
|
+
if (methodStart > 0)
|
|
22540
|
+
break;
|
|
22541
|
+
}
|
|
22542
|
+
if (methodStart < 0)
|
|
22543
|
+
return false;
|
|
22544
|
+
const lines = code.split(`
|
|
22545
|
+
`);
|
|
22546
|
+
const body2 = lines.slice(methodStart - 1, methodEnd).join(`
|
|
22547
|
+
`);
|
|
22548
|
+
if (sinkType === "path_traversal") {
|
|
22549
|
+
if (!/\.getCanonicalPath\s*\(/.test(body2))
|
|
22550
|
+
return false;
|
|
22551
|
+
if (!/\.startsWith\s*\([^)]*getCanonicalPath/.test(body2))
|
|
22552
|
+
return false;
|
|
22553
|
+
if (!/\bthrow\s+new\b/.test(body2))
|
|
22554
|
+
return false;
|
|
22555
|
+
return true;
|
|
22556
|
+
}
|
|
22557
|
+
if (sinkType === "xxe") {
|
|
22558
|
+
const setFeatureRe = /\.setFeature\s*\(\s*"(?:[^"]*disallow-doctype-decl|[^"]*external-general-entities|[^"]*external-parameter-entities|[^"]*load-external-dtd)"/;
|
|
22559
|
+
if (setFeatureRe.test(body2))
|
|
22560
|
+
return true;
|
|
22561
|
+
if (/\.setProperty\s*\([^,]*SUPPORT_DTD[^,]*,\s*false\s*\)/.test(body2))
|
|
22562
|
+
return true;
|
|
22563
|
+
return false;
|
|
22564
|
+
}
|
|
22565
|
+
return false;
|
|
22566
|
+
}
|
|
22567
|
+
function detectCollectionFlows(calls, sources, sinks, taintedVars, unreachableLines, code, types) {
|
|
22480
22568
|
const flows = [];
|
|
22481
22569
|
const callsByLine = new Map;
|
|
22482
22570
|
for (const call of calls) {
|
|
@@ -22497,8 +22585,11 @@ function detectCollectionFlows(calls, sources, sinks, taintedVars, unreachableLi
|
|
|
22497
22585
|
const varName = arg.variable;
|
|
22498
22586
|
const scopedName = call.in_method ? `${call.in_method}:${varName}` : varName;
|
|
22499
22587
|
if (taintedVars.has(varName) || taintedVars.has(scopedName)) {
|
|
22500
|
-
const source = sources
|
|
22588
|
+
const source = pickScopedSource(sources, sink.line, call.in_method ?? null, types, varName);
|
|
22501
22589
|
if (source) {
|
|
22590
|
+
if (source.variable && source.variable !== varName && source.in_method && call.in_method && source.in_method !== call.in_method) {
|
|
22591
|
+
continue;
|
|
22592
|
+
}
|
|
22502
22593
|
if (typeof code === "string" && isReassignedToLiteralBetween(code, varName, source.line, sink.line)) {
|
|
22503
22594
|
continue;
|
|
22504
22595
|
}
|
|
@@ -22534,8 +22625,11 @@ function detectCollectionFlows(calls, sources, sinks, taintedVars, unreachableLi
|
|
|
22534
22625
|
const collectionVar = match[1];
|
|
22535
22626
|
const scopedCollection = call.in_method ? `${call.in_method}:${collectionVar}` : collectionVar;
|
|
22536
22627
|
if (taintedVars.has(collectionVar) || taintedVars.has(scopedCollection)) {
|
|
22537
|
-
const source = sources
|
|
22628
|
+
const source = pickScopedSource(sources, sink.line, call.in_method ?? null, types, collectionVar);
|
|
22538
22629
|
if (source) {
|
|
22630
|
+
if (source.variable && source.variable !== collectionVar && source.in_method && call.in_method && source.in_method !== call.in_method) {
|
|
22631
|
+
continue;
|
|
22632
|
+
}
|
|
22539
22633
|
if (typeof code === "string" && isReassignedToLiteralBetween(code, collectionVar, source.line, sink.line)) {
|
|
22540
22634
|
continue;
|
|
22541
22635
|
}
|
|
@@ -22561,7 +22655,7 @@ function detectCollectionFlows(calls, sources, sinks, taintedVars, unreachableLi
|
|
|
22561
22655
|
}
|
|
22562
22656
|
return flows;
|
|
22563
22657
|
}
|
|
22564
|
-
function detectArrayElementFlows(calls, sources, sinks, taintedArrayElements, unreachableLines) {
|
|
22658
|
+
function detectArrayElementFlows(calls, sources, sinks, taintedArrayElements, unreachableLines, types) {
|
|
22565
22659
|
const flows = [];
|
|
22566
22660
|
const callsByLine = new Map;
|
|
22567
22661
|
for (const call of calls) {
|
|
@@ -22586,7 +22680,7 @@ function detectArrayElementFlows(calls, sources, sinks, taintedArrayElements, un
|
|
|
22586
22680
|
if (taintedIndices) {
|
|
22587
22681
|
const isTainted = taintedIndices.has(indexStr) || taintedIndices.has("*");
|
|
22588
22682
|
if (isTainted) {
|
|
22589
|
-
const source = sources
|
|
22683
|
+
const source = pickScopedSource(sources, sink.line, call.in_method ?? null, types, arrayName);
|
|
22590
22684
|
if (source) {
|
|
22591
22685
|
flows.push({
|
|
22592
22686
|
source_line: source.line,
|
|
@@ -22690,11 +22784,12 @@ function isReassignedToLiteralBetween(code, variable, srcLine, sinkLine) {
|
|
|
22690
22784
|
const strLit = `(?:"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"|'[^'\\\\]*(?:\\\\.[^'\\\\]*)*'|\`[^\`\\\\]*(?:\\\\.[^\`\\\\]*)*\`)`;
|
|
22691
22785
|
const reNaked = new RegExp(`^\\s*${variable}\\s*(?::?=)\\s*${strLit}\\s*;?\\s*$`);
|
|
22692
22786
|
const reGuarded = new RegExp(`^\\s*if\\b.*\\b${variable}\\s*=\\s*${strLit}\\s*;?\\s*$`);
|
|
22787
|
+
const reSwitchCase = new RegExp(`^\\s*(?:case\\b.*?|default\\s*):\\s*${variable}\\s*=\\s*${strLit}\\s*;?\\s*(?:break\\s*;?)?\\s*$`);
|
|
22693
22788
|
for (let i2 = lo;i2 < hi; i2++) {
|
|
22694
22789
|
const line = lines[i2];
|
|
22695
22790
|
if (!line)
|
|
22696
22791
|
continue;
|
|
22697
|
-
if (reNaked.test(line) || reGuarded.test(line))
|
|
22792
|
+
if (reNaked.test(line) || reGuarded.test(line) || reSwitchCase.test(line))
|
|
22698
22793
|
return true;
|
|
22699
22794
|
}
|
|
22700
22795
|
return false;
|
|
@@ -22821,6 +22916,9 @@ function detectExpressionScanFlows(calls, sources, sinks, sanitizers, unreachabl
|
|
|
22821
22916
|
for (const source of sourcesWithVar) {
|
|
22822
22917
|
if (source.line >= sink.line)
|
|
22823
22918
|
continue;
|
|
22919
|
+
if (source.in_method && call.in_method && source.in_method !== call.in_method) {
|
|
22920
|
+
continue;
|
|
22921
|
+
}
|
|
22824
22922
|
const re = reCache.get(source.variable);
|
|
22825
22923
|
if (!re || !re.test(expr))
|
|
22826
22924
|
continue;
|
|
@@ -22887,6 +22985,51 @@ function detectExpressionScanFlows(calls, sources, sinks, sanitizers, unreachabl
|
|
|
22887
22985
|
}
|
|
22888
22986
|
return flows;
|
|
22889
22987
|
}
|
|
22988
|
+
function pickScopedSource(sources, sinkLine, methodName, types, taintedVar) {
|
|
22989
|
+
if (sources.length === 0)
|
|
22990
|
+
return;
|
|
22991
|
+
const closestPreceding = (cands) => {
|
|
22992
|
+
let best;
|
|
22993
|
+
for (const s of cands) {
|
|
22994
|
+
if (s.line >= sinkLine)
|
|
22995
|
+
continue;
|
|
22996
|
+
if (!best || s.line > best.line)
|
|
22997
|
+
best = s;
|
|
22998
|
+
}
|
|
22999
|
+
return best;
|
|
23000
|
+
};
|
|
23001
|
+
if (taintedVar) {
|
|
23002
|
+
const byVar = sources.filter((s) => s.variable === taintedVar);
|
|
23003
|
+
const pick = closestPreceding(byVar);
|
|
23004
|
+
if (pick)
|
|
23005
|
+
return pick;
|
|
23006
|
+
}
|
|
23007
|
+
if (methodName && types && types.length > 0) {
|
|
23008
|
+
let methodStart = -1;
|
|
23009
|
+
let methodEnd = -1;
|
|
23010
|
+
for (const t of types) {
|
|
23011
|
+
for (const m of t.methods) {
|
|
23012
|
+
if (m.name === methodName) {
|
|
23013
|
+
methodStart = m.start_line;
|
|
23014
|
+
methodEnd = m.end_line;
|
|
23015
|
+
break;
|
|
23016
|
+
}
|
|
23017
|
+
}
|
|
23018
|
+
if (methodStart > 0)
|
|
23019
|
+
break;
|
|
23020
|
+
}
|
|
23021
|
+
if (methodStart > 0 && methodEnd >= methodStart) {
|
|
23022
|
+
const inScope = sources.filter((s) => s.line >= methodStart && s.line <= methodEnd);
|
|
23023
|
+
const pick = closestPreceding(inScope);
|
|
23024
|
+
if (pick)
|
|
23025
|
+
return pick;
|
|
23026
|
+
}
|
|
23027
|
+
}
|
|
23028
|
+
const globalPick = closestPreceding(sources);
|
|
23029
|
+
if (globalPick)
|
|
23030
|
+
return globalPick;
|
|
23031
|
+
return sources[0];
|
|
23032
|
+
}
|
|
22890
23033
|
|
|
22891
23034
|
// ../circle-ir/dist/analysis/interprocedural.js
|
|
22892
23035
|
function analyzeInterprocedural2(graphOrTypes, callsOrSources, dfgOrSinks, sourcesOrSanitizers, sinksOrOptions, sanitizersArg, optionsArg = {}) {
|
|
@@ -30904,7 +31047,7 @@ var colors = {
|
|
|
30904
31047
|
};
|
|
30905
31048
|
|
|
30906
31049
|
// src/version.ts
|
|
30907
|
-
var version = "3.
|
|
31050
|
+
var version = "3.65.0";
|
|
30908
31051
|
|
|
30909
31052
|
// src/formatters.ts
|
|
30910
31053
|
var SINK_SEVERITY = {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cognium-dev",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.65.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.65.0"
|
|
69
69
|
},
|
|
70
70
|
"devDependencies": {
|
|
71
71
|
"@types/node": "^25.5.0",
|