@dailephd/my-dev-kit-lab 0.2.1 → 0.3.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 (66) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +36 -24
  3. package/dist/scripts/audits/runAudit.js +101 -0
  4. package/dist/scripts/security/validate.js +68 -29
  5. package/dist/src/audits/codeRot/codeRotDetectors.js +17 -0
  6. package/dist/src/audits/codeRot/detectors/architectureDriftDetector.js +271 -0
  7. package/dist/src/audits/codeRot/detectors/crossPlatformRotDetector.js +214 -0
  8. package/dist/src/audits/codeRot/detectors/deadCodeCandidateDetector.js +344 -0
  9. package/dist/src/audits/codeRot/detectors/dependencyEnvironmentRotDetector.js +244 -0
  10. package/dist/src/audits/codeRot/detectors/docsCodeMismatchDetector.js +219 -0
  11. package/dist/src/audits/codeRot/detectors/duplicateImplementationDetector.js +273 -0
  12. package/dist/src/audits/codeRot/detectors/packageReleaseRotDetector.js +325 -0
  13. package/dist/src/audits/codeRot/detectors/securityValidationAssumptionRotDetector.js +385 -0
  14. package/dist/src/audits/codeRot/detectors/staleCommandReferenceDetector.js +135 -0
  15. package/dist/src/audits/codeRot/detectors/testRotDetector.js +348 -0
  16. package/dist/src/audits/codeRot/utils/boundedRead.js +23 -0
  17. package/dist/src/audits/codeRot/utils/docClaimPatterns.js +62 -0
  18. package/dist/src/audits/codeRot/utils/docCommandReferences.js +64 -0
  19. package/dist/src/audits/codeRot/utils/filePatternUtils.js +47 -0
  20. package/dist/src/audits/codeRot/utils/issueFactories.js +38 -0
  21. package/dist/src/audits/codeRot/utils/textLines.js +12 -0
  22. package/dist/src/audits/core/auditConfig.js +125 -0
  23. package/dist/src/audits/core/auditExitCode.js +75 -0
  24. package/dist/src/audits/core/auditIssue.js +20 -0
  25. package/dist/src/audits/core/auditRegistry.js +40 -0
  26. package/dist/src/audits/core/auditRunner.js +71 -0
  27. package/dist/src/audits/core/auditTarget.js +19 -0
  28. package/dist/src/audits/core/auditTypes.js +30 -0
  29. package/dist/src/audits/core/projectInventory.js +308 -0
  30. package/dist/src/audits/core/sourceOfTruth.js +339 -0
  31. package/dist/src/audits/core/textLines.js +24 -0
  32. package/dist/src/audits/report/auditReportModel.js +241 -0
  33. package/dist/src/audits/report/auditReportPaths.js +13 -0
  34. package/dist/src/audits/report/renderAuditJsonReport.js +29 -0
  35. package/dist/src/audits/report/renderAuditTextReport.js +165 -0
  36. package/dist/src/audits/report/sanitizeAuditText.js +39 -0
  37. package/dist/src/audits/report/writeAuditReports.js +27 -0
  38. package/dist/src/core/resolveCommand.js +53 -2
  39. package/dist/src/securityValidation/attackScenarios/attackProfile.js +20 -0
  40. package/dist/src/securityValidation/attackScenarios/attackResult.js +37 -0
  41. package/dist/src/securityValidation/attackScenarios/attackRunner.js +153 -0
  42. package/dist/src/securityValidation/attackScenarios/attackScenario.js +21 -0
  43. package/dist/src/securityValidation/attackScenarios/boundedSourceScan.js +100 -0
  44. package/dist/src/securityValidation/attackScenarios/exploitEvidence.js +69 -0
  45. package/dist/src/securityValidation/attackScenarios/payloadCorpus.js +0 -0
  46. package/dist/src/securityValidation/attackScenarios/profiles/localToolProfile.js +10 -0
  47. package/dist/src/securityValidation/attackScenarios/profiles/nodeCliPackageProfile.js +20 -0
  48. package/dist/src/securityValidation/attackScenarios/profiles/npmPackageProfile.js +10 -0
  49. package/dist/src/securityValidation/attackScenarios/reportSchemaGuard.js +41 -0
  50. package/dist/src/securityValidation/attackScenarios/scenarios/configInjectionScenario.js +144 -0
  51. package/dist/src/securityValidation/attackScenarios/scenarios/networkAssumptionScenario.js +129 -0
  52. package/dist/src/securityValidation/attackScenarios/scenarios/outputBoundaryScenario.js +69 -0
  53. package/dist/src/securityValidation/attackScenarios/scenarios/packageBoundaryScenario.js +118 -0
  54. package/dist/src/securityValidation/attackScenarios/scenarios/pathTraversalScenario.js +119 -0
  55. package/dist/src/securityValidation/attackScenarios/scenarios/reportPoisoningScenario.js +176 -0
  56. package/dist/src/securityValidation/attackScenarios/scenarios/secretLeakageScenario.js +143 -0
  57. package/dist/src/securityValidation/attackScenarios/scenarios/subprocessInjectionScenario.js +117 -0
  58. package/dist/src/securityValidation/attackScenarios/scenarios/targetSandboxScenario.js +118 -0
  59. package/dist/src/securityValidation/attackScenarios/targetSnapshot.js +70 -0
  60. package/dist/src/securityValidation/packageChecks/forbiddenPackageContents.js +4 -1
  61. package/dist/src/securityValidation/report/renderSecurityReport.js +135 -13
  62. package/dist/src/securityValidation/types.js +7 -0
  63. package/dist/src/securityValidation/validate/cliOptions.js +182 -0
  64. package/dist/src/securityValidation/validate/runSecurityValidation.js +134 -35
  65. package/dist/src/securityValidation/validate/verdict.js +147 -0
  66. package/package.json +18 -17
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 dailephd LLC
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,8 +1,10 @@
1
1
  # my-dev-kit-lab
