code-auditor-mcp 4.0.4 → 4.1.1

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 (155) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/CHANGELOG.md +132 -0
  3. package/README.md +5 -5
  4. package/configs/hhra-compat.json +2 -2
  5. package/dist/analyzers/applicability.d.ts +25 -2
  6. package/dist/analyzers/applicability.d.ts.map +1 -1
  7. package/dist/analyzers/applicability.js +44 -50
  8. package/dist/analyzers/applicability.js.map +1 -1
  9. package/dist/analyzers/cross-language/APIContractAnalyzer.d.ts +18 -49
  10. package/dist/analyzers/cross-language/APIContractAnalyzer.d.ts.map +1 -1
  11. package/dist/analyzers/cross-language/APIContractAnalyzer.js +22 -240
  12. package/dist/analyzers/cross-language/APIContractAnalyzer.js.map +1 -1
  13. package/dist/analyzers/cross-language/SchemaValidator.d.ts +2 -14
  14. package/dist/analyzers/cross-language/SchemaValidator.d.ts.map +1 -1
  15. package/dist/analyzers/cross-language/SchemaValidator.js +0 -104
  16. package/dist/analyzers/cross-language/SchemaValidator.js.map +1 -1
  17. package/dist/analyzers/orgFilterTiers.d.ts +89 -0
  18. package/dist/analyzers/orgFilterTiers.d.ts.map +1 -0
  19. package/dist/analyzers/orgFilterTiers.js +96 -0
  20. package/dist/analyzers/orgFilterTiers.js.map +1 -0
  21. package/dist/analyzers/ruleRegistry.d.ts +15 -0
  22. package/dist/analyzers/ruleRegistry.d.ts.map +1 -1
  23. package/dist/analyzers/ruleRegistry.js +139 -185
  24. package/dist/analyzers/ruleRegistry.js.map +1 -1
  25. package/dist/analyzers/universal/UniversalDRYAnalyzer.d.ts +1 -12
  26. package/dist/analyzers/universal/UniversalDRYAnalyzer.d.ts.map +1 -1
  27. package/dist/analyzers/universal/UniversalDRYAnalyzer.js +1 -66
  28. package/dist/analyzers/universal/UniversalDRYAnalyzer.js.map +1 -1
  29. package/dist/analyzers/universal/UniversalDataAccessAnalyzer.d.ts +39 -0
  30. package/dist/analyzers/universal/UniversalDataAccessAnalyzer.d.ts.map +1 -1
  31. package/dist/analyzers/universal/UniversalDataAccessAnalyzer.js +62 -41
  32. package/dist/analyzers/universal/UniversalDataAccessAnalyzer.js.map +1 -1
  33. package/dist/analyzers/universal/UniversalSchemaAnalyzer.d.ts +2 -2
  34. package/dist/analyzers/universal/UniversalSchemaAnalyzer.d.ts.map +1 -1
  35. package/dist/analyzers/universal/UniversalSchemaAnalyzer.js +2 -2
  36. package/dist/analyzers/universal/UniversalSchemaAnalyzer.js.map +1 -1
  37. package/dist/analyzers/universal/UniversalSecurityAnalyzer.d.ts +86 -0
  38. package/dist/analyzers/universal/UniversalSecurityAnalyzer.d.ts.map +1 -0
  39. package/dist/analyzers/universal/UniversalSecurityAnalyzer.js +606 -0
  40. package/dist/analyzers/universal/UniversalSecurityAnalyzer.js.map +1 -0
  41. package/dist/analyzers/universal/schema/codeAnalysis.d.ts +14 -0
  42. package/dist/analyzers/universal/schema/codeAnalysis.d.ts.map +1 -1
  43. package/dist/analyzers/universal/schema/codeAnalysis.js +35 -18
  44. package/dist/analyzers/universal/schema/codeAnalysis.js.map +1 -1
  45. package/dist/analyzers/universal/schema/migrations.d.ts +14 -1
  46. package/dist/analyzers/universal/schema/migrations.d.ts.map +1 -1
  47. package/dist/analyzers/universal/schema/migrations.js +58 -14
  48. package/dist/analyzers/universal/schema/migrations.js.map +1 -1
  49. package/dist/auditRunner.d.ts.map +1 -1
  50. package/dist/auditRunner.js +71 -33
  51. package/dist/auditRunner.js.map +1 -1
  52. package/dist/churn/churnExtractor.js +5 -5
  53. package/dist/churn/churnExtractor.js.map +1 -1
  54. package/dist/cli.js +75 -26
  55. package/dist/cli.js.map +1 -1
  56. package/dist/codeIndexDB.d.ts +7 -9
  57. package/dist/codeIndexDB.d.ts.map +1 -1
  58. package/dist/codeIndexDB.js +33 -21
  59. package/dist/codeIndexDB.js.map +1 -1
  60. package/dist/config/configGeneratePath.d.ts +19 -0
  61. package/dist/config/configGeneratePath.d.ts.map +1 -0
  62. package/dist/config/configGeneratePath.js +29 -0
  63. package/dist/config/configGeneratePath.js.map +1 -0
  64. package/dist/config/configLoader.d.ts +36 -4
  65. package/dist/config/configLoader.d.ts.map +1 -1
  66. package/dist/config/configLoader.js +141 -16
  67. package/dist/config/configLoader.js.map +1 -1
  68. package/dist/config/defaults.d.ts.map +1 -1
  69. package/dist/config/defaults.js +5 -6
  70. package/dist/config/defaults.js.map +1 -1
  71. package/dist/config/lintConfigReader.d.ts +4 -0
  72. package/dist/config/lintConfigReader.d.ts.map +1 -1
  73. package/dist/config/lintConfigReader.js +35 -36
  74. package/dist/config/lintConfigReader.js.map +1 -1
  75. package/dist/config/staticObjectExtract.d.ts +35 -0
  76. package/dist/config/staticObjectExtract.d.ts.map +1 -0
  77. package/dist/config/staticObjectExtract.js +312 -0
  78. package/dist/config/staticObjectExtract.js.map +1 -0
  79. package/dist/constants.d.ts +1 -2
  80. package/dist/constants.d.ts.map +1 -1
  81. package/dist/constants.js +10 -7
  82. package/dist/constants.js.map +1 -1
  83. package/dist/daemon/core.js +1 -1
  84. package/dist/daemon/core.js.map +1 -1
  85. package/dist/daemon/main.js +1 -1
  86. package/dist/daemon/main.js.map +1 -1
  87. package/dist/daemon/resolve.d.ts +4 -4
  88. package/dist/daemon/resolve.js +4 -4
  89. package/dist/dataPaths.d.ts +10 -0
  90. package/dist/dataPaths.d.ts.map +1 -1
  91. package/dist/dataPaths.js +23 -2
  92. package/dist/dataPaths.js.map +1 -1
  93. package/dist/generators/BaseConfigGenerator.d.ts +0 -4
  94. package/dist/generators/BaseConfigGenerator.d.ts.map +1 -1
  95. package/dist/generators/BaseConfigGenerator.js +1 -7
  96. package/dist/generators/BaseConfigGenerator.js.map +1 -1
  97. package/dist/installConfig.d.ts +3 -1
  98. package/dist/installConfig.d.ts.map +1 -1
  99. package/dist/installConfig.js +15 -2
  100. package/dist/installConfig.js.map +1 -1
  101. package/dist/installer.d.ts +10 -0
  102. package/dist/installer.d.ts.map +1 -1
  103. package/dist/installer.js +14 -2
  104. package/dist/installer.js.map +1 -1
  105. package/dist/languages/LanguageOrchestrator.js +6 -6
  106. package/dist/languages/LanguageOrchestrator.js.map +1 -1
  107. package/dist/languages/RuntimeManager.js +13 -10
  108. package/dist/languages/RuntimeManager.js.map +1 -1
  109. package/dist/mcp-tools-shared.d.ts.map +1 -1
  110. package/dist/mcp-tools-shared.js +7 -2
  111. package/dist/mcp-tools-shared.js.map +1 -1
  112. package/dist/mcp-ui-simple.d.ts +5 -1
  113. package/dist/mcp-ui-simple.d.ts.map +1 -1
  114. package/dist/mcp-ui-simple.js +81 -29
  115. package/dist/mcp-ui-simple.js.map +1 -1
  116. package/dist/mcp.js +25 -6
  117. package/dist/mcp.js.map +1 -1
  118. package/dist/mcpAuditJobs.d.ts.map +1 -1
  119. package/dist/mcpAuditJobs.js +1 -0
  120. package/dist/mcpAuditJobs.js.map +1 -1
  121. package/dist/nextFileIncremental.js +1 -1
  122. package/dist/nextFileIncremental.js.map +1 -1
  123. package/dist/pipeline.d.ts.map +1 -1
  124. package/dist/pipeline.js +13 -2
  125. package/dist/pipeline.js.map +1 -1
  126. package/dist/pipelineAdapters.d.ts +16 -1
  127. package/dist/pipelineAdapters.d.ts.map +1 -1
  128. package/dist/pipelineAdapters.js +142 -11
  129. package/dist/pipelineAdapters.js.map +1 -1
  130. package/dist/reporting/htmlReportGenerator.js +2 -2
  131. package/dist/reporting/htmlReportGenerator.js.map +1 -1
  132. package/dist/ruleAliases.d.ts.map +1 -1
  133. package/dist/ruleAliases.js +45 -0
  134. package/dist/ruleAliases.js.map +1 -1
  135. package/dist/styles/tailwindConfigLoader.d.ts +4 -0
  136. package/dist/styles/tailwindConfigLoader.d.ts.map +1 -1
  137. package/dist/styles/tailwindConfigLoader.js +54 -36
  138. package/dist/styles/tailwindConfigLoader.js.map +1 -1
  139. package/dist/types.d.ts +60 -23
  140. package/dist/types.d.ts.map +1 -1
  141. package/dist/types.js +15 -0
  142. package/dist/types.js.map +1 -1
  143. package/dist/version.generated.d.ts +1 -1
  144. package/dist/version.generated.js +1 -1
  145. package/examples/.auditrc.example.json +2 -2
  146. package/examples/.auditrc.json +3 -3
  147. package/examples/nextjs.auditrc.json +1 -1
  148. package/examples/node-api.auditrc.json +1 -1
  149. package/examples/react.auditrc.json +2 -2
  150. package/examples/tolerant-solid.auditrc.json +1 -1
  151. package/package.json +5 -4
  152. package/plugin/.claude-plugin/plugin.json +1 -1
  153. package/plugin/scripts/hook-common.sh +27 -27
  154. package/plugin/skills/code-auditor/SKILL.md +1 -1
  155. package/server.json +2 -2
