cognium-dev 4.7.0 → 4.7.1

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 +100 -17
  2. package/package.json +2 -2
package/dist/cli.js CHANGED
@@ -9350,13 +9350,15 @@ function computeChains(defs, uses) {
9350
9350
  existing.push(use);
9351
9351
  usesByLine.set(use.line, existing);
9352
9352
  }
9353
+ const seenChains = new Set;
9353
9354
  for (const def of defs) {
9354
9355
  if (def.kind === "local") {
9355
9356
  const usesOnLine = usesByLine.get(def.line) ?? [];
9356
9357
  for (const use of usesOnLine) {
9357
9358
  if (use.def_id !== null && use.def_id !== def.id) {
9358
- const exists = chains.some((c) => c.from_def === use.def_id && c.to_def === def.id && c.via === use.variable);
9359
- if (!exists) {
9359
+ const key = `${use.def_id}\x00${def.id}\x00${use.variable}`;
9360
+ if (!seenChains.has(key)) {
9361
+ seenChains.add(key);
9360
9362
  chains.push({
9361
9363
  from_def: use.def_id,
9362
9364
  to_def: def.id,
@@ -11531,7 +11533,7 @@ var DEFAULT_SINKS = [
11531
11533
  { method: "validate", type: "xss", cwe: "CWE-79", severity: "medium", arg_positions: [0] },
11532
11534
  { method: "startElement", class: "ContentHandler", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0, 1, 2] },
11533
11535
  { method: "characters", class: "ContentHandler", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0] },
11534
- { method: "output", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0] },
11536
+ { method: "output", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0], exclude_languages: ["rust"] },
11535
11537
  { method: "setOutput", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0] },
11536
11538
  { method: "writeAttribute", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0, 1] },
11537
11539
  { method: "startElement", class: "MagicSAXFilter", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0, 1, 2] },
@@ -25032,6 +25034,7 @@ class LanguageSourcesPass {
25032
25034
  additionalSources.push(...findSetterChainSources(types, code, language));
25033
25035
  additionalSources.push(...findJavaScriptAssignmentSources(code, language));
25034
25036
  additionalSources.push(...findCSharpRequestSources(code, language));
25037
+ additionalSources.push(...findGoArgvSources(code, language));
25035
25038
  const jsDOMSinks = findJavaScriptDOMSinks(code, language);
25036
25039
  for (const s of jsDOMSinks) {
25037
25040
  const alreadyExists = additionalSinks.some((x) => x.line === s.line && x.cwe === s.cwe);
@@ -25634,6 +25637,35 @@ function findSetterChainSources(types, sourceCode, language) {
25634
25637
  }
25635
25638
  return sources;
25636
25639
  }
25640
+ function findGoArgvSources(sourceCode, language) {
25641
+ if (language !== "go")
25642
+ return [];
25643
+ const sources = [];
25644
+ const lines = sourceCode.split(`
25645
+ `);
25646
+ const rangeRe = /\bfor\b[^;{]*?,\s*([A-Za-z_]\w*)\s*:?=\s*range\s+os\.Args\b/;
25647
+ const assignRe = /\b([A-Za-z_]\w*)\s*:?=\s*os\.Args\b/;
25648
+ for (let i2 = 0;i2 < lines.length; i2++) {
25649
+ const line = lines[i2];
25650
+ const rm = rangeRe.exec(line);
25651
+ const m = rm ?? assignRe.exec(line);
25652
+ if (!m)
25653
+ continue;
25654
+ const varName = m[1];
25655
+ const lineNumber = i2 + 1;
25656
+ if (sources.some((s) => s.line === lineNumber && s.variable === varName))
25657
+ continue;
25658
+ sources.push({
25659
+ type: "io_input",
25660
+ location: `${varName} = os.Args`,
25661
+ severity: "high",
25662
+ line: lineNumber,
25663
+ confidence: 1,
25664
+ variable: varName
25665
+ });
25666
+ }
25667
+ return sources;
25668
+ }
25637
25669
  function findCSharpRequestSources(sourceCode, language) {
25638
25670
  if (language !== "csharp")
25639
25671
  return [];
@@ -26597,7 +26629,7 @@ function findBashTaintSources(sourceCode, dfg) {
26597
26629
  });
26598
26630
  }
26599
26631
  }
26600
- const envRe = /\$([A-Z][A-Z0-9_]{2,})|\$\{([A-Z][A-Z0-9_]{2,})\}/g;
26632
+ const envRe = /\$([A-Z][A-Z0-9_]{2,})|\$\{([A-Z][A-Z0-9_]{2,})(?:[:#%/^,@!*+?=-][^}]*)?\}/g;
26601
26633
  let em;
26602
26634
  while ((em = envRe.exec(line)) !== null) {
26603
26635
  const envVar = em[1] ?? em[2];
@@ -36419,6 +36451,26 @@ function analyzeInterprocedural2(graphOrTypes, callsOrSources, dfgOrSinks, sourc
36419
36451
  "Command",
36420
36452
  "CommandContext"
36421
36453
  ]);
36454
+ const ormQueryBuilderMethods = new Set([
36455
+ "findMany",
36456
+ "findFirst",
36457
+ "findUnique",
36458
+ "findUniqueOrThrow",
36459
+ "findFirstOrThrow",
36460
+ "findOne",
36461
+ "findAll",
36462
+ "findByPk",
36463
+ "findAndCountAll",
36464
+ "findBy",
36465
+ "findOneBy",
36466
+ "where",
36467
+ "andWhere",
36468
+ "orWhere",
36469
+ "whereIn",
36470
+ "whereNot",
36471
+ "first",
36472
+ "bind"
36473
+ ]);
36422
36474
  const sanitizerMethods = new Set;
36423
36475
  for (const san of sanitizers) {
36424
36476
  sanitizerMethods.add(san.method);
@@ -36443,7 +36495,7 @@ function analyzeInterprocedural2(graphOrTypes, callsOrSources, dfgOrSinks, sourc
36443
36495
  }
36444
36496
  const targetMethod = getMethodNode(methodNodes, call.method_name);
36445
36497
  if (!targetMethod) {
36446
- if (taintedArgPositions.length > 0 && !collectionMethods.has(call.method_name) && !sanitizerMethods.has(call.method_name) && !safeUtilityMethods.has(call.method_name)) {
36498
+ if (taintedArgPositions.length > 0 && !collectionMethods.has(call.method_name) && !sanitizerMethods.has(call.method_name) && !safeUtilityMethods.has(call.method_name) && !ormQueryBuilderMethods.has(call.method_name)) {
36447
36499
  const isBash = graph.ir.meta.language === "bash";
36448
36500
  const bashSafeBuiltins = new Set([
36449
36501
  "echo",
@@ -38233,6 +38285,7 @@ var SKIP_NAMES3 = new Set([
38233
38285
  "unknown",
38234
38286
  "bigint"
38235
38287
  ]);
38288
+ var EMPTY_LINES = [];
38236
38289
 
38237
38290
  class UnusedVariablePass {
38238
38291
  name = "unused-variable";
@@ -38247,6 +38300,35 @@ class UnusedVariablePass {
38247
38300
  `);
38248
38301
  const unusedVars = [];
38249
38302
  const reported = new Set;
38303
+ const defsByVar = new Map;
38304
+ for (const d of graph.ir.dfg.defs) {
38305
+ const arr = defsByVar.get(d.variable);
38306
+ if (arr)
38307
+ arr.push(d);
38308
+ else
38309
+ defsByVar.set(d.variable, [d]);
38310
+ }
38311
+ const nameLines = new Map;
38312
+ const idRe = /[A-Za-z_][A-Za-z0-9_]*/g;
38313
+ for (let i2 = 0;i2 < codeLines.length; i2++) {
38314
+ const line = codeLines[i2];
38315
+ idRe.lastIndex = 0;
38316
+ let seen = null;
38317
+ let m;
38318
+ while ((m = idRe.exec(line)) !== null) {
38319
+ const w = m[0];
38320
+ if (seen === null)
38321
+ seen = new Set;
38322
+ if (seen.has(w))
38323
+ continue;
38324
+ seen.add(w);
38325
+ const arr = nameLines.get(w);
38326
+ if (arr)
38327
+ arr.push(i2 + 1);
38328
+ else
38329
+ nameLines.set(w, [i2 + 1]);
38330
+ }
38331
+ }
38250
38332
  for (const def of graph.ir.dfg.defs) {
38251
38333
  if (def.kind !== "local")
38252
38334
  continue;
@@ -38263,19 +38345,20 @@ class UnusedVariablePass {
38263
38345
  const uses = graph.usesOfDef(def.id);
38264
38346
  if (uses.length > 0)
38265
38347
  continue;
38266
- const otherDefs = graph.ir.dfg.defs.filter((d) => d.variable === variable && d.id !== def.id);
38348
+ const otherDefs = (defsByVar.get(variable) ?? []).filter((d) => d.id !== def.id);
38267
38349
  const otherDefLines = new Set(otherDefs.map((d) => d.line));
38268
- const escapedName = variable.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
38269
- const namePattern = new RegExp(`\\b${escapedName}\\b`);
38350
+ const plainName = /^[A-Za-z_][A-Za-z0-9_]*$/.test(variable);
38351
+ const idxLines = plainName ? nameLines.get(variable) ?? EMPTY_LINES : null;
38352
+ const namePattern = plainName ? null : new RegExp(`\\b${variable.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\b`);
38270
38353
  if (otherDefLines.size === 0) {
38271
- const usedElsewhere = codeLines.some((line, idx) => idx !== def.line - 1 && namePattern.test(line));
38354
+ const usedElsewhere = idxLines !== null ? idxLines.some((ln) => ln !== def.line) : codeLines.some((line, idx) => idx !== def.line - 1 && namePattern.test(line));
38272
38355
  if (usedElsewhere)
38273
38356
  continue;
38274
38357
  } else {
38275
38358
  const lineText2 = codeLines[def.line - 1] ?? "";
38276
38359
  const isTrueDeclaration = /\b(?:let|const|var)\s+[\w{[]/.test(lineText2) || /\b(?:int|long|float|double|boolean|byte|char|short|var|final)\b/.test(lineText2) || /\b[A-Z]\w*(?:<[^>]*>)?\s+\w/.test(lineText2) || /\blet\s+(?:mut\s+)?\w/.test(lineText2);
38277
38360
  if (isTrueDeclaration) {
38278
- const usedBeforeNextDef = codeLines.some((line, idx) => {
38361
+ const usedBeforeNextDef = idxLines !== null ? idxLines.some((ln) => ln !== def.line && !otherDefLines.has(ln) && !otherDefs.some((d) => d.line > def.line && d.line < ln)) : codeLines.some((line, idx) => {
38279
38362
  const lineNum = idx + 1;
38280
38363
  if (lineNum === def.line || otherDefLines.has(lineNum))
38281
38364
  return false;
@@ -38286,7 +38369,7 @@ class UnusedVariablePass {
38286
38369
  if (usedBeforeNextDef)
38287
38370
  continue;
38288
38371
  } else {
38289
- const usedOnNonDefLine = codeLines.some((line, idx) => {
38372
+ const usedOnNonDefLine = idxLines !== null ? idxLines.some((ln) => ln !== def.line && !otherDefLines.has(ln)) : codeLines.some((line, idx) => {
38290
38373
  const lineNum = idx + 1;
38291
38374
  return lineNum !== def.line && !otherDefLines.has(lineNum) && namePattern.test(line);
38292
38375
  });
@@ -43712,9 +43795,7 @@ class InfoDisclosureStacktracePass {
43712
43795
  // ../circle-ir/dist/analysis/passes/unrestricted-file-upload-pass.js
43713
43796
  var UPLOAD_NAME_RE = /(?:getOriginalFilename|getSubmittedFileName|originalname|originalName|\.filename|\.Filename|FileHeader\.Filename|UploadFile)/;
43714
43797
  var FILE_SAFE_CALL_RE = /(?:secure_filename|FilenameUtils\.getExtension|\.lastIndexOf\(['"]\.['"]\)|ALLOWED_EXT|ALLOWED_EXTENSIONS|allowedExtensions|\bfileFilter\b|filepath\.Ext|path\.extname)/;
43715
- function lineWindow(code, startLine, endLine) {
43716
- const lines = code.split(`
43717
- `);
43798
+ function lineWindow(lines, startLine, endLine) {
43718
43799
  const s = Math.max(0, startLine - 1);
43719
43800
  const e = Math.min(lines.length, endLine);
43720
43801
  return lines.slice(s, e).join(`
@@ -43738,10 +43819,12 @@ class UnrestrictedFileUploadPass {
43738
43819
  const { graph, language, code } = ctx;
43739
43820
  const file = graph.ir.meta.file;
43740
43821
  const findings = [];
43822
+ const codeLines = code.split(`
43823
+ `);
43741
43824
  const safeFunctionRanges = [];
43742
43825
  for (const t of graph.ir.types) {
43743
43826
  for (const m of t.methods) {
43744
- const body2 = lineWindow(code, m.start_line, m.end_line);
43827
+ const body2 = lineWindow(codeLines, m.start_line, m.end_line);
43745
43828
  if (FILE_SAFE_CALL_RE.test(body2)) {
43746
43829
  safeFunctionRanges.push({ start: m.start_line, end: m.end_line });
43747
43830
  }
@@ -43752,7 +43835,7 @@ class UnrestrictedFileUploadPass {
43752
43835
  if (line >= r.start && line <= r.end)
43753
43836
  return true;
43754
43837
  }
43755
- const win = lineWindow(code, Math.max(1, line - 20), line + 5);
43838
+ const win = lineWindow(codeLines, Math.max(1, line - 20), line + 5);
43756
43839
  return FILE_SAFE_CALL_RE.test(win);
43757
43840
  };
43758
43841
  if (language === "java") {
@@ -48002,7 +48085,7 @@ var colors = {
48002
48085
  };
48003
48086
 
48004
48087
  // src/version.ts
48005
- var version = "4.7.0";
48088
+ var version = "4.7.1";
48006
48089
 
48007
48090
  // src/formatters.ts
48008
48091
  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": "4.7.0",
3
+ "version": "4.7.1",
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": "^4.7.0"
69
+ "circle-ir": "^4.7.1"
70
70
  },
71
71
  "devDependencies": {
72
72
  "@types/node": "^25.5.0",