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.
Files changed (2) hide show
  1. package/dist/cli.js +146 -17
  2. 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
- const finalFlows = sanitizedNames.size === 0 ? flows : flows.filter((f) => {
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 detectCollectionFlows(calls, sources, sinks, taintedVars, unreachableLines, code) {
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[0];
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[0];
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[0];
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.62.0";
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.62.0",
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.62.0"
68
+ "circle-ir": "^3.64.0"
69
69
  },
70
70
  "devDependencies": {
71
71
  "@types/node": "^25.5.0",