cognium-dev 4.10.0 → 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 +141 -4
  2. package/package.json +3 -3
package/dist/cli.js CHANGED
@@ -27299,6 +27299,27 @@ function findPythonAssignmentSources(sourceCode, language) {
27299
27299
  const lineNumber = lineNum + 1;
27300
27300
  if (line.trimStart().startsWith("#"))
27301
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
+ }
27302
27323
  const assignmentMatch = line.match(/^(\s*\w[\w.]*)\s*(?::\s*\w[\w\[\], .]*)?\s*=\s*(.+)/);
27303
27324
  if (!assignmentMatch)
27304
27325
  continue;
@@ -36572,6 +36593,103 @@ class SpeculativePromptParamSourcePass {
36572
36593
  }
36573
36594
  }
36574
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
+
36575
36693
  // ../circle-ir/dist/analysis/passes/cli-main-reflection-suppress-pass.js
36576
36694
  var REFLECTION_SINK_METHODS = new Set([
36577
36695
  "forName",
@@ -43827,6 +43945,13 @@ var PROVIDER_PATTERNS = [
43827
43945
  level: "error",
43828
43946
  fix: "Revoke the GitHub refresh token and store secrets outside source control."
43829
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
+ },
43830
43955
  {
43831
43956
  name: "Stripe live secret key",
43832
43957
  regex: /\bsk_live_[A-Za-z0-9]{24,}\b/,
@@ -43834,6 +43959,13 @@ var PROVIDER_PATTERNS = [
43834
43959
  level: "error",
43835
43960
  fix: "Rotate the Stripe secret key in the Stripe Dashboard and load it from a secrets manager."
43836
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
+ },
43837
43969
  {
43838
43970
  name: "Stripe live publishable key",
43839
43971
  regex: /\bpk_live_[A-Za-z0-9]{24,}\b/,
@@ -44185,6 +44317,9 @@ class ScanSecretsPass {
44185
44317
  const m = pattern.regex.exec(lineText);
44186
44318
  if (!m)
44187
44319
  continue;
44320
+ if ((pattern.name === "Stripe live restricted key" || pattern.name === "GitLab personal access token") && PLACEHOLDER_RE.test(m[0])) {
44321
+ continue;
44322
+ }
44188
44323
  if (pattern.name === "PEM private key" && !pemHasInlineBody(lines, i2)) {
44189
44324
  continue;
44190
44325
  }
@@ -49424,6 +49559,8 @@ async function analyze(code, filePath, language, options = {}) {
49424
49559
  pipeline.add(new InterproceduralPass({
49425
49560
  enableEntryPointGate: options.enableEntryPointGate ?? true
49426
49561
  }));
49562
+ if (!disabledPasses.has("param-source-flow-gate"))
49563
+ pipeline.add(new ParamSourceFlowGatePass(options.speculativeParamSources === true));
49427
49564
  if (!disabledPasses.has("library-profile-cwe22-path-gate"))
49428
49565
  pipeline.add(new LibraryProfileCwe22PathGatePass);
49429
49566
  if (!disabledPasses.has("scan-secrets"))
@@ -50874,7 +51011,7 @@ var colors = {
50874
51011
  };
50875
51012
 
50876
51013
  // src/version.ts
50877
- var version = "4.10.0";
51014
+ var version = "4.11.0";
50878
51015
 
50879
51016
  // src/formatters.ts
50880
51017
  var LIBRARY_API_SURFACE_TAG2 = "library-api-surface:caller-responsibility";
@@ -51426,8 +51563,8 @@ function generateSarifResults(results, crossFileData) {
51426
51563
 
51427
51564
  // src/build-info.ts
51428
51565
  var buildInfo = {
51429
- gitSha: "598f8fc",
51430
- builtAt: "2026-10-02T23:23:28.653Z"
51566
+ gitSha: "165e82d",
51567
+ builtAt: "2026-10-07T23:49:13.604Z"
51431
51568
  };
51432
51569
 
51433
51570
  // src/utils/args.ts
@@ -51678,7 +51815,7 @@ function loadConfig(profilePath) {
51678
51815
  const content = readFileSync(configPath, "utf-8");
51679
51816
  return JSON.parse(content);
51680
51817
  } catch (err2) {
51681
- 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 });
51682
51819
  }
51683
51820
  }
51684
51821
  function convertConfigToPassOptions(config) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cognium-dev",
3
- "version": "4.10.0",
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"
@@ -65,10 +65,10 @@
65
65
  },
66
66
  "dependencies": {
67
67
  "@cognium/project-profile-detect": "1.1.2",
68
- "circle-ir": "4.10.0"
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",