cognium-dev 4.9.24 → 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 +216 -37
  2. package/package.json +4 -6
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);
@@ -50156,19 +50273,30 @@ var YELLOW = "\x1B[93m";
50156
50273
  var BLUE = "\x1B[94m";
50157
50274
  var CYAN = "\x1B[96m";
50158
50275
  var MAGENTA = "\x1B[95m";
50276
+ function colorEnabled() {
50277
+ const noColor = process.env.NO_COLOR;
50278
+ if (noColor !== undefined && noColor !== "")
50279
+ return false;
50280
+ if (process.env.TERM === "dumb")
50281
+ return false;
50282
+ return true;
50283
+ }
50284
+ function paint(code, text) {
50285
+ return colorEnabled() ? `${code}${text}${RESET}` : text;
50286
+ }
50159
50287
  var colors = {
50160
- red: (text) => `${RED}${text}${RESET}`,
50161
- green: (text) => `${GREEN}${text}${RESET}`,
50162
- yellow: (text) => `${YELLOW}${text}${RESET}`,
50163
- blue: (text) => `${BLUE}${text}${RESET}`,
50164
- cyan: (text) => `${CYAN}${text}${RESET}`,
50165
- magenta: (text) => `${MAGENTA}${text}${RESET}`,
50166
- bold: (text) => `${BOLD}${text}${RESET}`,
50288
+ red: (text) => paint(RED, text),
50289
+ green: (text) => paint(GREEN, text),
50290
+ yellow: (text) => paint(YELLOW, text),
50291
+ blue: (text) => paint(BLUE, text),
50292
+ cyan: (text) => paint(CYAN, text),
50293
+ magenta: (text) => paint(MAGENTA, text),
50294
+ bold: (text) => paint(BOLD, text),
50167
50295
  dim: (text) => text
50168
50296
  };
50169
50297
 
50170
50298
  // src/version.ts
50171
- var version = "4.9.24";
50299
+ var version = "4.9.26";
50172
50300
 
50173
50301
  // src/formatters.ts
50174
50302
  var LIBRARY_API_SURFACE_TAG2 = "library-api-surface:caller-responsibility";