2
2
 
3
- my-dev-kit-lab is the experiment, evidence, reporting, and demo companion for [my-dev-kit](https://github.com/your-org/my-dev-kit). It runs reproducible experiments that test whether my-dev-kit's graph-guided retrieval helps coding-agent workflows, collects metrics, renders reports, generates plots, captures screenshots, and builds gallery outputs.
3
+ my-dev-kit-lab is the experiment, evidence, reporting, security-validation, and future audit companion for my-dev-kit. It runs reproducible experiments that test whether my-dev-kit's graph-guided retrieval helps coding-agent workflows, collects metrics, renders reports, generates plots, captures screenshots, builds gallery outputs, and performs automated CLI/package security validation.
4
4
 
5
- As of v0.2.1, my-dev-kit-lab also exposes a generic experiment-plugin framework for validating local developer tools, security checks, codebase workflows, retrieval strategies, and experiment outcomes. The first plugin is `context-strategy-comparison`, which preserves the existing raw-full-file vs my-dev-kit-guided workflow through the plugin runner.
5
+ The current package version is `v0.3.0` (release-prepared; not yet published to npm, tagged, or released on GitHub). The latest published npm baseline is `v0.2.2`. Its generic experiment-plugin framework was introduced in v0.2.0. The first plugin is `context-strategy-comparison`, which preserves the existing raw-full-file vs my-dev-kit-guided workflow through the plugin runner.
6
+
7
+ my-dev-kit is most useful when the repository is larger than the task. It helps coding agents work with large codebases through reusable structural indexing, graph-guided retrieval, targeted source slices, and auditable context selection. Results are scoped evidence; the project does not claim that my-dev-kit always saves tokens.
6
8
 
7
9
  **my-dev-kit** is the repo indexing and graph-guided retrieval engine.
8
10
  **my-dev-kit-lab** is the separate lab layer that feeds it benchmark inputs and records evaluation outputs.
@@ -29,7 +31,8 @@ As of v0.2.1, my-dev-kit-lab also exposes a generic experiment-plugin framework
29
31
  - First experiment plugin: `context-strategy-comparison`
30
32
  - Target-aware experiment execution for local projects via `experiment:run -- --target <path>`
31
33
  - Plugin-aware JSON and HTML reports with plugin, target, variant, metric, artifact, warning, skip, and failure metadata
32
- - Security validation framework: dependency audit, package tarball inspection, CLI adversarial tests, static scans (CodeQL/Semgrep), bounded fuzz smoke, and release verdict — runnable against any local project via `security:validate --target <path>`
34
+ - Security validation framework: dependency audit, package tarball inspection, CLI adversarial tests, static scans (CodeQL/Semgrep), bounded fuzz smoke, attack-scenario checks, and structured verdict/report output — runnable against any local project via `security:validate --target <path>`
35
+ - Generic audit framework (package.json now specifies version `v0.3.0`; release-prepared but not yet published to npm, tagged, or released on GitHub): `npm run audit` runs heuristic, conservative code-rot detectors and writes text/JSON reports under `reports/audits/code-rot/` by default. Only the `code-rot` audit type is currently implemented; `quality`, `security`, `project`, and `all` audit types are planned and fail cleanly instead of running. `npm run audit` supports `--target`, `--types code-rot`, and other flags — see [docs/COMMANDS.md](docs/COMMANDS.md). Audit does not modify target files, does not auto-fix issues, does not run `security:validate`, and does not perform release-readiness determination.
33
36
 
34
37
  ---
35
38
 
@@ -37,19 +40,14 @@ As of v0.2.1, my-dev-kit-lab also exposes a generic experiment-plugin framework
37
40
 
38
41
  ```mermaid
39
42
  flowchart TD
40
- A[Benchmark Projects] --> B[Prompt Variants]
41
- B --> C[Agent Adapters\nfake-agent / Codex / Claude]
42
- C --> D[Controlled Experiment Runner]
43
- D --> E[Experiment Artifacts\nJSON]
44
- E --> F[Report Renderer]
45
- E --> G[Plot Generator]
46
- E --> H[Visualization Demos]
47
- F --> I[HTML Report + optional PNG]
48
- G --> J[SVG Charts]
49
- H --> K[Demo Artifacts]
50
- I --> L[Gallery]
51
- J --> L
52
- K --> L
43
+ A[scripts/experiments] --> B[Experiment plugin registry + runner]
44
+ B --> C[context-strategy-comparison]
45
+ C --> D[Controlled experiment foundations]
46
+ D --> E[Plugin + legacy artifacts]
47
+ E --> F[Plugin-aware reports]
48
+ E --> G[Plots / screenshots / gallery]
49
+ H[scripts/security] --> I[Automated security validation]
50
+ I --> J[Security reports + verdict]
53
51
  ```
54
52
 
55
53
  ---
@@ -205,7 +203,7 @@ See [docs/METRICS.md](docs/METRICS.md) for full metric definitions.
205
203
 
206
204
  ## Current baseline release positioning
207
205
 
208
- my-dev-kit-lab is at a working baseline. The raw-vs-indexed experiment pipeline is fully implemented and produces reproducible artifacts. Real-agent campaign support exists for Codex and Claude. v0.2.1 keeps the generic experiment-plugin framework, preserves `context-strategy-comparison` as the first plugin, and fixes target-aware security validation so installed-package checks run `npm run test:security` in the external target project root.
206
+ my-dev-kit-lab is at a working baseline. The raw-vs-indexed experiment pipeline is fully implemented and produces reproducible artifacts. Real-agent campaign support exists for Codex and Claude. The latest published npm baseline is `v0.2.2`, which implements the automated security-validation fortification work described below and has passed pre-release readiness and cross-platform validation. package.json now specifies version `v0.3.0`, which adds the generic audit framework described below; `v0.3.0` is release-prepared but has not yet been published to npm, tagged, or released on GitHub.
209
207
 
210
208
  ---
211
209
 
@@ -223,7 +221,7 @@ This is not a web application pentest framework. **my-dev-kit** is a local CLI/p
223
221
  - database-free
224
222
  - safe to run on local repositories
225
223
 
226
- The release gate is implemented as of v0.1.4. It combines static scans, dependency/package checks, adversarial CLI tests, bounded fuzz smoke tests, and a structured release security report with a four-category verdict.
224
+ The automated validation gate is implemented. It combines static scans, dependency/package checks, adversarial CLI tests, bounded fuzz smoke tests, and attack-scenario checks with structured text/JSON reports and a four-category verdict.
227
225
 
228
226
  ### Security commands
229
227
 
@@ -233,15 +231,23 @@ The release gate is implemented as of v0.1.4. It combines static scans, dependen
233
231
  | `npm run security:package` | npm pack --dry-run, forbidden content detection |
234
232
  | `npm run security:codeql` | CodeQL CLI availability check; skipped gracefully when absent |
235
233
  | `npm run security:semgrep` | Semgrep scan via local binary or npx; skipped gracefully when both absent |
236
- | `npm run test:security` | 165 adversarial CLI tests (path traversal, read-only boundaries, malformed artifacts, JSON safety, fuzz targets) |
234
+ | `npm run test:security` | Automated security and adversarial CLI tests (path traversal, read-only boundaries, malformed artifacts, JSON safety, and related checks) |
237
235
  | `npm run test:fuzz:smoke` | 9 bounded fuzz targets, seeded PRNG, completes in under 1 second |
238
- | `npm run security:validate` | Full release gate — runs all checks and writes `reports/security/<prefix>-security-validation.{txt,json}` |
236
+ | `npm run security:validate` | Runs selected security checks, applies profile-aware defaults when requested, and writes text/JSON reports according to `--format` |
237
+
238
+ CodeQL, Semgrep, and OSV-Scanner are optional. When unavailable locally, they are recorded as `skipped` in the report, not as passed. That can lead to `ready except optional manual checks`; it does not silently turn missing tooling into a clean pass.
239
+
240
+ Each security command can validate my-dev-kit-lab itself or another local project via `--target <path>`. When `--target` is omitted, the framework performs self-validation. Target projects are inspected in place: their source files are not modified by default, generated artifacts stay under `reports/security/` unless `--out` is used, and external-target reports identify the tool root, target root, target package metadata, target git metadata, command cwd, exit code, and stdout/stderr summaries.
241
+
242
+ `security:validate` supports `--checks`, `--profile`, `--format`, `--fail-on`, `--out`, and `--report-prefix`. The no-flag path remains backward compatible and still runs the classic implemented check groups: `deps`, `package`, `static`, `cli-adversarial`, and `fuzz`. Supplying `--profile` without `--checks` swaps in that profile's default checks; supplying explicit `--checks` always wins over profile defaults.
243
+
244
+ The current attack-scenario checks cover boundary, subprocess, secrets, and network assumptions. They are automated adversarial checks, not a manual pentest. `verdictImpact` metadata from each registered scenario drives blocker categorization in report reasoning, and narrowed `--checks` runs are labeled as scoped rather than described as a full release gate.
239
245
 
240
- CodeQL, Semgrep, and OSV-Scanner are optional. When unavailable locally, they are recorded as `skipped` in the report — not as failures — and the verdict is `ready except optional manual checks` rather than `not ready`.
246
+ JSON report poisoning/config-injection checks use a baseline-diff schema guard so legitimate additive JSON fields do not fail the guard while payload-created trusted top-level fields still do. Text reports are sanitized to strip ANSI/control-byte payloads before rendering.
241
247
 
242
- Each security command can validate my-dev-kit-lab itself or another local project via `--target <path>`. When `--target` is omitted, the framework performs self-validation. Target projects are inspected in place: their source files are not modified, generated artifacts stay under `reports/security/`, and external-target reports identify the tool root, target root, target package metadata, target git metadata, command cwd, exit code, and stdout/stderr summaries.
243
-
244
- For external-target validation, `security:validate` reads the target `package.json`, detects `scripts.test:security`, and runs `npm run test:security` in the target project root when that script exists. This behavior is validated both from the source checkout and from an installed packed tarball because published-package execution differs from local source execution.
248
+ Known limits remain intentionally explicit: secret scanning is bounded rather than exhaustive, the network/local-first check is a bounded static assumption check rather than proof of runtime isolation, package-boundary severity is still result-level rather than per-evidence-item, and profile-specific scenario behavior is limited to profile-based scenario selection/default checks.
249
+
250
+ For external-target validation, `security:validate` reads the target `package.json`, detects `scripts.test:security`, and runs `npm run test:security` in the target project root when that script exists. This behavior is validated both from the source checkout and from an installed packed tarball because published-package execution differs from local source execution.
245
251
 
246
252
  Generated security reports under `reports/security/` are excluded from git by default. They are produced locally or in CI as release-gate evidence and are not committed to the repository.
247
253
 
@@ -249,6 +255,12 @@ See [docs/COMMANDS.md](docs/COMMANDS.md) for full command options and [docs/secu
249
255
 
250
256
  ---
251
257
 
258
+ ## License
259
+
260
+ MIT License. See [LICENSE](LICENSE) for the full text.
261
+
262
+ ---
263
+
252
264
  ## Support
253
265
 
254
266
  my-dev-kit-lab is an independent project by dailephd LLC, developed and maintained by Dai Le.
@@ -0,0 +1,101 @@
1
+ #!/usr/bin/env node
2
+ import { parseAuditArgs, normalizeAuditConfig } from "../../src/audits/core/auditConfig.js";
3
+ import { resolveAuditTarget } from "../../src/audits/core/auditTarget.js";
4
+ import { runAudit } from "../../src/audits/core/auditRunner.js";
5
+ import { AUDIT_EXIT_CODES } from "../../src/audits/core/auditExitCode.js";
6
+ import { resolveToolRoot } from "../security/resolveToolRoot.js";
7
+ import { buildAuditReportModel } from "../../src/audits/report/auditReportModel.js";
8
+ import { writeAuditReports } from "../../src/audits/report/writeAuditReports.js";
9
+ // ---------------------------------------------------------------------------
10
+ // v0.3.0 Batch 5 -- npm run audit entrypoint.
11
+ //
12
+ // Thin wiring only: parse args -> normalize config -> resolve target ->
13
+ // runAudit() -> buildAuditReportModel() -> writeAuditReports() -> print a
14
+ // concise console summary -> set process.exitCode. All report-shaping logic
15
+ // (the former inline buildJsonReport()/renderMinimalTextSummary()/
16
+ // summarizeInventory()/summarizeSourceOfTruth() functions from Batch 1/2)
17
+ // now lives in src/audits/report/ -- see auditReportModel.ts,
18
+ // renderAuditJsonReport.ts, renderAuditTextReport.ts, writeAuditReports.ts.
19
+ // Mirrors the structure of scripts/security/validate.ts.
20
+ // ---------------------------------------------------------------------------
21
+ const USAGE = "Usage: npm run audit -- [--target <path>] [--types code-rot] " +
22
+ "[--include docs,tests,package,architecture,cli] [--format text,json] " +
23
+ "[--fail-on blocker|high|medium|low|none] [--out <path>]";
24
+ const rawArgs = process.argv.slice(2);
25
+ const toolRoot = resolveToolRoot(import.meta.url);
26
+ let config;
27
+ try {
28
+ const args = parseAuditArgs(rawArgs);
29
+ config = normalizeAuditConfig(args, toolRoot);
30
+ }
31
+ catch (err) {
32
+ const msg = err instanceof Error ? err.message : String(err);
33
+ console.error(`\nERROR: ${msg}`);
34
+ console.error(USAGE);
35
+ process.exitCode = AUDIT_EXIT_CODES.FATAL_ERROR;
36
+ process.exit(AUDIT_EXIT_CODES.FATAL_ERROR);
37
+ }
38
+ let target;
39
+ try {
40
+ target = resolveAuditTarget(config.targetPathArg, toolRoot);
41
+ }
42
+ catch (err) {
43
+ const msg = err instanceof Error ? err.message : String(err);
44
+ console.error(`\nERROR: ${msg}`);
45
+ console.error(USAGE);
46
+ process.exitCode = AUDIT_EXIT_CODES.FATAL_ERROR;
47
+ process.exit(AUDIT_EXIT_CODES.FATAL_ERROR);
48
+ }
49
+ console.log("=".repeat(60));
50
+ console.log("my-dev-kit-lab audit");
51
+ console.log("=".repeat(60));
52
+ console.log(`Tool root : ${toolRoot}`);
53
+ if (!target.isSelf) {
54
+ console.log(`Target : ${target.rootPath}`);
55
+ }
56
+ else {
57
+ console.log(`Mode : self-audit`);
58
+ }
59
+ console.log(`Types : ${config.types.join(", ")}`);
60
+ console.log(`Include : ${config.include.join(", ")}`);
61
+ console.log("");
62
+ let result;
63
+ try {
64
+ result = await runAudit({ config, toolRoot, target });
65
+ }
66
+ catch (err) {
67
+ const msg = err instanceof Error ? err.message : String(err);
68
+ console.error(`\nERROR: audit runtime failure: ${msg}`);
69
+ process.exitCode = AUDIT_EXIT_CODES.FATAL_ERROR;
70
+ process.exit(AUDIT_EXIT_CODES.FATAL_ERROR);
71
+ }
72
+ const model = buildAuditReportModel(result, { target });
73
+ const { writtenPaths } = writeAuditReports({ model, config, outDir: config.out });
74
+ console.log(`Issues found : ${model.summary.totalIssues}`);
75
+ console.log(` blocker=${model.summary.issuesBySeverity.blocker} high=${model.summary.issuesBySeverity.high} medium=${model.summary.issuesBySeverity.medium} low=${model.summary.issuesBySeverity.low} info=${model.summary.issuesBySeverity.info}`);
76
+ console.log(`Skipped detectors : ${model.summary.skippedDetectorCount}`);
77
+ if (model.summary.detectorErrorCount > 0) {
78
+ console.log(`Detector errors : ${model.summary.detectorErrorCount}`);
79
+ for (const e of model.detectorErrors) {
80
+ console.log(` [${e.id}] ${e.message}`);
81
+ }
82
+ }
83
+ if (model.summary.noDetectorsRegistered) {
84
+ console.log(`Note: no code-rot detectors are registered for this run's --types/--include selection -- this run collected inventory/source-of-truth data only.`);
85
+ }
86
+ console.log(`\nInventory: ${model.inventory.totalScannedFileCount} file(s) scanned, ${model.inventory.skippedFileCount} skipped`);
87
+ console.log(` source=${model.inventory.filesByCategory.source} tests=${model.inventory.filesByCategory.tests} docs=${model.inventory.filesByCategory.docs} package=${model.inventory.filesByCategory.package} config=${model.inventory.filesByCategory.config} scripts=${model.inventory.filesByCategory.scripts} ci=${model.inventory.filesByCategory.ci}`);
88
+ console.log(`Source of truth: package=${model.sourceOfTruth.packageName ?? "(none)"}@${model.sourceOfTruth.packageVersion ?? "?"} readme=${model.sourceOfTruth.hasReadme} changelog=${model.sourceOfTruth.hasChangelog} ciWorkflows=${model.sourceOfTruth.ciWorkflowCount}`);
89
+ if (writtenPaths.length > 0) {
90
+ console.log(`\nReports written:`);
91
+ for (const p of writtenPaths) {
92
+ console.log(` ${p}`);
93
+ }
94
+ }
95
+ console.log(`\nHighest severity: ${model.summary.highestSeverity ?? "(none)"}`);
96
+ console.log(`Verdict: ${model.summary.finalVerdictLabel}`);
97
+ console.log(`Exit reason: ${model.exit.reason}`);
98
+ console.log(model.exit.code === AUDIT_EXIT_CODES.SUCCESS
99
+ ? `Exit 0 -- audit completed.`
100
+ : `Exit 1 -- fail-on threshold breached.`);
101
+ process.exitCode = model.exit.code;
@@ -4,11 +4,34 @@ import fs from "node:fs";
4
4
  import { runSecurityValidation } from "../../src/securityValidation/validate/runSecurityValidation.js";
5
5
  import { resolveValidationTarget, reportFilenamePrefix } from "../../src/securityValidation/validate/resolveTarget.js";
6
6
  import { renderTextReport, renderJsonReport } from "../../src/securityValidation/report/renderSecurityReport.js";
7
+ import { parseSecurityValidateArgs, normalizeSecurityValidateConfig, applyProfileDefaultChecksIfApplicable, } from "../../src/securityValidation/validate/cliOptions.js";
8
+ import { resolveAttackProfile } from "../../src/securityValidation/attackScenarios/attackProfile.js";
9
+ import { findingsBreachFailOnThreshold } from "../../src/securityValidation/validate/verdict.js";
7
10
  import { resolveToolRoot } from "./resolveToolRoot.js";
11
+ const USAGE = "Usage: npm run security:validate -- [--target <path>] [--out <dir>] [--report-prefix <name>] " +
12
+ "[--checks deps,package,static,cli-adversarial,fuzz,boundary,subprocess,secrets,network] " +
13
+ "[--profile node-cli-package|local-tool|npm-package] [--format text,json] [--fail-on blocker|high|medium|low]";
8
14
  // Parse CLI arguments from process.argv (after the node/tsx and script path).
9
15
  const rawArgs = process.argv.slice(2);
10
- const args = parseArgs(rawArgs);
11
16
  const toolRoot = resolveToolRoot(import.meta.url);
17
+ let args;
18
+ let config;
19
+ try {
20
+ args = parseSecurityValidateArgs(rawArgs);
21
+ config = normalizeSecurityValidateConfig(args, toolRoot);
22
+ // v0.2.2 Batch 5: --profile with no --checks uses that profile's declared
23
+ // default checks. No-op unless the user passed --profile without --checks
24
+ // (see applyProfileDefaultChecksIfApplicable's guard) — the true no-flag
25
+ // case and any explicit --checks case are always unaffected.
26
+ config = applyProfileDefaultChecksIfApplicable(config, resolveAttackProfile(config.profile).defaultCheckIds);
27
+ }
28
+ catch (err) {
29
+ const msg = err instanceof Error ? err.message : String(err);
30
+ console.error(`\nERROR: ${msg}`);
31
+ console.error(USAGE);
32
+ process.exitCode = 1;
33
+ process.exit(1);
34
+ }
12
35
  // Resolve and validate target early so we can fail fast with a clean error.
13
36
  let target;
14
37
  try {
@@ -17,7 +40,7 @@ try {
17
40
  catch (err) {
18
41
  const msg = err instanceof Error ? err.message : String(err);
19
42
  console.error(`\nERROR: ${msg}`);
20
- console.error("Usage: npm run security:validate -- [--target <path>] [--out <dir>] [--report-prefix <name>]");
43
+ console.error(USAGE);
21
44
  process.exitCode = 1;
22
45
  process.exit(1);
23
46
  }
@@ -33,13 +56,30 @@ if (!target.isSelf) {
33
56
  else {
34
57
  console.log(`Mode : self-validation`);
35
58
  }
59
+ if (!config.checksWereDefault) {
60
+ console.log(`Checks : ${config.checks.join(", ")}`);
61
+ }
62
+ if (config.plannedChecksRequested.length > 0) {
63
+ console.log(`Note : ${config.plannedChecksRequested.join(", ")} are routed through the attack-scenario framework rather than the classic check-group runners. Review the attack-scenario section in the report for their concrete results.`);
64
+ }
36
65
  console.log("");
37
66
  const summary = await runSecurityValidation({
38
67
  cwd: toolRoot,
39
68
  targetPath: args.target,
40
69
  fuzzIterations: parseInt(process.env["FUZZ_ITERATIONS"] ?? "50", 10),
41
70
  fuzzSeed: parseInt(process.env["FUZZ_SEED"] ?? "0xDEADBEEF", 16),
71
+ // Full selection (implemented + planned/attack-scenario ids) — the runner
72
+ // gates both implemented check groups and attack-scenario checks off the
73
+ // same selectedChecks set.
74
+ selectedChecks: config.checks,
75
+ profile: config.profile,
42
76
  });
77
+ // v0.2.2 Batch 5: --fail-on breach is additive to (never a substitute for)
78
+ // the existing verdict-based exit decision below — it can only escalate
79
+ // exit 0 to exit 1 (e.g. --fail-on medium/low catching minor/informational
80
+ // findings the verdict policy doesn't already treat as blocking), never
81
+ // downgrade an existing blocker.
82
+ const failOnBreached = findingsBreachFailOnThreshold(summary.findings, config.failOnThreshold);
43
83
  // Build report object
44
84
  const report = {
45
85
  metadata: {
@@ -55,19 +95,25 @@ const report = {
55
95
  isSelf: summary.isSelf,
56
96
  generatedAt: summary.finishedAt,
57
97
  totalDurationMs: new Date(summary.finishedAt).getTime() - new Date(summary.startedAt).getTime(),
98
+ profile: config.profile,
99
+ selectedChecks: config.checks,
100
+ failOnThreshold: config.failOnThreshold,
101
+ formats: config.formats,
102
+ failOnBreached,
103
+ isFullReleaseGate: summary.isFullReleaseGate,
58
104
  },
59
105
  sections: [],
60
106
  allChecks: summary.checks,
61
107
  allFindings: summary.findings,
62
108
  verdict: summary.verdict,
63
109
  recommendedNextStep: summary.recommendedNextStep,
110
+ attackResults: summary.attackResults,
111
+ verdictReasonSummary: summary.verdictReasonSummary,
64
112
  };
65
113
  const textReport = renderTextReport(report);
66
114
  const jsonReport = renderJsonReport(report);
67
115
  // Determine output directory
68
- const reportsDir = args.out
69
- ? path.resolve(args.out)
70
- : path.join(toolRoot, "reports", "security");
116
+ const reportsDir = config.out;
71
117
  if (!fs.existsSync(reportsDir)) {
72
118
  fs.mkdirSync(reportsDir, { recursive: true });
73
119
  }
@@ -75,14 +121,22 @@ if (!fs.existsSync(reportsDir)) {
75
121
  const prefix = args.reportPrefix ?? reportFilenamePrefix(target);
76
122
  const txtPath = path.join(reportsDir, `${prefix}-security-validation.txt`);
77
123
  const jsonPath = path.join(reportsDir, `${prefix}-security-validation.json`);
78
- fs.writeFileSync(txtPath, textReport, "utf8");
79
- fs.writeFileSync(jsonPath, jsonReport, "utf8");
124
+ const writtenPaths = [];
125
+ if (config.formats.includes("text")) {
126
+ fs.writeFileSync(txtPath, textReport, "utf8");
127
+ writtenPaths.push(txtPath);
128
+ }
129
+ if (config.formats.includes("json")) {
130
+ fs.writeFileSync(jsonPath, jsonReport, "utf8");
131
+ writtenPaths.push(jsonPath);
132
+ }
80
133
  // Print report to stdout
81
134
  console.log(textReport);
82
135
  console.log(`\nReports written:`);
83
- console.log(` ${txtPath}`);
84
- console.log(` ${jsonPath}`);
85
- // Exit code based on verdict
136
+ for (const p of writtenPaths) {
137
+ console.log(` ${p}`);
138
+ }
139
+ // Exit code based on verdict, escalated (never downgraded) by --fail-on.
86
140
  const blockerExists = summary.verdict === "not-ready-security-blocker-remains";
87
141
  const inconclusive = summary.verdict === "inconclusive-audit-environment-incomplete";
88
142
  if (blockerExists) {
@@ -93,26 +147,11 @@ else if (inconclusive) {
93
147
  console.warn("\nExit 2 — audit environment incomplete.");
94
148
  process.exitCode = 2;
95
149
  }
150
+ else if (failOnBreached) {
151
+ console.error(`\nExit 1 — --fail-on ${config.failOnThreshold} threshold breached.`);
152
+ process.exitCode = 1;
153
+ }
96
154
  else {
97
155
  console.log("\nExit 0 — validation completed.");
98
156
  process.exitCode = 0;
99
157
  }
100
- // ---------------------------------------------------------------------------
101
- // Argument parser
102
- // ---------------------------------------------------------------------------
103
- function parseArgs(argv) {
104
- const result = {};
105
- for (let i = 0; i < argv.length; i++) {
106
- const arg = argv[i];
107
- if ((arg === "--target" || arg === "-t") && i + 1 < argv.length) {
108
- result.target = argv[++i];
109
- }
110
- else if (arg === "--out" && i + 1 < argv.length) {
111
- result.out = argv[++i];
112
- }
113
- else if (arg === "--report-prefix" && i + 1 < argv.length) {
114
- result.reportPrefix = argv[++i];
115
- }
116
- }
117
- return result;
118
- }
@@ -0,0 +1,17 @@
1
+ // ---------------------------------------------------------------------------
2
+ // v0.3.0 Batch 3 — code-rot detector aggregator.
3
+ //
4
+ // Single import surface for src/audits/core/auditRegistry.ts's
5
+ // DEFAULT_AUDIT_REGISTRY. Keeps the registry file from needing to know each
6
+ // detector's individual file path.
7
+ // ---------------------------------------------------------------------------
8
+ export { STALE_COMMAND_REFERENCE_DETECTOR } from "./detectors/staleCommandReferenceDetector.js";
9
+ export { DOCS_CODE_MISMATCH_DETECTOR } from "./detectors/docsCodeMismatchDetector.js";
10
+ export { PACKAGE_RELEASE_ROT_DETECTOR } from "./detectors/packageReleaseRotDetector.js";
11
+ export { DUPLICATE_IMPLEMENTATION_DETECTOR } from "./detectors/duplicateImplementationDetector.js";
12
+ export { DEAD_CODE_CANDIDATE_DETECTOR } from "./detectors/deadCodeCandidateDetector.js";
13
+ export { TEST_ROT_DETECTOR } from "./detectors/testRotDetector.js";
14
+ export { ARCHITECTURE_DRIFT_DETECTOR } from "./detectors/architectureDriftDetector.js";
15
+ export { DEPENDENCY_ENVIRONMENT_ROT_DETECTOR } from "./detectors/dependencyEnvironmentRotDetector.js";
16
+ export { CROSS_PLATFORM_ROT_DETECTOR } from "./detectors/crossPlatformRotDetector.js";
17
+ export { SECURITY_VALIDATION_ASSUMPTION_ROT_DETECTOR } from "./detectors/securityValidationAssumptionRotDetector.js";