cognium-dev 4.9.25 → 4.9.26

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 +134 -17
  2. package/package.json +3 -3
package/dist/cli.js CHANGED
@@ -3349,8 +3349,9 @@ async function initParser(options = {}) {
3349
3349
  });
3350
3350
  }
3351
3351
  parserInitialized = true;
3352
+ })().finally(() => {
3352
3353
  parserInitializing = null;
3353
- })();
3354
+ });
3354
3355
  return parserInitializing;
3355
3356
  }
3356
3357
  async function loadLanguage(language, wasmPath) {
@@ -26727,6 +26728,52 @@ function findGoRequestBodySources(sourceCode, language) {
26727
26728
  continue;
26728
26729
  }
26729
26730
  }
26731
+ const wrapRe = new RegExp(`^\\s*(?:var\\s+)?([A-Za-z_]\\w*)\\s*:?=\\s*bufio\\s*\\.\\s*New(?:Scanner|Reader|ReaderSize)\\s*\\(.*${bodyRef}`);
26732
+ const braceDelta = (s) => {
26733
+ const code = s.replace(/"(?:[^"\\]|\\.)*"|`[^`]*`|'(?:[^'\\]|\\.)*'/g, '""').replace(/\/\/.*$/, "");
26734
+ let d = 0;
26735
+ for (const ch of code) {
26736
+ if (ch === "{")
26737
+ d++;
26738
+ else if (ch === "}")
26739
+ d--;
26740
+ }
26741
+ return d;
26742
+ };
26743
+ for (let i2 = 0;i2 < lines.length; i2++) {
26744
+ const w = wrapRe.exec(lines[i2]);
26745
+ if (!w)
26746
+ continue;
26747
+ const wrapper = w[1];
26748
+ if (wrapper === "_")
26749
+ continue;
26750
+ const readRe = new RegExp(`(?<![\\w.])${wrapper}\\s*\\.\\s*(Text|Bytes|ReadString|ReadBytes|ReadLine|ReadSlice)\\s*\\(`);
26751
+ const assignRe = new RegExp(`^\\s*(?:var\\s+)?([A-Za-z_][\\w\\s,]*?)\\s*:?=(?!=)\\s*(?:string\\s*\\(\\s*)?${wrapper}\\s*\\.\\s*(?:Text|Bytes|ReadString|ReadBytes|ReadLine|ReadSlice)\\s*\\(`);
26752
+ const rebindRe = new RegExp(`(?<![\\w.])${wrapper}\\s*:?=(?!=)`);
26753
+ let depth = 0;
26754
+ for (let j = i2 + 1;j < lines.length; j++) {
26755
+ depth += braceDelta(lines[j - 1]);
26756
+ if (depth < 0)
26757
+ break;
26758
+ const line = lines[j];
26759
+ if (/^\s*\/\//.test(line))
26760
+ continue;
26761
+ if (rebindRe.test(line))
26762
+ break;
26763
+ const rd = readRe.exec(line);
26764
+ if (!rd)
26765
+ continue;
26766
+ const as = assignRe.exec(line);
26767
+ let variable = "<inline>";
26768
+ if (as) {
26769
+ const bound = as[1].split(",").map((v) => v.trim()).find((v) => v !== "_" && v !== "err" && /^[A-Za-z_]\w*$/.test(v));
26770
+ if (!bound)
26771
+ continue;
26772
+ variable = bound;
26773
+ }
26774
+ push(variable, j + 1, `bufio ${wrapper}.${rd[1]}() over req.Body`);
26775
+ }
26776
+ }
26730
26777
  return sources;
26731
26778
  }