@@ -50713,8 +50841,8 @@ function generateSarifResults(results, crossFileData) {
50713
50841
 
50714
50842
  // src/build-info.ts
50715
50843
  var buildInfo = {
50716
- gitSha: "527cda0",
50717
- builtAt: "2026-09-23T02:30:52.145Z"
50844
+ gitSha: "178c681",
50845
+ builtAt: "2026-09-24T18:11:45.249Z"
50718
50846
  };
50719
50847
 
50720
50848
  // src/utils/args.ts
@@ -50773,7 +50901,7 @@ COMMANDS:
50773
50901
  version Display version information
50774
50902
 
50775
50903
  SCAN OPTIONS:
50776
- -l, --language <lang> Scan only files for language (bash|go|html|java|javascript|typescript|python|rust)
50904
+ -l, --language <lang> Scan only files for language (bash|csharp|go|html|java|javascript|typescript|python|rust)
50777
50905
  -f, --format <format> Output format (text|json|sarif) [default: text]
50778
50906
  --threads <n> Parallel analysis threads [default: 4]
50779
50907
  --severity <level> Filter by severity:
@@ -50806,7 +50934,7 @@ SCAN OPTIONS:
50806
50934
  surfaced in output (text warning / JSON / SARIF field).
50807
50935
 
50808
50936
  METRICS OPTIONS:
50809
- -l, --language <lang> Analyze only files for language (bash|go|html|java|javascript|typescript|python|rust)
50937
+ -l, --language <lang> Analyze only files for language (bash|csharp|go|html|java|javascript|typescript|python|rust)
50810
50938
  -f, --format <format> Output format (text|json) [default: text]
50811
50939
  --category <cats> Filter by metric category (comma-separated):
50812
50940
  - Valid categories: complexity, size, coupling,
@@ -50915,19 +51043,19 @@ class Spinner {
50915
51043
  succeed(text) {
50916
51044
  this.stop();
50917
51045
  const message = text || this._text;
50918
- console.error(`\x1B[32m${CHECKMARK}\x1B[0m ${message}`);
51046
+ console.error(`${colors.green(CHECKMARK)} ${message}`);
50919
51047
  return this;
50920
51048
  }
50921
51049
  fail(text) {
50922
51050
  this.stop();
50923
51051
  const message = text || this._text;
50924
- console.error(`\x1B[31m${CROSS}\x1B[0m ${message}`);
51052
+ console.error(`${colors.red(CROSS)} ${message}`);
50925
51053
  return this;
50926
51054
  }
50927
51055
  warn(text) {
50928
51056
  this.stop();
50929
51057
  const message = text || this._text;
50930
- console.error(`\x1B[33m${WARNING}\x1B[0m ${message}`);
51058
+ console.error(`${colors.yellow(WARNING)} ${message}`);
50931
51059
  return this;
50932
51060
  }
50933
51061
  set text(value) {
@@ -50954,8 +51082,7 @@ function loadConfig(profilePath) {
50954
51082
  const content = readFileSync(configPath, "utf-8");
50955
51083
  return JSON.parse(content);
50956
51084
  } catch (err2) {
50957
- console.error(colors.yellow(`Warning: Failed to parse ${configPath}: ${err2}`));
50958
- return null;
51085
+ throw new Error(`Failed to parse ${configPath}: ${err2 instanceof Error ? err2.message : String(err2)}. ` + "Fix the JSON or remove the file.");
50959
51086
  }
50960
51087
  }
50961
51088
  function convertConfigToPassOptions(config) {
@@ -51021,6 +51148,27 @@ function applySuppressionsToResults(results, suppressions, basePath) {
51021
51148
  return { ...result, vulnerabilities: filteredVulns };
51022
51149
  });
51023
51150
  }
51151
+ function applySuppressionsToTaintPaths(paths, suppressions, basePath) {
51152
+ if (suppressions.length === 0)
51153
+ return paths;
51154
+ return paths.filter((path) => {
51155
+ for (const supp of suppressions) {
51156
+ const sinkType = path.sink.type;
51157
+ if (supp.pass !== sinkType && supp.pass !== `cross-file-${sinkType}`)
51158
+ continue;
51159
+ if (supp.file) {
51160
+ const suppFile = supp.file.replace(/^\.\//, "");
51161
+ const relativeFile = relative4(basePath, path.sink.file) || path.sink.file;
51162
+ if (suppFile !== relativeFile && suppFile !== path.sink.file)
51163
+ continue;
51164
+ }
51165
+ if (supp.line !== undefined && supp.line !== path.sink.line)
51166
+ continue;
51167
+ return false;
51168
+ }
51169
+ return true;
51170
+ });
51171
+ }
51024
51172
  var TEST_PATTERNS = [
51025
51173
  /[\/\\]test[\/\\]/i,
51026
51174
  /[\/\\]tests[\/\\]/i,
@@ -51078,9 +51226,30 @@ function detectLanguage7(filePath) {
51078
51226
  const ext = extname(filePath).toLowerCase();
51079
51227
  return LANG_MAP[ext] || null;
51080
51228
  }
51229
+ var SUPPORTED_SCAN_LANGUAGES = [
51230
+ ...new Set(Object.values(LANG_MAP))
51231
+ ].sort();
51081
51232
  function normalizeLanguage(language) {
51082
51233
  return language.trim().toLowerCase();
51083
51234
  }
51235
+ function resolveScanLanguage(raw) {
51236
+ if (raw === undefined || raw === "")
51237
+ return;
51238
+ const normalized = normalizeLanguage(String(raw));
51239
+ if (!SUPPORTED_SCAN_LANGUAGES.includes(normalized)) {
51240
+ throw new Error(`Invalid language: ${normalized}
51241
+ Valid options: ${SUPPORTED_SCAN_LANGUAGES.join(", ")}`);
51242
+ }
51243
+ return normalized;
51244
+ }
51245
+ function languageFromCliOptions(options) {
51246
+ try {
51247
+ return resolveScanLanguage(options.language || options.l);
51248
+ } catch (err2) {
51249
+ console.error(colors.red(`Error: ${err2.message}`));
51250
+ process.exit(1);
51251
+ }
51252
+ }
51084
51253
  function fileMatchesLanguage(filePath, language) {
51085
51254
  const detected = detectLanguage7(filePath);
51086
51255
  if (!detected)
@@ -51091,9 +51260,13 @@ function fileMatchesLanguage(filePath, language) {
51091
51260
  }
51092
51261
  function matchesGlob(filePath, pattern) {
51093
51262
  const normalizedPath = filePath.replace(/\\/g, "/");
51094
- const normalizedPattern = pattern.replace(/\\/g, "/");
51263
+ let normalizedPattern = pattern.replace(/\\/g, "/");
51264
+ normalizedPattern = normalizedPattern.replace(/\*{3,}/g, "**").replace(/(?:\*\*\/)+/g, "**/").replace(/(?:\/\*\*)+/g, "/**");
51265
+ const globstarCount = (normalizedPattern.match(/\*\*/g) ?? []).length;
51266
+ if (globstarCount > 4)
51267
+ return false;
51095
51268
  let regex = normalizedPattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*\*/g, "{{GLOBSTAR}}").replace(/\*/g, "[^/]*").replace(/\?/g, ".");
51096
- regex = regex.replace(/\{\{GLOBSTAR\}\}\//g, "(?:.+/)?").replace(/\/\{\{GLOBSTAR\}\}/g, "(?:/.*)?").replace(/\{\{GLOBSTAR\}\}/g, ".*");
51269
+ regex = regex.replace(/\{\{GLOBSTAR\}\}\//g, "(?:[^/]+/)*").replace(/\/\{\{GLOBSTAR\}\}/g, "(?:/[^/]+)*").replace(/\{\{GLOBSTAR\}\}/g, "[^]*");
51097
51270
  return new RegExp("^" + regex + "$").test(normalizedPath);
51098
51271
  }
51099
51272
  function matchesAnyPattern(filePath, patterns) {
@@ -51484,9 +51657,12 @@ async function runScan(targetPath, options) {
51484
51657
  if (spin)
51485
51658
  spin.succeed(`Scanned ${files.length} file(s)`);
51486
51659
  if (suppressions.length > 0) {
51487
- const beforeCount = results.reduce((sum, r) => sum + r.vulnerabilities.length, 0);
51660
+ const beforeCount = results.reduce((sum, r) => sum + r.vulnerabilities.length, 0) + (crossFileData?.taintPaths.length ?? 0);
51488
51661
  results = applySuppressionsToResults(results, suppressions, process.cwd());
51489
- const afterCount = results.reduce((sum, r) => sum + r.vulnerabilities.length, 0);
51662
+ if (crossFileData) {
51663
+ crossFileData.taintPaths = applySuppressionsToTaintPaths(crossFileData.taintPaths, suppressions, process.cwd());
51664
+ }
51665
+ const afterCount = results.reduce((sum, r) => sum + r.vulnerabilities.length, 0) + (crossFileData?.taintPaths.length ?? 0);
51490
51666
  if (!options.quiet && beforeCount !== afterCount) {
51491
51667
  console.error(colors.dim(`Suppressed ${beforeCount - afterCount} finding(s) via config`));
51492
51668
  }
@@ -52035,7 +52211,7 @@ Usage: cognium-dev metrics <path> [options]`);
52035
52211
  category: options.category,
52036
52212
  output: options.output || options.o,
52037
52213
  quiet: options.quiet === true || options.q === true,
52038
- language: options.language || options.l ? normalizeLanguage(options.language || options.l) : undefined,
52214
+ language: languageFromCliOptions(options),
52039
52215
  excludeTests: options["exclude-tests"] === true,
52040
52216
  profile: options.profile || options.p
52041
52217
  };
@@ -52073,7 +52249,7 @@ Usage: cognium-dev scan <path> [options]`);
52073
52249
  }
52074
52250
  const targetPath = args2[0];
52075
52251
  const scanOptions = {
52076
- language: options.language || options.l ? normalizeLanguage(options.language || options.l) : undefined,
52252
+ language: languageFromCliOptions(options),
52077
52253
  format: options.format || options.f || "text",
52078
52254
  threads: parseInt(options.threads || "4", 10),
52079
52255
  severity: options.severity,
@@ -52107,12 +52283,15 @@ main().catch((error) => {
52107
52283
  process.exit(2);
52108
52284
  });
52109
52285
  export {
52286
+ resolveScanLanguage,
52110
52287
  matchesGlob,
52111
52288
  loadConfig,
52112
52289
  isTestFile2 as isTestFile,
52113
52290
  detectLanguage7 as detectLanguage,
52114
52291
  dedupeVulnerabilities,
52115
52292
  convertConfigToPassOptions,
52293
+ applySuppressionsToTaintPaths,
52116
52294
  applySuppressionsToResults,
52295
+ SUPPORTED_SCAN_LANGUAGES,
52117
52296
  PASS_REGISTRY
52118
52297
  };
package/package.json CHANGED
@@ -1,9 +1,7 @@
1
1
  {
2
2
  "name": "cognium-dev",
3
- "version": "4.9.24",
3
+ "version": "4.9.26",
4
4
  "description": "Static Application Security Testing CLI for detecting security vulnerabilities via taint tracking",
5
- "main": "dist/index.js",
6
- "types": "dist/index.d.ts",
7
5
  "bin": {
8
6
  "cognium-dev": "dist/cli.js"
9
7
  },
@@ -14,7 +12,7 @@
14
12
  "test": "bun test",
15
13
  "clean": "rm -rf dist cognium-dev cognium-dev-* *.bun-build wasm",
16
14
  "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",
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",
18
16
  "dogfood": "bun run src/cli.ts scan src/ -q",
19
17
  "prepublishOnly": "bun run build",
20
18
  "test:coverage": "bun test --coverage"
@@ -66,8 +64,8 @@
66
64
  "registry": "https://registry.npmjs.org/"
67
65
  },
68
66
  "dependencies": {
69
- "@cognium/project-profile-detect": "1.1.1",
70
- "circle-ir": "4.9.24"
67
+ "@cognium/project-profile-detect": "1.1.2",
68
+ "circle-ir": "4.9.26"
71
69
  },
72
70
  "devDependencies": {
73
71
  "@types/node": "^26.6.1",