package/dist/cli.js CHANGED
@@ -25,7 +25,7 @@ import { resolveTelemetryConfig, signatureForFinding, buildTelemetryPayload, for
25
25
  import { getInstallId } from './installConfig.js';
26
26
  import { ALL_ANALYZERS } from './analyzers/ruleRegistry.js';
27
27
  import { computeGatingDecision } from './enforcement/gate.js';
28
- import { BLOCKING_SEVERITIES } from './types.js';
28
+ import { BLOCKING_SEVERITIES, SEVERITIES } from './types.js';
29
29
  import { rankFilesByPriority, orderFindingsWithinFile } from './nextFile.js';
30
30
  import { runNextFile } from './nextFileIncremental.js';
31
31
  import { describeSqliteBackend } from './sqlite/driver.js';
@@ -96,6 +96,7 @@ program
96
96
  .option('-c, --config <config>', 'Configuration name')
97
97
  .option('-o, --output <dir>', 'Output directory for reports')
98
98
  .option('-f, --format <format>', 'Report format: html, json, csv, or sarif')
99
+ .option('--overwrite', 'Overwrite an existing report file at --output (default: refuse)')
99
100
  .option('--fail-on <severity>', 'Exit code 2 when violations at or above this severity exist')
100
101
  .option('--full', 'Show full violation inventory (overrides default delta view when baseline exists)')
101
102
  .option('--include-baseline', 'Evaluate baseline-known violations in --fail-on checks')
@@ -118,7 +119,7 @@ program
118
119
  try {
119
120
  await initParsers();
120
121
  // Validate --fail-on severity
121
- const validSeverities = ['critical', 'severe', 'high'];
122
+ const validSeverities = SEVERITIES;
122
123
  const failOnSeverity = options.failOn;
123
124
  if (failOnSeverity && !validSeverities.includes(failOnSeverity)) {
124
125
  const renamed = LEGACY_SEVERITY_NAMES[failOnSeverity];
@@ -194,12 +195,24 @@ program
194
195
  console.log(chalk.yellow(` ${name} [${d.kind}] — ${d.message}`));
195
196
  }
196
197
  }
198
+ // ── Ignored config keys (Spec 61 R1.4) ─────────────────────────
199
+ // Keys dropped by sanitizeProjectFileConfig: undeclared keys (like
200
+ // `scope`, which previously reached execSync via mergeConfig's raw-key
201
+ // iteration) and path keys resolving outside the project root. Surfaced
202
+ // so a silently-ignored config never reads as "loaded fine".
203
+ const ignoredConfigKeys = (result.metadata?.diagnostics ?? []).filter((d) => d.kind === 'config-key-rejected');
204
+ if (ignoredConfigKeys.length > 0) {
205
+ console.log(chalk.yellow(`── Ignored config keys ── ${ignoredConfigKeys.length}`));
206
+ for (const d of ignoredConfigKeys) {
207
+ console.log(chalk.yellow(` ${d.message}`));
208
+ }
209
+ }
197
210
  // ── Coverage gaps (Spec 58 follow-up) ─────────────────────────
198
211
  // Coverage diagnostics are the analyzer saying "my visibility ends here",
199
212
  // not "the code is wrong". They lead with counts, then per-occurrence
200
213
  // file:line — visible and counted, never blocking (they never reach the
201
214
  // gate). Rendered alongside the coverage panel that leads the report.
202
- const coverageDiagnostics = (result.metadata?.diagnostics ?? []).filter((d) => d.kind === 'unresolved-query' || d.kind === 'unresolved-dynamic-import' || d.kind === 'undefined-class-not-found');
215
+ const coverageDiagnostics = (result.metadata?.diagnostics ?? []).filter((d) => d.kind === 'unresolved-query' || d.kind === 'unresolved-dynamic-import' || d.kind === 'undefined-class-not-found' || d.kind === 'cannot-fire');
203
216
  if (coverageDiagnostics.length > 0) {
204
217
  const byKind = {};
205
218
  for (const d of coverageDiagnostics)
@@ -444,12 +457,33 @@ program
444
457
  ? { rootDir, ...readVersionControlProvenance(rootDir) }
445
458
  : { rootDir };
446
459
  const report = generateReport(result, options.format, config);
447
- const outputDir = options.output || process.cwd();
448
460
  const ext = options.format === 'sarif' ? 'sarif' : options.format;
449
- const reportPath = join(outputDir, `audit-report.${ext}`);
450
- await fs.mkdir(dirname(reportPath), { recursive: true });
451
- await fs.writeFile(reportPath, report, 'utf-8');
452
- console.log(chalk.green(`\nReport written to ${reportPath}`));
461
+ if (options.output) {
462
+ // Explicit output path — write a file. Refuse to clobber an existing
463
+ // report unless --overwrite is passed. A report write is destructive;
464
+ // a silent overwrite of `audit-report.json` in a consuming repo is the
465
+ // defect this guard removes.
466
+ const outputDir = resolve(options.output);
467
+ const reportPath = join(outputDir, `audit-report.${ext}`);
468
+ let exists = false;
469
+ try {
470
+ await fs.access(reportPath);
471
+ exists = true;
472
+ }
473
+ catch { /* ok */ }
474
+ if (exists && !options.overwrite) {
475
+ console.error(chalk.red(`Refusing to overwrite existing report ${reportPath} — pass --overwrite to replace it.`));
476
+ process.exit(1);
477
+ }
478
+ await fs.mkdir(dirname(reportPath), { recursive: true });
479
+ await fs.writeFile(reportPath, report, 'utf-8');
480
+ console.log(chalk.green(`\nReport written to ${reportPath}`));
481
+ }
482
+ else {
483
+ // No output path — default to stdout. Never write a report into the
484
+ // audited project without an explicit --output on the command line.
485
+ process.stdout.write(report + '\n');
486
+ }
453
487
  }
454
488
  // ── Fail-on logic (Spec 18 R3) ───────────────────────────────
455
489
  // --fail-on-regression: compare total high debt to baseline snapshot
@@ -487,7 +521,7 @@ program
487
521
  const evaluableViolations = (baseline && !options.includeBaseline)
488
522
  ? violations.filter((v) => v.new || v.analyzer === 'invariants')
489
523
  : violations;
490
- const severityOrder = ['critical', 'severe', 'high'];
524
+ const severityOrder = SEVERITIES;
491
525
  const failIndex = severityOrder.indexOf(failOnSeverity);
492
526
  const hasAtOrAbove = evaluableViolations.some((v) => {
493
527
  // Spec 57 — a dismissed finding never blocks the gate.
@@ -821,7 +855,7 @@ program
821
855
  .action(async (paths, options) => {
822
856
  try {
823
857
  await initParsers();
824
- const validSeverities = ['critical', 'severe', 'high'];
858
+ const validSeverities = SEVERITIES;
825
859
  const failOnSeverity = options.failOn;
826
860
  if (!validSeverities.includes(failOnSeverity)) {
827
861
  const renamed = LEGACY_SEVERITY_NAMES[failOnSeverity];
@@ -859,7 +893,7 @@ program
859
893
  });
860
894
  const result = await runner.run();
861
895
  const violations = Object.values(result.analyzerResults).flatMap((r) => r.violations || []);
862
- const severityOrder = ['critical', 'severe', 'high'];
896
+ const severityOrder = SEVERITIES;
863
897
  const failIndex = severityOrder.indexOf(failOnSeverity);
864
898
  const blocking = violations.filter((v) => {
865
899
  if (!isSelfAuditInScope(v.file ?? ''))
@@ -1186,8 +1220,9 @@ program
1186
1220
  .description('Generate a .codeauditor.json scaffold with invariant rules')
1187
1221
  .option('-o, --output <dir>', 'Output directory', '.')
1188
1222
  .option('-i, --interactive', 'Interactive rule builder')
1189
- .option('-f, --force', 'Force overwrite existing file without confirmation')
1190
- .option('-y, --yes', 'Skip confirmation prompts (same as --force)')
1223
+ .option('--overwrite', 'Overwrite an existing .codeauditor.json without confirmation')
1224
+ .option('-f, --force', 'Alias for --overwrite')
1225
+ .option('-y, --yes', 'Alias for --overwrite')
1191
1226
  .action(async (options) => {
1192
1227
  console.log(chalk.blue('🛠️ Code Auditor Config Generator'));
1193
1228
  console.log(chalk.gray('════════════════════════════════════════════════════'));
@@ -1492,7 +1527,7 @@ configCmd
1492
1527
  let profiles = [];
1493
1528
  try {
1494
1529
  await fs.access(configPath);
1495
- const config = await loadConfig({ configPath });
1530
+ const { config } = await loadConfig({ configPath, projectRoot });
1496
1531
  profiles = config.pathProfiles || [];
1497
1532
  }
1498
1533
  catch {
@@ -1600,7 +1635,7 @@ configCmd
1600
1635
  try {
1601
1636
  const configPath = pathModule.join(projectRoot, '.codeauditor.json');
1602
1637
  await fsPromises.access(configPath);
1603
- config = await loadConfig({ configPath });
1638
+ config = (await loadConfig({ configPath, projectRoot })).config;
1604
1639
  }
1605
1640
  catch {
1606
1641
  // No config — use defaults
@@ -1783,6 +1818,7 @@ program
1783
1818
  .option('--no-unused-imports', 'Hide unused import warnings')
1784
1819
  .option('--min-complexity <threshold>', 'Minimum complexity threshold for warnings', '7')
1785
1820
  .option('-o, --output <file>', 'Save output to file instead of displaying')
1821
+ .option('--overwrite', 'Overwrite an existing output file (default: refuse)')
1786
1822
  .action(async (options) => {
1787
1823
  console.log(chalk.blue('🗺️ Codebase Map Generator'));
1788
1824
  console.log(chalk.gray('════════════════════════════════════════════════════'));
@@ -3143,16 +3179,13 @@ program
3143
3179
  // normalizes the real config, so path keys compare cleanly instead of
3144
3180
  // reading as "user-set" because one side is absolute and the other is not.
3145
3181
  const pristine = { ...getDefaultConfig(), codeIndex: { ...DEFAULT_CODE_INDEX_CONFIG } };
3146
- if (pristine.outputDirectory) {
3147
- pristine.outputDirectory = pathModule.resolve(projectRoot, pristine.outputDirectory);
3148
- }
3149
3182
  pristine.includePaths = (pristine.includePaths ?? []).map((p) => pathModule.isAbsolute(p) ? p : pathModule.resolve(projectRoot, p));
3150
3183
  pristine.excludePaths = (pristine.excludePaths ?? []).map((p) => pathModule.isAbsolute(p) ? p : pathModule.resolve(projectRoot, p));
3151
3184
  // Load the project config (defaults when no config file exists).
3152
3185
  let config;
3153
3186
  try {
3154
3187
  await fs.access(configPath);
3155
- config = await loadConfig({ configPath });
3188
+ config = (await loadConfig({ configPath, projectRoot })).config;
3156
3189
  }
3157
3190
  catch {
3158
3191
  config = pristine;
@@ -3522,6 +3555,9 @@ const SCAFFOLD_CONFIG = {
3522
3555
  async function generateConfigurations(options) {
3523
3556
  const outputDir = resolve(options.output || '.');
3524
3557
  const outputPath = join(outputDir, '.codeauditor.json');
3558
+ // One consent spelling across the CLI: `--overwrite`. `--force`/`--yes` are
3559
+ // retained as aliases for back-compat.
3560
+ const overwrite = Boolean(options.overwrite || options.force || options.yes);
3525
3561
  let config;
3526
3562
  if (options.interactive) {
3527
3563
  // --- Interactive rule builder ---
@@ -3541,7 +3577,7 @@ async function generateConfigurations(options) {
3541
3577
  config = await buildRulesInteractively();
3542
3578
  }
3543
3579
  // Confirm output directory
3544
- if (!options.force && !options.yes) {
3580
+ if (!overwrite) {
3545
3581
  const { dir } = await inquirer.prompt({
3546
3582
  dir: {
3547
3583
  type: 'input',
@@ -3586,12 +3622,12 @@ async function generateConfigurations(options) {
3586
3622
  exists = true;
3587
3623
  }
3588
3624
  catch { /* ok */ }
3589
- if (exists && !options.force && !options.yes) {
3625
+ if (exists && !overwrite) {
3590
3626
  console.log(chalk.yellow(`.codeauditor.json already exists at ${outputPath}`));
3591
- console.log(chalk.gray('Use --force or --yes to overwrite, or --interactive to build a custom config.'));
3627
+ console.log(chalk.gray('Use --overwrite to replace it, or --interactive to build a custom config.'));
3592
3628
  return;
3593
3629
  }
3594
- if (exists && (options.force || options.yes)) {
3630
+ if (exists && overwrite) {
3595
3631
  console.log(chalk.yellow('Overwriting existing .codeauditor.json...'));
3596
3632
  }
3597
3633
  config = SCAFFOLD_CONFIG;
@@ -3865,9 +3901,22 @@ async function generateCodeMap(options) {
3865
3901
  const textOutput = mapGenerator.formatAsText(completeCodeMap, mapOptions);
3866
3902
  // Output results
3867
3903
  if (options.output) {
3868
- // Save to file
3869
- await fs.writeFile(options.output, textOutput);
3870
- console.log(chalk.green(`✓ Code map saved to: ${options.output}`));
3904
+ // Save to file. Refuse to clobber an existing file unless --overwrite is
3905
+ // passed — same guard as the report writer, so `map -o` cannot silently
3906
+ // destroy a file the developer did not consent to replacing.
3907
+ const outPath = resolve(options.output);
3908
+ let exists = false;
3909
+ try {
3910
+ await fs.access(outPath);
3911
+ exists = true;
3912
+ }
3913
+ catch { /* ok */ }
3914
+ if (exists && !options.overwrite) {
3915
+ console.error(chalk.red(`Refusing to overwrite existing file ${outPath} — pass --overwrite to replace it.`));
3916
+ process.exit(1);
3917
+ }
3918
+ await fs.writeFile(outPath, textOutput);
3919
+ console.log(chalk.green(`✓ Code map saved to: ${outPath}`));
3871
3920
  // Show summary
3872
3921
  console.log('');
3873
3922
  console.log(chalk.blue('Summary:'));