cognium-dev 3.154.0 → 3.155.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 +127 -13
  2. package/package.json +2 -2
package/dist/cli.js CHANGED
@@ -11659,6 +11659,7 @@ var DEFAULT_SANITIZERS = [
11659
11659
  { method: "parseDouble", class: "Double", removes: ["sql_injection", "command_injection", "path_traversal", "code_injection"] },
11660
11660
  { method: "parseShort", class: "Short", removes: ["sql_injection", "command_injection", "path_traversal", "code_injection"] },
11661
11661
  { method: "parseByte", class: "Byte", removes: ["sql_injection", "command_injection", "path_traversal", "code_injection"] },
11662
+ { method: "parseBoolean", class: "Boolean", removes: ["sql_injection", "command_injection", "path_traversal", "code_injection", "xss", "crlf", "log_injection", "xpath_injection", "ldap_injection", "xxe"] },
11662
11663
  { method: "fromString", class: "UUID", removes: ["sql_injection", "command_injection", "path_traversal", "code_injection"] },
11663
11664
  { method: "BigInt", removes: ["sql_injection", "nosql_injection", "command_injection", "path_traversal", "code_injection"] },
11664
11665
  { method: "Atoi", class: "strconv", removes: ["sql_injection", "command_injection", "path_traversal", "code_injection"] },
@@ -13770,6 +13771,18 @@ class CodeGraph {
13770
13771
  }
13771
13772
  return this._chainsByFromDef;
13772
13773
  }
13774
+ _chainsByToDef = null;
13775
+ get chainsByToDef() {
13776
+ if (!this._chainsByToDef) {
13777
+ this._chainsByToDef = new Map;
13778
+ for (const chain of this.ir.dfg.chains ?? []) {
13779
+ const arr = this._chainsByToDef.get(chain.to_def) ?? [];
13780
+ arr.push(chain);
13781
+ this._chainsByToDef.set(chain.to_def, arr);
13782
+ }
13783
+ }
13784
+ return this._chainsByToDef;
13785
+ }
13773
13786
  _callsByLine = null;