26732
26779
  function findCSharpRequestSources(sourceCode, language) {
@@ -26748,12 +26795,57 @@ function findCSharpRequestSources(sourceCode, language) {
26748
26795
  formFileParams.add(d[1]);
26749
26796
  }
26750
26797
  const formFileReadRe = formFileParams.size > 0 ? new RegExp(`\\b(?:${[...formFileParams].join("|")})\\s*\\.\\s*(?:FileName|ContentType)\\b`) : null;
26798
+ const classify = (text) => requestReadRe.test(text) ? "http_param" : formFileReadRe?.test(text) ? "http_param" : consoleReadRe.test(text) ? "io_input" : envReadRe.test(text) ? "env_input" : null;
26799
+ const inlineReads = [
26800
+ [requestReadRe, "http_param"],
26801
+ [formFileReadRe, "http_param"],
26802
+ [consoleReadRe, "io_input"],
26803
+ [envReadRe, "env_input"]
26804
+ ];
26805
+ const inlineRead = (text) => {
26806
+ const argsStart = text.indexOf("(");
26807
+ for (const [re, type] of inlineReads) {
26808
+ if (!re)
26809
+ continue;
26810
+ const g = new RegExp(re.source, "g");
26811
+ let hit;
26812
+ while ((hit = g.exec(text)) !== null) {
26813
+ if (hit.index > argsStart) {
26814
+ return { type, read: hit[0].replace(/\s+/g, "").replace(/[([]$/, "") };
26815
+ }
26816
+ }
26817
+ }
26818
+ return null;
26819
+ };
26820
+ const exprStmtCallRe = /^\s*(?:await\s+)?[A-Za-z_][\w.]*(?:<[^>]*>)?\s*\(.*\)\s*;\s*$/;
26821
+ const nonCallKeywordRe = /^\s*(?:await\s+)?(?:if|while|for|foreach|switch|using|lock|return|throw|catch|when|nameof|typeof|sizeof|default)\b/;
26751
26822
  for (let i2 = 0;i2 < lines.length; i2++) {
26752
26823
  const m = assignRe.exec(lines[i2]);
26753
- if (!m)
26824
+ if (!m) {
26825
+ const stmt = lines[i2];
26826
+ if (!exprStmtCallRe.test(stmt) || nonCallKeywordRe.test(stmt))
26827
+ continue;
26828
+ const hit = inlineRead(stmt);
26829
+ if (!hit)
26830
+ continue;
26831
+ const { type: type2, read } = hit;
26832
+ const lineNumber2 = i2 + 1;
26833
+ if (sources.some((s) => s.line === lineNumber2))
26834
+ continue;
26835
+ const code = stmt.trim();
26836
+ sources.push({
26837
+ type: type2,
26838
+ location: `${read} in ${code.substring(0, 50)}${code.length > 50 ? "..." : ""}`,
26839
+ severity: "high",
26840
+ line: lineNumber2,
26841
+ confidence: 1,
26842
+ variable: "<inline>",
26843
+ code
26844
+ });
26754
26845
  continue;
26846
+ }
26755
26847
  const [, varName, rhs] = m;
26756
- const type = requestReadRe.test(rhs) ? "http_param" : formFileReadRe?.test(rhs) ? "http_param" : consoleReadRe.test(rhs) ? "io_input" : envReadRe.test(rhs) ? "env_input" : null;
26848
+ const type = classify(rhs);
26757
26849
  if (!type)
26758
26850
  continue;
26759
26851
  const lineNumber = i2 + 1;
@@ -37425,6 +37517,18 @@ function maskStringLiteralsForTokenScan(expr) {
37425
37517
  }
37426
37518
  function detectExpressionScanFlows(calls, sources, sinks, sanitizers, unreachableLines, tainted, code, language, types) {
37427
37519
  const flows = [];
37520
+ const goFnRanges = language === "go" && types ? types.flatMap((t) => t.methods.map((m) => [m.start_line, m.end_line])) : [];
37521
+ const goFnStart = (line) => {
37522
+ let best = -1;
37523
+ let bestSpan = -1;
37524
+ for (const [start2, end] of goFnRanges) {
37525
+ if (line >= start2 && line <= end && end - start2 > bestSpan) {
37526
+ best = start2;
37527
+ bestSpan = end - start2;
37528
+ }
37529
+ }
37530
+ return best;
37531
+ };
37428
37532
  const sourcesWithVar = sources.filter((s) => typeof s.variable === "string" && s.variable.length > 0);
37429
37533
  const aliasSanitizedFor = new Map;
37430
37534
  if (language === "python" && typeof code === "string") {
@@ -37833,6 +37937,12 @@ function detectExpressionScanFlows(calls, sources, sinks, sanitizers, unreachabl
37833
37937
  if (source.in_method && call.in_method && source.in_method !== call.in_method) {
37834
37938
  continue;
37835
37939
  }
37940
+ if (goFnRanges.length > 0) {
37941
+ const srcFn = goFnStart(source.line);
37942
+ const sinkFn = goFnStart(sink.line);
37943
+ if (srcFn !== -1 && sinkFn !== -1 && srcFn !== sinkFn)
37944
+ continue;
37945
+ }
37836
37946
  if (complex) {
37837
37947
  const re = reCache.get(source.variable);
37838
37948
  if (!re || !re.test(expr))
@@ -49945,18 +50055,24 @@ async function enumerate(dir, out2) {
49945
50055
  }
49946
50056
  }
49947
50057
  }
49948
- function ownerOf(file, modules) {
49949
- let best;
49950
- let bestLen = -1;
50058
+ function createOwnerIndex(modules) {
50059
+ const byRoot = new Map;
49951
50060
  for (const m of modules) {
49952
- if (file === m.root || file.startsWith(m.root + "/")) {
49953
- if (m.root.length > bestLen) {
49954
- best = m;
49955
- bestLen = m.root.length;
49956
- }
50061
+ if (!byRoot.has(m.root))
50062
+ byRoot.set(m.root, m);
50063
+ }
50064
+ return (file) => {
50065
+ let p = file;
50066
+ for (;; ) {
50067
+ const hit = byRoot.get(p);
50068
+ if (hit)
50069
+ return hit;
50070
+ const slash = p.lastIndexOf("/");
50071
+ if (slash <= 0)
50072
+ return;
50073
+ p = p.slice(0, slash);
49957
50074
  }
49958
- }
49959
- return best;
50075
+ };
49960
50076
  }
49961
50077
 
49962
50078
  // ../project-profile-detect/dist/publication-detect.js
@@ -50115,6 +50231,7 @@ async function detectProjectProfiles(scanRoot, options = {}) {
50115
50231
  }
50116
50232
  const profileByFile = new Map;
50117
50233
  const unknownFiles = [];
50234
+ const ownerIndex = createOwnerIndex(modules);
50118
50235
  for (const file of files) {
50119
50236
  const rel = relative3(scanRoot, file);
50120
50237
  const ov = applyOverrides(rel, compiledOverrides);
@@ -50128,7 +50245,7 @@ async function detectProjectProfiles(scanRoot, options = {}) {
50128
50245
  profileByFile.set(file, `${shape2}/${env2}`);
50129
50246
  continue;
50130
50247
  }
50131
- const owner = ownerOf(file, modules);
50248
+ const owner = ownerIndex(file);
50132
50249
  if (!owner) {
50133
50250
  profileByFile.set(file, "unknown");
50134
50251
  unknownFiles.push(file);
@@ -50179,7 +50296,7 @@ var colors = {
50179
50296
  };
50180
50297
 
50181
50298
  // src/version.ts
50182
- var version = "4.9.25";
50299
+ var version = "4.9.26";
50183
50300
 
50184
50301
  // src/formatters.ts
50185
50302
  var LIBRARY_API_SURFACE_TAG2 = "library-api-surface:caller-responsibility";
@@ -50724,8 +50841,8 @@ function generateSarifResults(results, crossFileData) {
50724
50841
 
50725
50842
  // src/build-info.ts
50726
50843
  var buildInfo = {
50727
- gitSha: "2467260",
50728
- builtAt: "2026-09-24T06:46:18.058Z"
50844
+ gitSha: "178c681",
50845
+ builtAt: "2026-09-24T18:11:45.249Z"
50729
50846
  };
50730
50847
 
50731
50848
  // src/utils/args.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cognium-dev",
3
- "version": "4.9.25",
3
+ "version": "4.9.26",
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"
@@ -64,8 +64,8 @@
64
64
  "registry": "https://registry.npmjs.org/"
65
65
  },
66
66
  "dependencies": {
67
- "@cognium/project-profile-detect": "1.1.1",
68
- "circle-ir": "4.9.25"
67
+ "@cognium/project-profile-detect": "1.1.2",
68
+ "circle-ir": "4.9.26"
69
69
  },
70
70
  "devDependencies": {
71
71
  "@types/node": "^26.6.1",