cognium-dev 4.9.7 → 4.9.9

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 +138 -39
  2. package/package.json +6 -6
package/dist/cli.js CHANGED
@@ -13764,17 +13764,27 @@ function isSafeCSharpProcessStartCall(call, pattern, language, sourceLines) {
13764
13764
  function stripCsLiterals(line) {
13765
13765
  return line.replace(/\/\/.*$/, "").replace(/@?"(?:[^"\\]|\\.)*"/g, '""').replace(/'(?:[^'\\]|\\.)'/g, "''");
13766
13766
  }
13767
+ var _csDepthLines;
13768
+ var _csDepthPrefix;
13767
13769
  function csBraceDepthBefore(lines, idx) {
13768
- let depth = 0;
13769
- for (let i2 = 0;i2 < idx && i2 < lines.length; i2++) {
13770
- for (const ch of stripCsLiterals(lines[i2])) {
13771
- if (ch === "{")
13772
- depth++;
13773
- else if (ch === "}")
13774
- depth--;
13770
+ if (lines !== _csDepthLines) {
13771
+ const pref = new Int32Array(lines.length + 1);
13772
+ let depth = 0;
13773
+ for (let i2 = 0;i2 < lines.length; i2++) {
13774
+ pref[i2] = depth;
13775
+ for (const ch of stripCsLiterals(lines[i2])) {
13776
+ if (ch === "{")
13777
+ depth++;
13778
+ else if (ch === "}")
13779
+ depth--;
13780
+ }
13775
13781
  }
13782
+ pref[lines.length] = depth;
13783
+ _csDepthLines = lines;
13784
+ _csDepthPrefix = pref;
13776
13785
  }
13777
- return depth;
13786
+ const clamped = idx < 0 ? 0 : idx > lines.length ? lines.length : idx;
13787
+ return _csDepthPrefix[clamped];
13778
13788
  }
13779
13789
  function netBraces(fragment) {
13780
13790
  let n = 0;
@@ -13885,6 +13895,33 @@ function csThenBlock(lines, ifIdx, rest) {
13885
13895
  }
13886
13896
  return { start: ifIdx, end: firstIdx, earlyExit: isExit(firstText.trim()) };
13887
13897
  }
13898
+ var _csGuardLines;
13899
+ var _csGuardMap;
13900
+ function csEqualityGuardIndex(lines) {
13901
+ if (lines === _csGuardLines && _csGuardMap)
13902
+ return _csGuardMap;
13903
+ const map = new Map;
13904
+ for (let i2 = 0;i2 < lines.length; i2++) {
13905
+ const parts2 = splitCsIf(lines[i2]);
13906
+ if (!parts2)
13907
+ continue;
13908
+ const guard = csEqualityGuard(parts2.cond);
13909
+ if (!guard)
13910
+ continue;
13911
+ const entry = { i: i2, rest: parts2.rest, ifCol: parts2.ifCol, op: guard.op };
13912
+ for (const id of csIdentifiers(guard.exprSide)) {
13913
+ let arr = map.get(id);
13914
+ if (!arr) {
13915
+ arr = [];
13916
+ map.set(id, arr);
13917
+ }
13918
+ arr.push(entry);
13919
+ }
13920
+ }
13921
+ _csGuardLines = lines;
13922
+ _csGuardMap = map;
13923
+ return map;
13924
+ }
13888
13925
  function isCSharpSsrfHostAllowlistGuarded(call, pattern, language, sourceLines) {
13889
13926
  if (language !== "csharp")
13890
13927
  return false;
@@ -13903,24 +13940,25 @@ function isCSharpSsrfHostAllowlistGuarded(call, pattern, language, sourceLines)
13903
13940
  return false;
13904
13941
  const sinkIdx = call.location.line - 1;
13905
13942
  const sinkDepth = csBraceDepthBefore(sourceLines, sinkIdx);
13906
- for (let i2 = 0;i2 < sinkIdx; i2++) {
13907
- const parts2 = splitCsIf(sourceLines[i2]);
13908
- if (!parts2)
13909
- continue;
13910
- const guard = csEqualityGuard(parts2.cond);
13911
- if (!guard)
13912
- continue;
13913
- const guardIds = csIdentifiers(guard.exprSide);
13914
- if (![...guardIds].some((id) => candidates.has(id)))
13915
- continue;
13916
- const block = csThenBlock(sourceLines, i2, parts2.rest);
13917
- if (guard.op === "==") {
13918
- if (sinkIdx >= block.start && sinkIdx <= block.end)
13919
- return true;
13920
- } else {
13921
- const guardDepth = csBraceDepthBefore(sourceLines, i2) + netBraces(stripCsLiterals(sourceLines[i2]).slice(0, parts2.ifCol));
13922
- if (block.earlyExit && sinkIdx > block.end && guardDepth === sinkDepth) {
13923
- return true;
13943
+ const guardMap = csEqualityGuardIndex(sourceLines);
13944
+ const checked = new Set;
13945
+ for (const cand of candidates) {
13946
+ const guards = guardMap.get(cand);
13947
+ if (!guards)
13948
+ continue;
13949
+ for (const g of guards) {
13950
+ if (g.i >= sinkIdx || checked.has(g))
13951
+ continue;
13952
+ checked.add(g);
13953
+ const block = csThenBlock(sourceLines, g.i, g.rest);
13954
+ if (g.op === "==") {
13955
+ if (sinkIdx >= block.start && sinkIdx <= block.end)
13956
+ return true;
13957
+ } else {
13958
+ const guardDepth = csBraceDepthBefore(sourceLines, g.i) + netBraces(stripCsLiterals(sourceLines[g.i]).slice(0, g.ifCol));
13959
+ if (block.earlyExit && sinkIdx > block.end && guardDepth === sinkDepth) {
13960
+ return true;
13961
+ }
13924
13962
  }
13925
13963
  }
13926
13964
  }
@@ -26939,6 +26977,13 @@ function buildJavaTaintedVars(sourceCode, seedVars) {
26939
26977
  "interface",
26940
26978
  "enum"
26941
26979
  ]);
26980
+ const complexTaintedRes = [];
26981
+ for (const v of knownTainted) {
26982
+ if (!/^[\p{L}\p{N}_]+$/u.test(v)) {
26983
+ const esc = v.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
26984
+ complexTaintedRes.push(new RegExp(`(?<![\\p{L}\\p{N}_])${esc}(?![\\p{L}\\p{N}_])`, "u"));
26985
+ }
26986
+ }
26942
26987
  let changed = true;
26943
26988
  let guard = 0;
26944
26989
  while (changed && guard < lines.length + 2) {
@@ -26960,9 +27005,9 @@ function buildJavaTaintedVars(sourceCode, seedVars) {
26960
27005
  continue;
26961
27006
  if (knownTainted.has(lhs))
26962
27007
  continue;
26963
- const escaped = (v) => v.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
26964
27008
  const rhsCode = rhs.replace(/\$@?"(?:[^"\\]|\\.)*"/g, (lit) => " " + [...lit.matchAll(/\{([^}]*)\}/g)].map((x) => x[1]).join(" ") + " ").replace(/@?"(?:[^"\\]|\\.)*"/g, " ").replace(/'(?:[^'\\]|\\.)'/g, " ");
26965
- const ref = [...knownTainted].some((v) => new RegExp(`(?<![\\p{L}\\p{N}_])${escaped(v)}(?![\\p{L}\\p{N}_])`, "u").test(rhsCode));
27009
+ const rhsIdents = rhsCode.match(/[\p{L}\p{N}_]+/gu);
27010
+ const ref = rhsIdents !== null && rhsIdents.some((t) => knownTainted.has(t)) || complexTaintedRes.length > 0 && complexTaintedRes.some((re) => re.test(rhsCode));
26966
27011
  if (ref) {
26967
27012
  derived.set(lhs, i2 + 1);
26968
27013
  knownTainted.add(lhs);
@@ -36283,13 +36328,22 @@ function detectParameterSinkFlows(types, calls, sources, sinks, unreachableLines
36283
36328
  }
36284
36329
  return flows;
36285
36330
  }
36331
+ var _reassignSplitCode;
36332
+ var _reassignSplitLines;
36333
+ function splitLinesCached(code) {
36334
+ if (code === _reassignSplitCode && _reassignSplitLines)
36335
+ return _reassignSplitLines;
36336
+ _reassignSplitCode = code;
36337
+ _reassignSplitLines = code.split(`
36338
+ `);
36339
+ return _reassignSplitLines;
36340
+ }
36286
36341
  function isReassignedToLiteralBetween(code, variable, srcLine, sinkLine) {
36287
36342
  if (!variable || sinkLine - srcLine < 2)
36288
36343
  return false;
36289
36344
  if (!/^[A-Za-z_][\w]*$/.test(variable))
36290
36345
  return false;
36291
- const lines = code.split(`
36292
- `);
36346
+ const lines = splitLinesCached(code);
36293
36347
  const lo = Math.max(0, srcLine);
36294
36348
  const hi = Math.min(lines.length, sinkLine - 1);
36295
36349
  const strLit = `(?:"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"|'[^'\\\\]*(?:\\\\.[^'\\\\]*)*'|\`[^\`\\\\]*(?:\\\\.[^\`\\\\]*)*\`)`;
@@ -36627,9 +36681,10 @@ function detectExpressionScanFlows(calls, sources, sinks, sanitizers, unreachabl
36627
36681
  }
36628
36682
  }
36629
36683
  }
36684
+ const SIMPLE_IDENT = /^[\p{L}\p{N}_]+$/u;
36630
36685
  const reCache = new Map;
36631
36686
  for (const s of sourcesWithVar) {
36632
- if (reCache.has(s.variable))
36687
+ if (SIMPLE_IDENT.test(s.variable) || reCache.has(s.variable))
36633
36688
  continue;
36634
36689
  const escaped = s.variable.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
36635
36690
  reCache.set(s.variable, new RegExp(`(?<![\\p{L}\\p{N}_])${escaped}(?![\\p{L}\\p{N}_])`, "u"));
@@ -36640,6 +36695,23 @@ function detectExpressionScanFlows(calls, sources, sinks, sanitizers, unreachabl
36640
36695
  existing.push(call);
36641
36696
  callsByLine.set(call.location.line, existing);
36642
36697
  }
36698
+ const sourcesByVar = new Map;
36699
+ const complexSources = [];
36700
+ sourcesWithVar.forEach((source, ord) => {
36701
+ const complex = !SIMPLE_IDENT.test(source.variable);
36702
+ const entry = { source, ord, complex };
36703
+ if (!complex) {
36704
+ let list = sourcesByVar.get(source.variable);
36705
+ if (!list) {
36706
+ list = [];
36707
+ sourcesByVar.set(source.variable, list);
36708
+ }
36709
+ list.push(entry);
36710
+ } else {
36711
+ complexSources.push(entry);
36712
+ }
36713
+ });
36714
+ const flowKeys = new Set;
36643
36715
  for (const sink of sinks) {
36644
36716
  if (unreachableLines.has(sink.line))
36645
36717
  continue;
@@ -36652,18 +36724,35 @@ function detectExpressionScanFlows(calls, sources, sinks, sanitizers, unreachabl
36652
36724
  const expr = arg.expression;
36653
36725
  if (!expr)
36654
36726
  continue;
36655
- for (const source of sourcesWithVar) {
36727
+ const exprIdentMatches = expr.match(/[\p{L}\p{N}_]+/gu);
36728
+ let candidates = complexSources.length > 0 ? complexSources.slice() : null;
36729
+ if (exprIdentMatches) {
36730
+ for (const id of new Set(exprIdentMatches)) {
36731
+ const list = sourcesByVar.get(id);
36732
+ if (!list)
36733
+ continue;
36734
+ (candidates ??= []).push(...list);
36735
+ }
36736
+ }
36737
+ if (!candidates)
36738
+ continue;
36739
+ if (candidates.length > 1)
36740
+ candidates.sort((a, b) => a.ord - b.ord);
36741
+ for (const { source, complex } of candidates) {
36656
36742
  if (source.line >= sink.line)
36657
36743
  continue;
36658
36744
  if (source.in_method && call.in_method && source.in_method !== call.in_method) {
36659
36745
  continue;
36660
36746
  }
36661
- const re = reCache.get(source.variable);
36662
- if (!re || !re.test(expr))
36663
- continue;
36747
+ if (complex) {
36748
+ const re = reCache.get(source.variable);
36749
+ if (!re || !re.test(expr))
36750
+ continue;
36751
+ }
36664
36752
  if (!sourceSemanticsAllowed(source, sink.type))
36665
36753
  continue;
36666
- if (flows.some((f) => f.source_line === source.line && f.sink_line === sink.line && f.sink_type === sink.type))
36754
+ const dedupKey = `${source.line}:${sink.line}:${sink.type}`;
36755
+ if (flowKeys.has(dedupKey))
36667
36756
  continue;
36668
36757
  if (aliasSanitizedFor.get(source.variable)?.has(sink.type)) {
36669
36758
  break;
@@ -36683,6 +36772,7 @@ function detectExpressionScanFlows(calls, sources, sinks, sanitizers, unreachabl
36683
36772
  confidence: source.confidence * sink.confidence * 0.7,
36684
36773
  sanitized: false
36685
36774
  });
36775
+ flowKeys.add(dedupKey);
36686
36776
  break;
36687
36777
  }
36688
36778
  }
@@ -48731,7 +48821,7 @@ var colors = {
48731
48821
  };
48732
48822
 
48733
48823
  // src/version.ts
48734
- var version = "4.9.7";
48824
+ var version = "4.9.9";
48735
48825
 
48736
48826
  // src/formatters.ts
48737
48827
  var LIBRARY_API_SURFACE_TAG2 = "library-api-surface:caller-responsibility";
@@ -49282,6 +49372,12 @@ function generateSarifResults(results, crossFileData) {
49282
49372
  return sarifResults;
49283
49373
  }
49284
49374
 
49375
+ // src/build-info.ts
49376
+ var buildInfo = {
49377
+ gitSha: "5c476ca",
49378
+ builtAt: "2026-08-26T20:08:56.375Z"
49379
+ };
49380
+
49285
49381
  // src/utils/args.ts
49286
49382
  function parseArgs(argv) {
49287
49383
  const args2 = [];
@@ -49422,8 +49518,11 @@ EXAMPLES:
49422
49518
  For more information, visit: https://cognium.dev
49423
49519
  `);
49424
49520
  }
49425
- function showVersion(version2) {
49521
+ function showVersion(version2, buildInfo2) {
49426
49522
  console.log(`cognium-dev v${version2}`);
49523
+ if (buildInfo2?.builtAt) {
49524
+ console.log(`build ${buildInfo2.gitSha} · ${buildInfo2.builtAt}`);
49525
+ }
49427
49526
  console.log(`Powered by Cognium Labs`);
49428
49527
  }
49429
49528
 
@@ -50557,7 +50656,7 @@ async function main() {
50557
50656
  return;
50558
50657
  }
50559
50658
  if (command === "version" || options.version || options.V) {
50560
- showVersion(version);
50659
+ showVersion(version, buildInfo);
50561
50660
  return;
50562
50661
  }
50563
50662
  if (command === "init") {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cognium-dev",
3
- "version": "4.9.7",
3
+ "version": "4.9.9",
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",
@@ -8,13 +8,13 @@
8
8
  "cognium-dev": "dist/cli.js"
9
9
  },
10
10
  "scripts": {
11
- "build": "bun build src/cli.ts --outdir dist --target node --format esm --external pino-pretty",
12
- "build:standalone": "mkdir -p wasm && cp ../circle-ir/wasm/*.wasm wasm/ && bun build src/cli.ts --compile --outfile cognium-dev --external pino-pretty",
11
+ "build": "node scripts/build.mjs",
12
+ "build:standalone": "mkdir -p wasm && cp ../circle-ir/wasm/*.wasm wasm/ && node scripts/build.mjs --standalone",
13
13
  "dev": "bun run src/cli.ts",
14
14
  "test": "bun test",
15
15
  "clean": "rm -rf dist cognium-dev cognium-dev-* *.bun-build wasm",
16
16
  "typecheck": "tsc --noEmit",
17
- "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",
17
+ "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",
18
18
  "dogfood": "bun run src/cli.ts scan src/ -q",
19
19
  "prepublishOnly": "bun run build"
20
20
  },
@@ -65,8 +65,8 @@
65
65
  "registry": "https://registry.npmjs.org/"
66
66
  },
67
67
  "dependencies": {
68
- "@cognium/project-profile-detect": "^1.1.0",
69
- "circle-ir": "^4.9.7"
68
+ "@cognium/project-profile-detect": "1.1.0",
69
+ "circle-ir": "4.9.9"
70
70
  },
71
71
  "devDependencies": {
72
72
  "@types/node": "^25.5.0",