13774
13787
  get callsByLine() {
13775
13788
  if (!this._callsByLine) {
@@ -15640,6 +15653,45 @@ class AnalysisPipeline {
15640
15653
  return { results, findings };
15641
15654
  }
15642
15655
  }
15656
+ // ../circle-ir/dist/analysis/dfg-walk.js
15657
+ function walkBackwardDefs(startDefId, chainsByToDef, defById, options = {}) {
15658
+ const maxHops = options.maxHops ?? 32;
15659
+ const visited = new Set;
15660
+ const lines = new Set;
15661
+ let hopCapReached = false;
15662
+ const startDef = defById.get(startDefId);
15663
+ if (!startDef) {
15664
+ return { visited, lines, hopCapReached: false };
15665
+ }
15666
+ visited.add(startDefId);
15667
+ lines.add(startDef.line);
15668
+ const queue = [startDefId];
15669
+ let hops = 0;
15670
+ while (queue.length > 0) {
15671
+ if (hops >= maxHops) {
15672
+ hopCapReached = true;
15673
+ break;
15674
+ }
15675
+ hops++;
15676
+ const currentId = queue.shift();
15677
+ const incoming = chainsByToDef.get(currentId);
15678
+ if (!incoming)
15679
+ continue;
15680
+ for (const chain of incoming) {
15681
+ const fromId = chain.from_def;
15682
+ if (visited.has(fromId))
15683
+ continue;
15684
+ visited.add(fromId);
15685
+ const fromDef = defById.get(fromId);
15686
+ if (fromDef) {
15687
+ lines.add(fromDef.line);
15688
+ }
15689
+ queue.push(fromId);
15690
+ }
15691
+ }
15692
+ return { visited, lines, hopCapReached };
15693
+ }
15694
+
15643
15695
  // ../circle-ir/dist/analysis/taint-propagation.js
15644
15696
  function buildSanitizersByLine(sanitizers) {
15645
15697
  const out2 = new Map;
@@ -15721,7 +15773,11 @@ function propagateTaint(graphOrDfg, callsOrSources, sourcesOrSinks, sinksOrSanit
15721
15773
  if (allTaintedDefIds.has(use.def_id)) {
15722
15774
  const taintInfo = taintByDefId.get(use.def_id);
15723
15775
  if (taintInfo) {
15724
- const isSanitized = checkSanitized(taintInfo.line, sink.line, sink.type, sanitizersByLine);
15776
+ const isSanitized = checkSanitized(taintInfo.line, sink.line, sink.type, sanitizersByLine, {
15777
+ startDefId: use.def_id,
15778
+ chainsByToDef: graph.chainsByToDef,
15779
+ defById
15780
+ });
15725
15781
  if (!isSanitized.sanitized) {
15726
15782
  const source = sources.find((s) => s.line === taintInfo.sourceLine);
15727
15783
  if (source) {
@@ -15851,20 +15907,36 @@ var KNOWN_SINK_TYPES = new Set([
15851
15907
  "crlf",
15852
15908
  "mass_assignment"
15853
15909
  ]);
15854
- function checkSanitized(_fromLine, toLine, sinkType, sanitizersByLine) {
15855
- const sanitizersAtTarget = sanitizersByLine.get(toLine);
15856
- if (!sanitizersAtTarget || sanitizersAtTarget.length === 0) {
15857
- return { sanitized: false };
15858
- }
15910
+ function checkSanitized(_fromLine, toLine, sinkType, sanitizersByLine, ctx) {
15859
15911
  const isKnownSinkType = KNOWN_SINK_TYPES.has(sinkType);
15860
- for (const san of sanitizersAtTarget) {
15861
- if (isKnownSinkType) {
15862
- if (san.sanitizes.includes(sinkType)) {
15912
+ const sanitizersAtTarget = sanitizersByLine.get(toLine);
15913
+ if (sanitizersAtTarget && sanitizersAtTarget.length > 0) {
15914
+ for (const san of sanitizersAtTarget) {
15915
+ if (isKnownSinkType) {
15916
+ if (san.sanitizes.includes(sinkType)) {
15917
+ return { sanitized: true, sanitizer: san };
15918
+ }
15919
+ } else if (san.sanitizes.length > 0) {
15863
15920
  return { sanitized: true, sanitizer: san };
15864
15921
  }
15865
- } else {
15866
- if (san.sanitizes.length > 0) {
15867
- return { sanitized: true, sanitizer: san };
15922
+ }
15923
+ }
15924
+ if (ctx) {
15925
+ const walk = walkBackwardDefs(ctx.startDefId, ctx.chainsByToDef, ctx.defById, { maxHops: ctx.maxHops ?? 32 });
15926
+ for (const line of walk.lines) {
15927
+ if (line === toLine)
15928
+ continue;
15929
+ const sansAtLine = sanitizersByLine.get(line);
15930
+ if (!sansAtLine || sansAtLine.length === 0)
15931
+ continue;
15932
+ for (const san of sansAtLine) {
15933
+ if (isKnownSinkType) {
15934
+ if (san.sanitizes.includes(sinkType)) {
15935
+ return { sanitized: true, sanitizer: san };
15936
+ }
15937
+ } else if (san.sanitizes.length > 0) {
15938
+ return { sanitized: true, sanitizer: san };
15939
+ }
15868
15940
  }
15869
15941
  }
15870
15942
  }
@@ -24132,6 +24204,20 @@ function findJavaScriptDOMSinks(sourceCode, language) {
24132
24204
  method = "cssText";
24133
24205
  else if (line.includes("style.textContent"))
24134
24206
  method = "textContent";
24207
+ if (method === "document.write" || method === "document.writeln") {
24208
+ const argMatch = line.match(/document\.write(?:ln)?\s*\(\s*([^)]*)\)/);
24209
+ if (argMatch) {
24210
+ const arg = argMatch[1].trim();
24211
+ if (!arg.includes("+")) {
24212
+ const doubleQ = /^"(?:[^"\\]|\\.)*"$/;
24213
+ const singleQ = /^'(?:[^'\\]|\\.)*'$/;
24214
+ const backtick = /^`(?:[^`\\$]|\\.|\$(?!\{))*`$/;
24215
+ if (doubleQ.test(arg) || singleQ.test(arg) || backtick.test(arg)) {
24216
+ break;
24217
+ }
24218
+ }
24219
+ }
24220
+ }
24135
24221
  const alreadyExists = sinks.some((s) => s.line === lineNumber && s.cwe === cwe);
24136
24222
  if (!alreadyExists) {
24137
24223
  sinks.push({ type, cwe, severity, line: lineNumber, location: line.trim().substring(0, 80), method });
@@ -38207,6 +38293,31 @@ class WeakHashPass {
38207
38293
  }
38208
38294
  }
38209
38295
 
38296
+ // ../circle-ir/dist/analysis/path-classification.js
38297
+ var TEST_PATH_PATTERNS = [
38298
+ /(^|\/)test\//i,
38299
+ /(^|\/)tests\//i,
38300
+ /(^|\/)testing\//i,
38301
+ /(^|\/)__tests__\//i,
38302
+ /(^|\/)spec\//i,
38303
+ /_test\.go$/i,
38304
+ /(^|\/)test_[^/]+\.py$/i,
38305
+ /_test\.py$/i,
38306
+ /\.test\.(?:tsx?|jsx?|mjs|cjs)$/i,
38307
+ /\.spec\.(?:tsx?|jsx?|mjs|cjs)$/i,
38308
+ /\.test\.(?:java|kt)$/i
38309
+ ];
38310
+ function isTestPath(filepath) {
38311
+ if (!filepath)
38312
+ return false;
38313
+ const normalized = filepath.replace(/\\/g, "/");
38314
+ for (const pattern of TEST_PATH_PATTERNS) {
38315
+ if (pattern.test(normalized))
38316
+ return true;
38317
+ }
38318
+ return false;
38319
+ }
38320
+
38210
38321
  // ../circle-ir/dist/analysis/passes/weak-crypto-pass.js
38211
38322
  var WEAK_CIPHER_BASES = new Set([
38212
38323
  "des",
@@ -38422,6 +38533,9 @@ class WeakCryptoPass {
38422
38533
  if (isProtocolMandatedCryptoFile(file, code)) {
38423
38534
  return { findings: [] };
38424
38535
  }
38536
+ if (isTestPath(file)) {
38537
+ return { findings: [] };
38538
+ }
38425
38539
  const findings = [];
38426
38540
  const constProp = ctx.hasResult("constant-propagation") ? ctx.getResult("constant-propagation") : null;
38427
38541
  const literalBindings = scanLiteralBindings(code, language);
@@ -43125,7 +43239,7 @@ var colors = {
43125
43239
  };
43126
43240
 
43127
43241
  // src/version.ts
43128
- var version = "3.154.0";
43242
+ var version = "3.155.0";
43129
43243
 
43130
43244
  // src/formatters.ts
43131
43245
  var LIBRARY_API_SURFACE_TAG2 = "library-api-surface:caller-responsibility";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cognium-dev",
3
- "version": "3.154.0",
3
+ "version": "3.155.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",
@@ -66,7 +66,7 @@
66
66
  },
67
67
  "dependencies": {
68
68
  "@cognium/project-profile-detect": "^1.1.0",
69
- "circle-ir": "^3.154.0"
69
+ "circle-ir": "^3.155.0"
70
70
  },
71
71
  "devDependencies": {
72
72
  "@types/node": "^25.5.0",