cognium-dev 4.9.29 → 4.11.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 +236 -10
  2. package/package.json +4 -4
package/dist/cli.js CHANGED
@@ -3667,7 +3667,7 @@ function detectLanguage(tree) {
3667
3667
  return "python";
3668
3668
  return jsScore > javaScore ? "javascript" : "java";
3669
3669
  }
3670
- function extractTypes(tree, cache, language) {
3670
+ function extractTypes(tree, cache, language, options) {
3671
3671
  const effectiveLanguage = language ?? detectLanguage(tree);
3672
3672
  const isJavaScript = effectiveLanguage === "javascript" || effectiveLanguage === "typescript" || effectiveLanguage === "tsx";
3673
3673
  const isPython = effectiveLanguage === "python";
@@ -3687,7 +3687,7 @@ function extractTypes(tree, cache, language) {
3687
3687
  if (effectiveLanguage === "csharp") {
3688
3688
  return extractCSharpTypes(tree, cache);
3689
3689
  }
3690
- return extractJavaTypes(tree, cache);
3690
+ return extractJavaTypes(tree, cache, options);
3691
3691
  }
3692
3692
  function csharpMemberNodes(body2) {
3693
3693
  if (!body2)
@@ -3843,7 +3843,7 @@ function extractCSharpModifiers(node) {
3843
3843
  }
3844
3844
  return mods;
3845
3845
  }
3846
- function extractJavaTypes(tree, cache) {
3846
+ function extractJavaTypes(tree, cache, options) {
3847
3847
  const types = [];
3848
3848
  const classes = getNodesFromCache(tree.rootNode, "class_declaration", cache);
3849
3849
  for (const cls of classes) {
@@ -3857,8 +3857,81 @@ function extractJavaTypes(tree, cache) {
3857
3857
  for (const enumDecl of enums) {
3858
3858
  types.push(extractEnumInfo(enumDecl));
3859
3859
  }
3860
+ if (!options?.navigationTypes)
3861
+ return types;
3862
+ for (const rec of getNodesFromCache(tree.rootNode, "record_declaration", cache)) {
3863
+ const info2 = extractClassInfo(rec);
3864
+ info2.is_record = true;
3865
+ for (const comp of recordComponents(rec)) {
3866
+ if (!info2.fields.some((f) => f.name === comp.name)) {
3867
+ info2.fields.push({
3868
+ name: comp.name,
3869
+ type: comp.type,
3870
+ modifiers: ["private", "final"],
3871
+ annotations: []
3872
+ });
3873
+ }
3874
+ if (!info2.methods.some((m) => m.name === comp.name)) {
3875
+ info2.methods.push({
3876
+ name: comp.name,
3877
+ return_type: comp.type,
3878
+ parameters: [],
3879
+ annotations: [],
3880
+ modifiers: ["public"],
3881
+ start_line: rec.startPosition.row + 1,
3882
+ end_line: rec.startPosition.row + 1
3883
+ });
3884
+ }
3885
+ }
3886
+ types.push(info2);
3887
+ }
3888
+ const declNodes = [
3889
+ ...classes,
3890
+ ...interfaces,
3891
+ ...enums,
3892
+ ...getNodesFromCache(tree.rootNode, "record_declaration", cache)
3893
+ ];
3894
+ for (const t of types) {
3895
+ const node = declNodes.find((n) => n.startPosition.row + 1 === t.start_line && getIdentifier(n, "name") === t.name);
3896
+ const path = node ? enclosingTypePath(node) : [];
3897
+ if (path.length > 0)
3898
+ t.enclosing_type = path.join(".");
3899
+ }
3860
3900
  return types;
3861
3901
  }
3902
+ function recordComponents(node) {
3903
+ const out2 = [];
3904
+ const params = node.childForFieldName("parameters");
3905
+ if (!params)
3906
+ return out2;
3907
+ for (let i2 = 0;i2 < params.childCount; i2++) {
3908
+ const c = params.child(i2);
3909
+ if (!c || c.type !== "formal_parameter")
3910
+ continue;
3911
+ const name2 = c.childForFieldName("name");
3912
+ const type = c.childForFieldName("type");
3913
+ if (name2)
3914
+ out2.push({ name: getNodeText(name2), type: type ? getNodeText(type) : null });
3915
+ }
3916
+ return out2;
3917
+ }
3918
+ function enclosingTypePath(node) {
3919
+ const DECLS = new Set([
3920
+ "class_declaration",
3921
+ "interface_declaration",
3922
+ "enum_declaration",
3923
+ "record_declaration"
3924
+ ]);
3925
+ const path = [];
3926
+ for (let p = node.parent;p; p = p.parent) {
3927
+ if (!DECLS.has(p.type))
3928
+ continue;
3929
+ const n = getIdentifier(p, "name");
3930
+ if (n)
3931
+ path.unshift(n);
3932
+ }
3933
+ return path;
3934
+ }
3862
3935
  function extractJavaScriptTypes(tree, cache) {
3863
3936
  const types = [];
3864
3937
  const classes = getNodesFromCache(tree.rootNode, "class_declaration", cache);
@@ -12591,6 +12664,22 @@ var DEFAULT_SINKS = [
12591
12664
  { method: "deserialize", class: "bincode", type: "deserialization", cwe: "CWE-502", severity: "critical", arg_positions: [0] },
12592
12665
  { method: "from_str", class: "toml", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
12593
12666
  { method: "from_str", class: "ron", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
12667
+ { method: "from_slice", class: "rmp_serde", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
12668
+ { method: "from_read", class: "rmp_serde", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
12669
+ { method: "from_read_ref", class: "rmp_serde", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
12670
+ { method: "from_slice", class: "serde_yaml", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
12671
+ { method: "from_slice", class: "toml", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
12672
+ { method: "from_reader", class: "ron", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
12673
+ { method: "from_bytes", class: "postcard", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
12674
+ { method: "from_slice", class: "serde_cbor", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
12675
+ { method: "from_reader", class: "serde_cbor", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
12676
+ { method: "from_reader", class: "ciborium", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
12677
+ { method: "from_str", class: "serde_xml_rs", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
12678
+ { method: "from_reader", class: "serde_xml_rs", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
12679
+ { method: "from_str", class: "quick_xml", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
12680
+ { method: "from_reader", class: "quick_xml", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
12681
+ { method: "from_slice", class: "bincode", type: "deserialization", cwe: "CWE-502", severity: "critical", arg_positions: [0] },
12682
+ { method: "deserialize_from", class: "bincode", type: "deserialization", cwe: "CWE-502", severity: "critical", arg_positions: [0] },
12594
12683
  { method: "body", class: "HttpResponseBuilder", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0] },
12595
12684
  { method: "body", class: "HttpResponse", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0] },
12596
12685
  { method: "Html", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0] },
@@ -27210,6 +27299,27 @@ function findPythonAssignmentSources(sourceCode, language) {
27210
27299
  const lineNumber = lineNum + 1;
27211
27300
  if (line.trimStart().startsWith("#"))
27212
27301
  continue;
27302
+ const forTargetMatch = line.match(/^\s*for\s+([\p{L}\p{N}_]+)\s+in\s+(.+?)\s*:\s*$/u);
27303
+ if (forTargetMatch) {
27304
+ const [, iterVar, iterExpr] = forTargetMatch;
27305
+ for (const { pattern, type } of PYTHON_TAINTED_PATTERNS2) {
27306
+ if (!pattern.test(iterExpr))
27307
+ continue;
27308
+ const alreadyExists = sources.some((s2) => s2.line === lineNumber && s2.type === type);
27309
+ if (!alreadyExists) {
27310
+ sources.push({
27311
+ type,
27312
+ location: `for ${iterVar} in ${iterExpr.trim().substring(0, 50)}${iterExpr.length > 50 ? "..." : ""}`,
27313
+ severity: "high",
27314
+ line: lineNumber,
27315
+ confidence: 0.95,
27316
+ variable: iterVar
27317
+ });
27318
+ }
27319
+ break;
27320
+ }
27321
+ continue;
27322
+ }
27213
27323
  const assignmentMatch = line.match(/^(\s*\w[\w.]*)\s*(?::\s*\w[\w\[\], .]*)?\s*=\s*(.+)/);
27214
27324
  if (!assignmentMatch)
27215
27325
  continue;
@@ -36483,6 +36593,103 @@ class SpeculativePromptParamSourcePass {
36483
36593
  }
36484
36594
  }
36485
36595
 
36596
+ // ../circle-ir/dist/analysis/passes/param-source-flow-gate-pass.js
36597
+ var EXEMPT_LANGUAGES = new Set(["java", "csharp"]);
36598
+ var ROUTE_ANNOTATION = /(?:^|[.\s@])(?:route|api_route|get|post|put|patch|delete|head|options|websocket|api_view|view_config|RequestMapping|GetMapping|PostMapping|PutMapping|DeleteMapping|PatchMapping|All)\s*(?:\(|$)/i;
36599
+ var RUST_ROUTE_ATTR = /^\s*#\[\s*(?:[A-Za-z_][\w]*::)*(?:get|post|put|patch|delete|head|options|route|handler)\s*[(\]]/;
36600
+ var RUST_HANDLER_RETURN = /\b(?:HttpResponse|Responder|Reply|IntoResponse|Response(?:<[^>]*>)?)\b/;
36601
+ function enclosingMethod(methods, line) {
36602
+ let best;
36603
+ for (const m of methods) {
36604
+ if (m.start_line <= line && line <= m.end_line) {
36605
+ if (!best || m.end_line - m.start_line < best.end_line - best.start_line)
36606
+ best = m;
36607
+ }
36608
+ }
36609
+ return best;
36610
+ }
36611
+ function rustAttributesAbove(lines, startLine) {
36612
+ const out2 = [];
36613
+ for (let i2 = startLine - 2;i2 >= 0; i2--) {
36614
+ const t = (lines[i2] ?? "").trim();
36615
+ if (t.startsWith("#[")) {
36616
+ out2.push(t);
36617
+ continue;
36618
+ }
36619
+ if (t === "" || t.startsWith("//"))
36620
+ continue;
36621
+ break;
36622
+ }
36623
+ return out2;
36624
+ }
36625
+ function isFrameworkHandler(method, language, lines) {
36626
+ if (method.annotations.some((a) => ROUTE_ANNOTATION.test(a)))
36627
+ return true;
36628
+ if (language === "rust") {
36629
+ if (rustAttributesAbove(lines, method.start_line).some((a) => RUST_ROUTE_ATTR.test(a)))
36630
+ return true;
36631
+ if (method.return_type && RUST_HANDLER_RETURN.test(method.return_type))
36632
+ return true;
36633
+ }
36634
+ return false;
36635
+ }
36636
+
36637
+ class ParamSourceFlowGatePass {
36638
+ keepParamSourcedFlows;
36639
+ name = "param-source-flow-gate";
36640
+ category = "security";
36641
+ constructor(keepParamSourcedFlows) {
36642
+ this.keepParamSourcedFlows = keepParamSourcedFlows;
36643
+ }
36644
+ run(ctx) {
36645
+ const result = { applied: false, dropped: 0, droppedBySinkType: {} };
36646
+ if (this.keepParamSourcedFlows || EXEMPT_LANGUAGES.has(ctx.language))
36647
+ return result;
36648
+ if (!ctx.hasResult("interprocedural"))
36649
+ return result;
36650
+ const flows = ctx.getResult("interprocedural").additionalFlows;
36651
+ result.applied = true;
36652
+ if (flows.length === 0)
36653
+ return result;
36654
+ const methods = ctx.graph.ir.types.flatMap((t) => t.methods);
36655
+ const lines = ctx.code.split(`
36656
+ `);
36657
+ const handlerCache = new Map;
36658
+ const isHandler = (m) => {
36659
+ let v = handlerCache.get(m);
36660
+ if (v === undefined) {
36661
+ v = isFrameworkHandler(m, ctx.language, lines);
36662
+ handlerCache.set(m, v);
36663
+ }
36664
+ return v;
36665
+ };
36666
+ const realSourceLines = new Set;
36667
+ if (ctx.hasResult("sink-filter")) {
36668
+ const sources = ctx.getResult("sink-filter").sources;
36669
+ for (const s of sources) {
36670
+ if (s.type !== "interprocedural_param")
36671
+ realSourceLines.add(s.line);
36672
+ }
36673
+ }
36674
+ let w = 0;
36675
+ for (const f of flows) {
36676
+ let keep = true;
36677
+ if (f.source_type === "interprocedural_param" && !realSourceLines.has(f.source_line)) {
36678
+ const m = enclosingMethod(methods, f.source_line);
36679
+ keep = m !== undefined && isHandler(m);
36680
+ }
36681
+ if (keep)
36682
+ flows[w++] = f;
36683
+ else {
36684
+ result.dropped++;
36685
+ result.droppedBySinkType[f.sink_type] = (result.droppedBySinkType[f.sink_type] ?? 0) + 1;
36686
+ }
36687
+ }
36688
+ flows.length = w;
36689
+ return result;
36690
+ }
36691
+ }
36692
+
36486
36693
  // ../circle-ir/dist/analysis/passes/cli-main-reflection-suppress-pass.js
36487
36694
  var REFLECTION_SINK_METHODS = new Set([
36488
36695
  "forName",
@@ -43738,6 +43945,13 @@ var PROVIDER_PATTERNS = [
43738
43945
  level: "error",
43739
43946
  fix: "Revoke the GitHub refresh token and store secrets outside source control."
43740
43947
  },
43948
+ {
43949
+ name: "GitLab personal access token",
43950
+ regex: /\bglpat-[A-Za-z0-9_-]{20,}\b/,
43951
+ severity: "critical",
43952
+ level: "error",
43953
+ fix: "Revoke the GitLab personal access token and store it outside source control."
43954
+ },
43741
43955
  {
43742
43956
  name: "Stripe live secret key",
43743
43957
  regex: /\bsk_live_[A-Za-z0-9]{24,}\b/,
@@ -43745,6 +43959,13 @@ var PROVIDER_PATTERNS = [
43745
43959
  level: "error",
43746
43960
  fix: "Rotate the Stripe secret key in the Stripe Dashboard and load it from a secrets manager."
43747
43961
  },
43962
+ {
43963
+ name: "Stripe live restricted key",
43964
+ regex: /\brk_live_[A-Za-z0-9]{24,}\b/,
43965
+ severity: "critical",
43966
+ level: "error",
43967
+ fix: "Rotate the Stripe restricted key in the Stripe Dashboard and load it from a secrets manager."
43968
+ },
43748
43969
  {
43749
43970
  name: "Stripe live publishable key",
43750
43971
  regex: /\bpk_live_[A-Za-z0-9]{24,}\b/,
@@ -44096,6 +44317,9 @@ class ScanSecretsPass {
44096
44317
  const m = pattern.regex.exec(lineText);
44097
44318
  if (!m)
44098
44319
  continue;
44320
+ if ((pattern.name === "Stripe live restricted key" || pattern.name === "GitLab personal access token") && PLACEHOLDER_RE.test(m[0])) {
44321
+ continue;
44322
+ }
44099
44323
  if (pattern.name === "PEM private key" && !pemHasInlineBody(lines, i2)) {
44100
44324
  continue;
44101
44325
  }
@@ -49270,7 +49494,7 @@ async function analyze(code, filePath, language, options = {}) {
49270
49494
  };
49271
49495
  }
49272
49496
  const tTypes = phaseStart();
49273
- const types = extractTypes(tree, nodeCache, language);
49497
+ const types = extractTypes(tree, nodeCache, language, { navigationTypes: options.navigationTypes });
49274
49498
  phaseEnd("extractTypes", tTypes);
49275
49499
  const tCalls = phaseStart();
49276
49500
  const calls = extractCalls(tree, nodeCache, language);
@@ -49335,6 +49559,8 @@ async function analyze(code, filePath, language, options = {}) {
49335
49559
  pipeline.add(new InterproceduralPass({
49336
49560
  enableEntryPointGate: options.enableEntryPointGate ?? true
49337
49561
  }));
49562
+ if (!disabledPasses.has("param-source-flow-gate"))
49563
+ pipeline.add(new ParamSourceFlowGatePass(options.speculativeParamSources === true));
49338
49564
  if (!disabledPasses.has("library-profile-cwe22-path-gate"))
49339
49565
  pipeline.add(new LibraryProfileCwe22PathGatePass);
49340
49566
  if (!disabledPasses.has("scan-secrets"))
@@ -50785,7 +51011,7 @@ var colors = {
50785
51011
  };
50786
51012
 
50787
51013
  // src/version.ts
50788
- var version = "4.9.29";
51014
+ var version = "4.11.0";
50789
51015
 
50790
51016
  // src/formatters.ts
50791
51017
  var LIBRARY_API_SURFACE_TAG2 = "library-api-surface:caller-responsibility";
@@ -51337,8 +51563,8 @@ function generateSarifResults(results, crossFileData) {
51337
51563
 
51338
51564
  // src/build-info.ts
51339
51565
  var buildInfo = {
51340
- gitSha: "9d1d45a",
51341
- builtAt: "2026-10-01T03:14:59.073Z"
51566
+ gitSha: "165e82d",
51567
+ builtAt: "2026-10-07T23:49:13.604Z"
51342
51568
  };
51343
51569
 
51344
51570
  // src/utils/args.ts
@@ -51589,7 +51815,7 @@ function loadConfig(profilePath) {
51589
51815
  const content = readFileSync(configPath, "utf-8");
51590
51816
  return JSON.parse(content);
51591
51817
  } catch (err2) {
51592
- throw new Error(`Failed to parse ${configPath}: ${err2 instanceof Error ? err2.message : String(err2)}. ` + "Fix the JSON or remove the file.");
51818
+ throw new Error(`Failed to parse ${configPath}: ${err2 instanceof Error ? err2.message : String(err2)}. ` + "Fix the JSON or remove the file.", { cause: err2 });
51593
51819
  }
51594
51820
  }
51595
51821
  function convertConfigToPassOptions(config) {
@@ -51966,7 +52192,7 @@ async function initWasm(spin) {
51966
52192
  go: join3(wasmDir, "tree-sitter-go.wasm"),
51967
52193
  java: join3(wasmDir, "tree-sitter-java.wasm"),
51968
52194
  javascript: join3(wasmDir, "tree-sitter-javascript.wasm"),
51969
- typescript: join3(wasmDir, "tree-sitter-javascript.wasm"),
52195
+ typescript: join3(wasmDir, "tree-sitter-typescript.wasm"),
51970
52196
  python: join3(wasmDir, "tree-sitter-python.wasm"),
51971
52197
  rust: join3(wasmDir, "tree-sitter-rust.wasm"),
51972
52198
  html: join3(wasmDir, "tree-sitter-html.wasm"),
@@ -52001,7 +52227,7 @@ Please ensure the wasm/ directory is located next to the binary or in your curre
52001
52227
  go: wasmBasePath + "tree-sitter-go.wasm",
52002
52228
  java: wasmBasePath + "tree-sitter-java.wasm",
52003
52229
  javascript: wasmBasePath + "tree-sitter-javascript.wasm",
52004
- typescript: wasmBasePath + "tree-sitter-javascript.wasm",
52230
+ typescript: wasmBasePath + "tree-sitter-typescript.wasm",
52005
52231
  python: wasmBasePath + "tree-sitter-python.wasm",
52006
52232
  rust: wasmBasePath + "tree-sitter-rust.wasm",
52007
52233
  html: wasmBasePath + "tree-sitter-html.wasm",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cognium-dev",
3
- "version": "4.9.29",
3
+ "version": "4.11.0",
4
4
  "description": "Static Application Security Testing CLI for detecting security vulnerabilities via taint tracking",
5
5
  "bin": {
6
6
  "cognium-dev": "dist/cli.js"
@@ -12,7 +12,7 @@
12
12
  "test": "bun test",
13
13
  "clean": "rm -rf dist cognium-dev cognium-dev-* *.bun-build wasm",
14
14
  "typecheck": "tsc --noEmit",
15
- "version": "node -e \"const v = require('./package.json').version; require('fs').writeFileSync('src/version.ts', '/**\\n * Version information\\n *\\n * Kept in sync with package.json via the \\`version\\` npm lifecycle script.\\n * Do not edit manually \u2014 use \\`npm version patch|minor|major\\` instead.\\n */\\nexport const version = \\x27' + v + '\\x27;\\n')\" && git add src/version.ts",
15
+ "version": "node -e \"const v = require('./package.json').version; require('fs').writeFileSync('src/version.ts', '/**\\n * Version information\\n *\\n * Kept in sync with package.json via the \\`version\\` npm lifecycle script.\\n * Do not edit manually — use \\`npm version patch|minor|major\\` instead.\\n */\\nexport const version = \\x27' + v + '\\x27;\\n')\" && git add src/version.ts",
16
16
  "dogfood": "bun run src/cli.ts scan src/ -q",
17
17
  "prepublishOnly": "bun run build",
18
18
  "test:coverage": "bun test --coverage"
@@ -65,10 +65,10 @@
65
65
  },
66
66
  "dependencies": {
67
67
  "@cognium/project-profile-detect": "1.1.2",
68
- "circle-ir": "4.9.29"
68
+ "circle-ir": "4.11.0"
69
69
  },
70
70
  "devDependencies": {
71
- "@types/node": "^26.6.2",
71
+ "@types/node": "^26.6.4",
72
72
  "ajv": "^8.17.1",
73
73
  "ajv-draft-04": "^1.0.0",
74
74
  "bun-types": "^1.4.2",