@dailephd/my-dev-kit-lab 0.2.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.
- package/README.md +272 -0
- package/benchmarks/contracts/benchmark-project-profiles.json +1199 -0
- package/benchmarks/contracts/todo-behavior.md +70 -0
- package/benchmarks/contracts/todo-benchmark-case.json +227 -0
- package/benchmarks/projects/README.md +34 -0
- package/benchmarks/projects/task-analytics-large-mixed/README.md +1 -0
- package/benchmarks/projects/task-analytics-large-mixed/py/task_analytics/__init__.py +3 -0
- package/benchmarks/projects/task-analytics-large-mixed/py/task_analytics/fixtures.py +6 -0
- package/benchmarks/projects/task-analytics-large-mixed/py/task_analytics/metrics.py +29 -0
- package/benchmarks/projects/task-analytics-large-mixed/py/task_analytics/models.py +21 -0
- package/benchmarks/projects/task-analytics-large-mixed/py/task_analytics/parser.py +16 -0
- package/benchmarks/projects/task-analytics-large-mixed/py/task_analytics/pipeline.py +9 -0
- package/benchmarks/projects/task-analytics-large-mixed/py/task_analytics/quality.py +8 -0
- package/benchmarks/projects/task-analytics-large-mixed/py/task_analytics/reporting.py +11 -0
- package/benchmarks/projects/task-analytics-large-mixed/py/tests/test_metrics.py +19 -0
- package/benchmarks/projects/task-analytics-large-mixed/py/tests/test_parser.py +15 -0
- package/benchmarks/projects/task-analytics-large-mixed/py/tests/test_quality.py +19 -0
- package/benchmarks/projects/task-analytics-large-mixed/py/tests/test_reporting.py +15 -0
- package/benchmarks/projects/task-analytics-large-mixed/ts/package.json +12 -0
- package/benchmarks/projects/task-analytics-large-mixed/ts/src/index.ts +11 -0
- package/benchmarks/projects/task-analytics-large-mixed/ts/src/models/analyticsSnapshot.ts +20 -0
- package/benchmarks/projects/task-analytics-large-mixed/ts/src/models/project.ts +5 -0
- package/benchmarks/projects/task-analytics-large-mixed/ts/src/models/task.ts +10 -0
- package/benchmarks/projects/task-analytics-large-mixed/ts/src/reporting/buildProjectLeaderboard.ts +7 -0
- package/benchmarks/projects/task-analytics-large-mixed/ts/src/reporting/formatTaskHealthReport.ts +13 -0
- package/benchmarks/projects/task-analytics-large-mixed/ts/src/services/buildAnalyticsSnapshot.ts +39 -0
- package/benchmarks/projects/task-analytics-large-mixed/ts/src/services/completeTask.ts +10 -0
- package/benchmarks/projects/task-analytics-large-mixed/ts/src/services/createTask.ts +21 -0
- package/benchmarks/projects/task-analytics-large-mixed/ts/src/services/listTasksByProject.ts +6 -0
- package/benchmarks/projects/task-analytics-large-mixed/ts/src/store/projectStore.ts +20 -0
- package/benchmarks/projects/task-analytics-large-mixed/ts/src/store/taskStore.ts +44 -0
- package/benchmarks/projects/task-analytics-large-mixed/ts/src/validation/projectValidation.ts +12 -0
- package/benchmarks/projects/task-analytics-large-mixed/ts/src/validation/taskValidation.ts +18 -0
- package/benchmarks/projects/task-analytics-large-mixed/ts/tests/buildAnalyticsSnapshot.test.ts +48 -0
- package/benchmarks/projects/task-analytics-large-mixed/ts/tests/completeTask.test.ts +21 -0
- package/benchmarks/projects/task-analytics-large-mixed/ts/tests/createTask.test.ts +31 -0
- package/benchmarks/projects/task-analytics-large-mixed/ts/tests/listTasksByProject.test.ts +18 -0
- package/benchmarks/projects/task-analytics-large-mixed/ts/tests/reporting.test.ts +19 -0
- package/benchmarks/projects/task-analytics-large-mixed/ts/tsconfig.json +12 -0
- package/benchmarks/projects/task-analytics-large-mixed/ts/vitest.config.ts +5 -0
- package/benchmarks/projects/task-workflow-medium-ts/README.md +1 -0
- package/benchmarks/projects/task-workflow-medium-ts/package.json +12 -0
- package/benchmarks/projects/task-workflow-medium-ts/src/index.ts +9 -0
- package/benchmarks/projects/task-workflow-medium-ts/src/models/project.ts +6 -0
- package/benchmarks/projects/task-workflow-medium-ts/src/models/task.ts +39 -0
- package/benchmarks/projects/task-workflow-medium-ts/src/services/completeTask.ts +15 -0
- package/benchmarks/projects/task-workflow-medium-ts/src/services/createTask.ts +26 -0
- package/benchmarks/projects/task-workflow-medium-ts/src/services/filterTasks.ts +17 -0
- package/benchmarks/projects/task-workflow-medium-ts/src/services/importTasks.ts +33 -0
- package/benchmarks/projects/task-workflow-medium-ts/src/services/summarizeTasks.ts +30 -0
- package/benchmarks/projects/task-workflow-medium-ts/src/store/taskStore.ts +76 -0
- package/benchmarks/projects/task-workflow-medium-ts/src/utils/deterministicId.ts +3 -0
- package/benchmarks/projects/task-workflow-medium-ts/src/validation/taskValidation.ts +45 -0
- package/benchmarks/projects/task-workflow-medium-ts/tests/completeTask.test.ts +16 -0
- package/benchmarks/projects/task-workflow-medium-ts/tests/createTask.test.ts +21 -0
- package/benchmarks/projects/task-workflow-medium-ts/tests/filterTasks.test.ts +18 -0
- package/benchmarks/projects/task-workflow-medium-ts/tests/importTasks.test.ts +22 -0
- package/benchmarks/projects/task-workflow-medium-ts/tests/summarizeTasks.test.ts +29 -0
- package/benchmarks/projects/task-workflow-medium-ts/tsconfig.json +12 -0
- package/benchmarks/projects/task-workflow-medium-ts/vitest.config.ts +5 -0
- package/benchmarks/projects/todo-js/README.md +3 -0
- package/benchmarks/projects/todo-js/package.json +11 -0
- package/benchmarks/projects/todo-js/src/index.js +2 -0
- package/benchmarks/projects/todo-js/src/taskService.js +37 -0
- package/benchmarks/projects/todo-js/src/taskStore.js +28 -0
- package/benchmarks/projects/todo-js/tests/taskService.test.js +45 -0
- package/benchmarks/projects/todo-js/vitest.config.js +5 -0
- package/benchmarks/projects/todo-mixed-ts-py/README.md +3 -0
- package/benchmarks/projects/todo-mixed-ts-py/package.json +13 -0
- package/benchmarks/projects/todo-mixed-ts-py/python/task_service.py +76 -0
- package/benchmarks/projects/todo-mixed-ts-py/src/taskCli.ts +38 -0
- package/benchmarks/projects/todo-mixed-ts-py/tests/mixedBoundary.test.ts +18 -0
- package/benchmarks/projects/todo-mixed-ts-py/tsconfig.json +12 -0
- package/benchmarks/projects/todo-mixed-ts-py/vitest.config.ts +5 -0
- package/benchmarks/projects/todo-python/README.md +3 -0
- package/benchmarks/projects/todo-python/src/__init__.py +4 -0
- package/benchmarks/projects/todo-python/src/task_service.py +32 -0
- package/benchmarks/projects/todo-python/src/task_store.py +28 -0
- package/benchmarks/projects/todo-python/tests/test_task_service.py +52 -0
- package/benchmarks/projects/todo-ts/README.md +3 -0
- package/benchmarks/projects/todo-ts/package.json +12 -0
- package/benchmarks/projects/todo-ts/src/index.ts +2 -0
- package/benchmarks/projects/todo-ts/src/taskService.ts +41 -0
- package/benchmarks/projects/todo-ts/src/taskStore.ts +34 -0
- package/benchmarks/projects/todo-ts/tests/taskService.test.ts +45 -0
- package/benchmarks/projects/todo-ts/tsconfig.json +12 -0
- package/benchmarks/projects/todo-ts/vitest.config.ts +5 -0
- package/dist/scripts/build-gallery.js +3 -0
- package/dist/scripts/capture-demo-report.js +3 -0
- package/dist/scripts/evaluate-token-savings.js +2 -0
- package/dist/scripts/experiments/describeExperiment.js +143 -0
- package/dist/scripts/experiments/listExperiments.js +44 -0
- package/dist/scripts/experiments/runExperiment.js +199 -0
- package/dist/scripts/generate-experiment-plots.js +3 -0
- package/dist/scripts/generate-prompt-variants.js +2 -0
- package/dist/scripts/render-experiment-report.js +2 -0
- package/dist/scripts/run-agent-prompt.js +2 -0
- package/dist/scripts/run-controlled-experiment.js +2 -0
- package/dist/scripts/run-final-demo.js +3 -0
- package/dist/scripts/run-lab-demo.js +5 -0
- package/dist/scripts/run-visualization-demos.js +3 -0
- package/dist/scripts/security/runCodeql.js +57 -0
- package/dist/scripts/security/runDependencyChecks.js +57 -0
- package/dist/scripts/security/runFuzzSmoke.js +29 -0
- package/dist/scripts/security/runPackageChecks.js +56 -0
- package/dist/scripts/security/runSemgrep.js +63 -0
- package/dist/scripts/security/validate.js +117 -0
- package/dist/scripts/verify-benchmarks.js +202 -0
- package/dist/src/agents/adapters/claudeAdapter.js +37 -0
- package/dist/src/agents/adapters/codexAdapter.js +110 -0
- package/dist/src/agents/adapters/fakeAgentAdapter.js +101 -0
- package/dist/src/agents/agentRegistry.js +21 -0
- package/dist/src/agents/index.js +7 -0
- package/dist/src/agents/parseAgentTokenUsage.js +137 -0
- package/dist/src/agents/runAgentPrompt.js +38 -0
- package/dist/src/agents/types.js +1 -0
- package/dist/src/commands/buildGalleryCommand.js +56 -0
- package/dist/src/commands/captureDemoReport.js +116 -0
- package/dist/src/commands/evaluateTokenSavings.js +175 -0
- package/dist/src/commands/generateExperimentPlotsCommand.js +38 -0
- package/dist/src/commands/generatePromptVariants.js +67 -0
- package/dist/src/commands/renderExperimentReportCommand.js +131 -0
- package/dist/src/commands/runAgentPromptCommand.js +132 -0
- package/dist/src/commands/runControlledExperimentCommand.js +174 -0
- package/dist/src/commands/runFinalDemoCommand.js +123 -0
- package/dist/src/commands/runLabDemo.js +62 -0
- package/dist/src/commands/runVisualizationDemosCommand.js +67 -0
- package/dist/src/core/commandLine.js +59 -0
- package/dist/src/core/countTokens.js +8 -0
- package/dist/src/core/fileGlobs.js +100 -0
- package/dist/src/core/localProjectTarget.js +75 -0
- package/dist/src/core/pathSafety.js +19 -0
- package/dist/src/core/pythonCommand.js +30 -0
- package/dist/src/core/resolveCommand.js +110 -0
- package/dist/src/core/runMeasuredCommand.js +143 -0
- package/dist/src/evaluation/benchmarkMetadata.js +207 -0
- package/dist/src/evaluation/buildExperimentMatrix.js +75 -0
- package/dist/src/evaluation/classifyAgentRunOutcome.js +40 -0
- package/dist/src/evaluation/compareExperimentRuns.js +79 -0
- package/dist/src/evaluation/compareTokenSavings.js +47 -0
- package/dist/src/evaluation/controlledExperimentTypes.js +1 -0
- package/dist/src/evaluation/index.js +18 -0
- package/dist/src/evaluation/parseAgentAnswer.js +230 -0
- package/dist/src/evaluation/projectComplexity.js +126 -0
- package/dist/src/evaluation/projectFileTree.js +83 -0
- package/dist/src/evaluation/readEvaluationCases.js +59 -0
- package/dist/src/evaluation/renderTokenSavingsReportInput.js +55 -0
- package/dist/src/evaluation/runControlledExperiment.js +158 -0
- package/dist/src/evaluation/runMyDevKitRetrieval.js +197 -0
- package/dist/src/evaluation/runRawFullFileBaseline.js +31 -0
- package/dist/src/evaluation/scoreCorrectness.js +127 -0
- package/dist/src/evaluation/types.js +1 -0
- package/dist/src/evaluation/writeExperimentArtifacts.js +104 -0
- package/dist/src/evaluation/writeTokenSavingsArtifacts.js +57 -0
- package/dist/src/experiments/config.js +24 -0
- package/dist/src/experiments/defaultRegistry.js +7 -0
- package/dist/src/experiments/errors.js +18 -0
- package/dist/src/experiments/index.js +9 -0
- package/dist/src/experiments/outputPaths.js +25 -0
- package/dist/src/experiments/plugins/contextStrategyComparison/config.js +37 -0
- package/dist/src/experiments/plugins/contextStrategyComparison/index.js +3 -0
- package/dist/src/experiments/plugins/contextStrategyComparison/plugin.js +83 -0
- package/dist/src/experiments/plugins/contextStrategyComparison/resultMapping.js +260 -0
- package/dist/src/experiments/plugins/index.js +1 -0
- package/dist/src/experiments/registry.js +43 -0
- package/dist/src/experiments/results.js +48 -0
- package/dist/src/experiments/runner.js +181 -0
- package/dist/src/experiments/target.js +8 -0
- package/dist/src/experiments/types.js +1 -0
- package/dist/src/gallery/index.js +2 -0
- package/dist/src/gallery/types.js +1 -0
- package/dist/src/gallery/writeGalleryManifest.js +214 -0
- package/dist/src/index.js +12 -0
- package/dist/src/plots/buildExperimentPlotData.js +137 -0
- package/dist/src/plots/index.js +4 -0
- package/dist/src/plots/renderSvgChart.js +82 -0
- package/dist/src/plots/types.js +1 -0
- package/dist/src/plots/writePlotArtifacts.js +46 -0
- package/dist/src/prompts/buildPromptContext.js +68 -0
- package/dist/src/prompts/generateMyDevKitPrompt.js +106 -0
- package/dist/src/prompts/generatePromptVariants.js +36 -0
- package/dist/src/prompts/generateRawFullFilePrompt.js +97 -0
- package/dist/src/prompts/index.js +7 -0
- package/dist/src/prompts/measurePromptComplexity.js +41 -0
- package/dist/src/prompts/types.js +1 -0
- package/dist/src/prompts/writePromptArtifacts.js +43 -0
- package/dist/src/report/buildExperimentReportInput.js +339 -0
- package/dist/src/report/experimentReportTypes.js +1 -0
- package/dist/src/report/experiments/buildPluginExperimentReport.js +153 -0
- package/dist/src/report/experiments/experimentReportModel.js +1 -0
- package/dist/src/report/experiments/index.js +4 -0
- package/dist/src/report/experiments/renderPluginExperimentReportHtml.js +133 -0
- package/dist/src/report/experiments/writePluginExperimentReports.js +30 -0
- package/dist/src/report/index.js +8 -0
- package/dist/src/report/renderExperimentHtmlReport.js +354 -0
- package/dist/src/report/renderHtmlReport.js +103 -0
- package/dist/src/report/types.js +10 -0
- package/dist/src/report/writeExperimentReportArtifacts.js +38 -0
- package/dist/src/report/writeReportArtifacts.js +39 -0
- package/dist/src/screenshot/captureReportScreenshot.js +75 -0
- package/dist/src/screenshot/index.js +2 -0
- package/dist/src/screenshot/types.js +1 -0
- package/dist/src/securityValidation/artifacts.js +15 -0
- package/dist/src/securityValidation/cliAdversarial/adversarialCliConfig.js +38 -0
- package/dist/src/securityValidation/cliAdversarial/dataVolumeChecks.js +194 -0
- package/dist/src/securityValidation/cliAdversarial/jsonStdoutChecks.js +359 -0
- package/dist/src/securityValidation/cliAdversarial/malformedArtifactChecks.js +284 -0
- package/dist/src/securityValidation/cliAdversarial/malformedArtifactFixtures.js +79 -0
- package/dist/src/securityValidation/cliAdversarial/pathBoundaryChecks.js +431 -0
- package/dist/src/securityValidation/cliAdversarial/pathCases.js +144 -0
- package/dist/src/securityValidation/cliAdversarial/readOnlyBoundaryChecks.js +294 -0
- package/dist/src/securityValidation/cliAdversarial/runAdversarialCheck.js +149 -0
- package/dist/src/securityValidation/cliAdversarial/subprocessSafetyChecks.js +214 -0
- package/dist/src/securityValidation/cliAdversarial/tempWorkspace.js +160 -0
- package/dist/src/securityValidation/commandRunner.js +136 -0
- package/dist/src/securityValidation/config.js +39 -0
- package/dist/src/securityValidation/dependencies/parseNpmAudit.js +115 -0
- package/dist/src/securityValidation/dependencies/parseNpmLs.js +71 -0
- package/dist/src/securityValidation/dependencies/parseNpmOutdated.js +41 -0
- package/dist/src/securityValidation/dependencies/runDependencyChecks.js +239 -0
- package/dist/src/securityValidation/dependencies/runOsvScanner.js +43 -0
- package/dist/src/securityValidation/fuzz/fuzzHarness.js +61 -0
- package/dist/src/securityValidation/fuzz/fuzzTargets.js +204 -0
- package/dist/src/securityValidation/fuzz/randomInput.js +0 -0
- package/dist/src/securityValidation/index.js +34 -0
- package/dist/src/securityValidation/packageChecks/forbiddenPackageContents.js +67 -0
- package/dist/src/securityValidation/packageChecks/parseNpmPackDryRun.js +56 -0
- package/dist/src/securityValidation/packageChecks/runPackageChecks.js +88 -0
- package/dist/src/securityValidation/report/renderSecurityReport.js +248 -0
- package/dist/src/securityValidation/report/securityReportTypes.js +1 -0
- package/dist/src/securityValidation/staticScans/codeql.js +66 -0
- package/dist/src/securityValidation/staticScans/semgrep.js +180 -0
- package/dist/src/securityValidation/testMatrix.js +535 -0
- package/dist/src/securityValidation/types.js +34 -0
- package/dist/src/securityValidation/validate/resolveTarget.js +32 -0
- package/dist/src/securityValidation/validate/runSecurityValidation.js +169 -0
- package/dist/src/securityValidation/validate/verdict.js +73 -0
- package/dist/src/visualizationDemos/buildMyDevKitVisualizationCommands.js +59 -0
- package/dist/src/visualizationDemos/index.js +4 -0
- package/dist/src/visualizationDemos/runVisualizationDemos.js +82 -0
- package/dist/src/visualizationDemos/types.js +1 -0
- package/dist/src/visualizationDemos/writeVisualizationDemoArtifacts.js +25 -0
- package/docs/METRICS.md +286 -0
- package/examples/demo-report-input.json +78 -0
- package/examples/lab-demo-cases.json +35 -0
- package/examples/real-agent-campaign-cases.json +118 -0
- package/examples/token-savings-cases.json +122 -0
- package/package.json +91 -0
- package/tests/fixtures/fake-adversarial-cli.js +152 -0
- package/tests/fixtures/fake-my-dev-kit-cli.js +83 -0
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { runSecurityCommand, resolveNpmCommand } from "../commandRunner.js";
|
|
3
|
+
import { writeCheckResult } from "../artifacts.js";
|
|
4
|
+
import { parseNpmAudit } from "./parseNpmAudit.js";
|
|
5
|
+
import { parseNpmLs } from "./parseNpmLs.js";
|
|
6
|
+
import { parseNpmOutdated } from "./parseNpmOutdated.js";
|
|
7
|
+
import { runOsvScanner } from "./runOsvScanner.js";
|
|
8
|
+
// Orchestrate all dependency checks and write structured results to reportDir.
|
|
9
|
+
// npm audit, npm ls, and npm outdated require network/lockfile access;
|
|
10
|
+
// OSV-Scanner is optional and marked skipped if not installed.
|
|
11
|
+
export async function runDependencyChecks(options) {
|
|
12
|
+
const { cwd, config } = options;
|
|
13
|
+
const { reportDir, rawOutputDir, commandTimeoutMs, requireOsvScanner } = config;
|
|
14
|
+
const allFindings = [];
|
|
15
|
+
const checks = [];
|
|
16
|
+
const npm = resolveNpmCommand();
|
|
17
|
+
// npm audit --json (full audit including devDependencies)
|
|
18
|
+
{
|
|
19
|
+
const startedAt = new Date().toISOString();
|
|
20
|
+
const cmd = await runSecurityCommand({
|
|
21
|
+
command: npm,
|
|
22
|
+
args: ["audit", "--json"],
|
|
23
|
+
cwd,
|
|
24
|
+
timeoutMs: commandTimeoutMs,
|
|
25
|
+
});
|
|
26
|
+
const finishedAt = new Date().toISOString();
|
|
27
|
+
const parsed = parseNpmAudit(cmd.stdout, "npm-audit-full");
|
|
28
|
+
const findings = parsed.findings;
|
|
29
|
+
allFindings.push(...findings);
|
|
30
|
+
const check = {
|
|
31
|
+
id: "npm-audit-full",
|
|
32
|
+
name: "npm audit (including devDependencies)",
|
|
33
|
+
category: "dependency-audit",
|
|
34
|
+
status: cmd.timedOut
|
|
35
|
+
? "failed"
|
|
36
|
+
: parsed.parseError
|
|
37
|
+
? "warning"
|
|
38
|
+
: parsed.ok
|
|
39
|
+
? "passed"
|
|
40
|
+
: parsed.severityCounts.critical > 0 || parsed.severityCounts.high > 0
|
|
41
|
+
? "failed"
|
|
42
|
+
: "warning",
|
|
43
|
+
severity: parsed.ok
|
|
44
|
+
? "informational"
|
|
45
|
+
: parsed.severityCounts.critical > 0 || parsed.severityCounts.high > 0
|
|
46
|
+
? "blocker"
|
|
47
|
+
: parsed.severityCounts.moderate > 0
|
|
48
|
+
? "major"
|
|
49
|
+
: "minor",
|
|
50
|
+
startedAt,
|
|
51
|
+
finishedAt,
|
|
52
|
+
durationMs: cmd.durationMs,
|
|
53
|
+
findings,
|
|
54
|
+
skippedReason: undefined,
|
|
55
|
+
command: "npm audit --json",
|
|
56
|
+
};
|
|
57
|
+
await writeCheckResult({
|
|
58
|
+
result: check,
|
|
59
|
+
outputPath: path.join(reportDir, "npm-audit-full.json"),
|
|
60
|
+
rawDir: rawOutputDir,
|
|
61
|
+
rawStdout: cmd.stdout,
|
|
62
|
+
rawStderr: cmd.stderr,
|
|
63
|
+
});
|
|
64
|
+
checks.push(check);
|
|
65
|
+
}
|
|
66
|
+
// npm audit --omit=dev --json (runtime dependencies only)
|
|
67
|
+
{
|
|
68
|
+
const startedAt = new Date().toISOString();
|
|
69
|
+
const cmd = await runSecurityCommand({
|
|
70
|
+
command: npm,
|
|
71
|
+
args: ["audit", "--omit=dev", "--json"],
|
|
72
|
+
cwd,
|
|
73
|
+
timeoutMs: commandTimeoutMs,
|
|
74
|
+
});
|
|
75
|
+
const finishedAt = new Date().toISOString();
|
|
76
|
+
const parsed = parseNpmAudit(cmd.stdout, "npm-audit-runtime");
|
|
77
|
+
const findings = parsed.findings;
|
|
78
|
+
allFindings.push(...findings);
|
|
79
|
+
const check = {
|
|
80
|
+
id: "npm-audit-runtime",
|
|
81
|
+
name: "npm audit --omit=dev (runtime dependencies only)",
|
|
82
|
+
category: "dependency-audit",
|
|
83
|
+
status: cmd.timedOut
|
|
84
|
+
? "failed"
|
|
85
|
+
: parsed.parseError
|
|
86
|
+
? "warning"
|
|
87
|
+
: parsed.ok
|
|
88
|
+
? "passed"
|
|
89
|
+
: parsed.severityCounts.critical > 0 || parsed.severityCounts.high > 0
|
|
90
|
+
? "failed"
|
|
91
|
+
: "warning",
|
|
92
|
+
severity: parsed.ok
|
|
93
|
+
? "informational"
|
|
94
|
+
: parsed.severityCounts.critical > 0 || parsed.severityCounts.high > 0
|
|
95
|
+
? "blocker"
|
|
96
|
+
: parsed.severityCounts.moderate > 0
|
|
97
|
+
? "major"
|
|
98
|
+
: "minor",
|
|
99
|
+
startedAt,
|
|
100
|
+
finishedAt,
|
|
101
|
+
durationMs: cmd.durationMs,
|
|
102
|
+
findings,
|
|
103
|
+
skippedReason: undefined,
|
|
104
|
+
command: "npm audit --omit=dev --json",
|
|
105
|
+
};
|
|
106
|
+
await writeCheckResult({
|
|
107
|
+
result: check,
|
|
108
|
+
outputPath: path.join(reportDir, "npm-audit-runtime.json"),
|
|
109
|
+
rawDir: rawOutputDir,
|
|
110
|
+
rawStdout: cmd.stdout,
|
|
111
|
+
rawStderr: cmd.stderr,
|
|
112
|
+
});
|
|
113
|
+
checks.push(check);
|
|
114
|
+
}
|
|
115
|
+
// npm outdated --json
|
|
116
|
+
{
|
|
117
|
+
const startedAt = new Date().toISOString();
|
|
118
|
+
const cmd = await runSecurityCommand({
|
|
119
|
+
command: npm,
|
|
120
|
+
args: ["outdated", "--json"],
|
|
121
|
+
cwd,
|
|
122
|
+
timeoutMs: commandTimeoutMs,
|
|
123
|
+
});
|
|
124
|
+
const finishedAt = new Date().toISOString();
|
|
125
|
+
const parsed = parseNpmOutdated(cmd.stdout, "npm-outdated");
|
|
126
|
+
const findings = parsed.findings;
|
|
127
|
+
allFindings.push(...findings);
|
|
128
|
+
const check = {
|
|
129
|
+
id: "npm-outdated",
|
|
130
|
+
name: "npm outdated",
|
|
131
|
+
category: "dependency-audit",
|
|
132
|
+
status: cmd.timedOut ? "failed" : parsed.outdatedCount > 0 ? "warning" : "passed",
|
|
133
|
+
severity: "informational",
|
|
134
|
+
startedAt,
|
|
135
|
+
finishedAt,
|
|
136
|
+
durationMs: cmd.durationMs,
|
|
137
|
+
findings,
|
|
138
|
+
skippedReason: undefined,
|
|
139
|
+
command: "npm outdated --json",
|
|
140
|
+
};
|
|
141
|
+
await writeCheckResult({
|
|
142
|
+
result: check,
|
|
143
|
+
outputPath: path.join(reportDir, "npm-outdated.json"),
|
|
144
|
+
rawDir: rawOutputDir,
|
|
145
|
+
rawStdout: cmd.stdout,
|
|
146
|
+
rawStderr: cmd.stderr,
|
|
147
|
+
});
|
|
148
|
+
checks.push(check);
|
|
149
|
+
}
|
|
150
|
+
// npm ls --all --json
|
|
151
|
+
{
|
|
152
|
+
const startedAt = new Date().toISOString();
|
|
153
|
+
const cmd = await runSecurityCommand({
|
|
154
|
+
command: npm,
|
|
155
|
+
args: ["ls", "--all", "--json"],
|
|
156
|
+
cwd,
|
|
157
|
+
timeoutMs: commandTimeoutMs,
|
|
158
|
+
});
|
|
159
|
+
const finishedAt = new Date().toISOString();
|
|
160
|
+
const parsed = parseNpmLs(cmd.stdout, "npm-ls");
|
|
161
|
+
const findings = parsed.findings;
|
|
162
|
+
allFindings.push(...findings);
|
|
163
|
+
const check = {
|
|
164
|
+
id: "npm-ls",
|
|
165
|
+
name: "npm ls --all (dependency tree resolution)",
|
|
166
|
+
category: "dependency-audit",
|
|
167
|
+
status: cmd.timedOut
|
|
168
|
+
? "failed"
|
|
169
|
+
: parsed.parseError
|
|
170
|
+
? "warning"
|
|
171
|
+
: parsed.ok
|
|
172
|
+
? "passed"
|
|
173
|
+
: "warning",
|
|
174
|
+
severity: parsed.missingCount > 0 ? "major" : "informational",
|
|
175
|
+
startedAt,
|
|
176
|
+
finishedAt,
|
|
177
|
+
durationMs: cmd.durationMs,
|
|
178
|
+
findings,
|
|
179
|
+
skippedReason: undefined,
|
|
180
|
+
command: "npm ls --all --json",
|
|
181
|
+
};
|
|
182
|
+
await writeCheckResult({
|
|
183
|
+
result: check,
|
|
184
|
+
outputPath: path.join(reportDir, "npm-ls.json"),
|
|
185
|
+
rawDir: rawOutputDir,
|
|
186
|
+
rawStdout: cmd.stdout,
|
|
187
|
+
rawStderr: cmd.stderr,
|
|
188
|
+
});
|
|
189
|
+
checks.push(check);
|
|
190
|
+
}
|
|
191
|
+
// OSV-Scanner (optional)
|
|
192
|
+
{
|
|
193
|
+
const startedAt = new Date().toISOString();
|
|
194
|
+
const cmd = await runOsvScanner({ cwd, timeoutMs: commandTimeoutMs, requireOsvScanner });
|
|
195
|
+
const finishedAt = new Date().toISOString();
|
|
196
|
+
const check = {
|
|
197
|
+
id: "osv-scanner",
|
|
198
|
+
name: "OSV-Scanner",
|
|
199
|
+
category: "dependency-audit",
|
|
200
|
+
status: cmd.skipped ? "skipped" : cmd.timedOut || cmd.exitCode === null ? "failed" : cmd.exitCode === 0 ? "passed" : "warning",
|
|
201
|
+
severity: cmd.skipped ? "skipped" : "informational",
|
|
202
|
+
startedAt,
|
|
203
|
+
finishedAt,
|
|
204
|
+
durationMs: cmd.durationMs,
|
|
205
|
+
findings: [],
|
|
206
|
+
skippedReason: cmd.skippedReason,
|
|
207
|
+
command: "osv-scanner --lockfile package-lock.json --format json",
|
|
208
|
+
};
|
|
209
|
+
await writeCheckResult({
|
|
210
|
+
result: check,
|
|
211
|
+
outputPath: path.join(reportDir, "osv-scanner.json"),
|
|
212
|
+
rawDir: rawOutputDir,
|
|
213
|
+
rawStdout: cmd.stdout,
|
|
214
|
+
rawStderr: cmd.stderr,
|
|
215
|
+
});
|
|
216
|
+
checks.push(check);
|
|
217
|
+
}
|
|
218
|
+
// Write combined dependency checks summary
|
|
219
|
+
const combined = {
|
|
220
|
+
id: "dependency-checks",
|
|
221
|
+
name: "Dependency checks summary",
|
|
222
|
+
category: "dependency-audit",
|
|
223
|
+
status: checks.some((c) => c.status === "failed") ? "failed" : checks.some((c) => c.status === "warning") ? "warning" : "passed",
|
|
224
|
+
severity: allFindings.reduce((worst, f) => {
|
|
225
|
+
const order = { blocker: 4, major: 3, minor: 2, informational: 1, skipped: 0 };
|
|
226
|
+
return (order[f.severity] ?? 0) > (order[worst] ?? 0) ? f.severity : worst;
|
|
227
|
+
}, "informational"),
|
|
228
|
+
startedAt: checks[0]?.startedAt ?? new Date().toISOString(),
|
|
229
|
+
finishedAt: checks[checks.length - 1]?.finishedAt ?? new Date().toISOString(),
|
|
230
|
+
durationMs: checks.reduce((sum, c) => sum + c.durationMs, 0),
|
|
231
|
+
findings: allFindings,
|
|
232
|
+
};
|
|
233
|
+
await writeCheckResult({
|
|
234
|
+
result: combined,
|
|
235
|
+
outputPath: path.join(reportDir, "dependency-checks.json"),
|
|
236
|
+
rawDir: rawOutputDir,
|
|
237
|
+
});
|
|
238
|
+
return { checks, findings: allFindings };
|
|
239
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
2
|
+
import { runSecurityCommand, skippedResult } from "../commandRunner.js";
|
|
3
|
+
// Detect whether osv-scanner is accessible by probing for its version flag.
|
|
4
|
+
function isOsvScannerAvailable() {
|
|
5
|
+
try {
|
|
6
|
+
const result = spawnSync("osv-scanner", ["--version"], { shell: false, encoding: "utf8" });
|
|
7
|
+
return result.error === undefined && result.status !== null;
|
|
8
|
+
}
|
|
9
|
+
catch {
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
// Run OSV-Scanner on the lockfile if the tool is available.
|
|
14
|
+
// When not available and requireOsvScanner is false, returns a skipped result.
|
|
15
|
+
export async function runOsvScanner(options) {
|
|
16
|
+
if (!isOsvScannerAvailable()) {
|
|
17
|
+
if (options.requireOsvScanner) {
|
|
18
|
+
return {
|
|
19
|
+
command: "osv-scanner",
|
|
20
|
+
args: ["--lockfile", "package-lock.json", "--format", "json"],
|
|
21
|
+
cwd: options.cwd,
|
|
22
|
+
exitCode: null,
|
|
23
|
+
durationMs: 0,
|
|
24
|
+
stdout: "",
|
|
25
|
+
stderr: "osv-scanner not found on PATH",
|
|
26
|
+
timedOut: false,
|
|
27
|
+
skipped: false,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
return skippedResult({
|
|
31
|
+
command: "osv-scanner",
|
|
32
|
+
args: ["--lockfile", "package-lock.json", "--format", "json"],
|
|
33
|
+
cwd: options.cwd,
|
|
34
|
+
reason: "osv-scanner is not installed or not on PATH; check skipped",
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
return runSecurityCommand({
|
|
38
|
+
command: "osv-scanner",
|
|
39
|
+
args: ["--lockfile", "package-lock.json", "--format", "json"],
|
|
40
|
+
cwd: options.cwd,
|
|
41
|
+
timeoutMs: options.timeoutMs,
|
|
42
|
+
});
|
|
43
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { createPrng } from "./randomInput.js";
|
|
2
|
+
export async function runFuzzTarget(target, options = {}) {
|
|
3
|
+
const seed = options.seed ?? 0xdeadbeef;
|
|
4
|
+
const iterations = options.iterations ?? 50;
|
|
5
|
+
const maxCrashes = options.maxCrashesPerTarget ?? 10;
|
|
6
|
+
const prng = createPrng(seed);
|
|
7
|
+
const crashes = [];
|
|
8
|
+
const started = Date.now();
|
|
9
|
+
for (let i = 0; i < iterations && crashes.length < maxCrashes; i++) {
|
|
10
|
+
const input = target.generateInput(prng, i);
|
|
11
|
+
try {
|
|
12
|
+
target.run(input);
|
|
13
|
+
}
|
|
14
|
+
catch (err) {
|
|
15
|
+
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
16
|
+
const stack = err instanceof Error ? err.stack : undefined;
|
|
17
|
+
crashes.push({ iteration: i, input: input.slice(0, 200), errorMessage, stack });
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return {
|
|
21
|
+
targetId: target.id,
|
|
22
|
+
targetName: target.name,
|
|
23
|
+
iterations,
|
|
24
|
+
crashes,
|
|
25
|
+
durationMs: Date.now() - started,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
export async function runAllFuzzTargets(targets, options = {}) {
|
|
29
|
+
const startedAt = new Date().toISOString();
|
|
30
|
+
const started = Date.now();
|
|
31
|
+
const allResults = [];
|
|
32
|
+
for (const target of targets) {
|
|
33
|
+
const result = await runFuzzTarget(target, options);
|
|
34
|
+
allResults.push(result);
|
|
35
|
+
}
|
|
36
|
+
const finishedAt = new Date().toISOString();
|
|
37
|
+
const durationMs = Date.now() - started;
|
|
38
|
+
const allCrashes = allResults.flatMap((r) => r.crashes.map((c) => ({ targetId: r.targetId, targetName: r.targetName, ...c })));
|
|
39
|
+
const findings = allCrashes.map((c) => ({
|
|
40
|
+
id: `fuzz-crash-${c.targetId}-iter-${c.iteration}`,
|
|
41
|
+
title: `Fuzz crash in ${c.targetName} (iteration ${c.iteration})`,
|
|
42
|
+
severity: "major",
|
|
43
|
+
category: "fuzz-smoke",
|
|
44
|
+
description: c.errorMessage,
|
|
45
|
+
evidence: `Input (first 200 chars): ${c.input}${c.stack ? `\nStack: ${c.stack.slice(0, 300)}` : ""}`,
|
|
46
|
+
recommendation: "Investigate and add defensive input validation in the parser.",
|
|
47
|
+
releaseImpact: "Should fix before release",
|
|
48
|
+
}));
|
|
49
|
+
const totalIterations = allResults.reduce((sum, r) => sum + r.iterations, 0);
|
|
50
|
+
return {
|
|
51
|
+
id: "fuzz-smoke",
|
|
52
|
+
name: `Fuzz smoke (${targets.length} targets, ${totalIterations} total iterations)`,
|
|
53
|
+
category: "fuzz-smoke",
|
|
54
|
+
status: findings.length === 0 ? "passed" : "failed",
|
|
55
|
+
severity: findings.length === 0 ? "informational" : "major",
|
|
56
|
+
startedAt,
|
|
57
|
+
finishedAt,
|
|
58
|
+
durationMs,
|
|
59
|
+
findings,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { ALL_MUTATION_STRATEGIES, PATH_TRAVERSAL_INPUTS, mutateJson, randomChoice, randomInt, randomJsonString, randomString, validCodeGraphJson, validManifestJson, } from "./randomInput.js";
|
|
3
|
+
import { parseNpmAudit } from "../dependencies/parseNpmAudit.js";
|
|
4
|
+
import { parseNpmLs } from "../dependencies/parseNpmLs.js";
|
|
5
|
+
import { parseNpmOutdated } from "../dependencies/parseNpmOutdated.js";
|
|
6
|
+
import { parseNpmPackDryRun } from "../packageChecks/parseNpmPackDryRun.js";
|
|
7
|
+
import { escapeDotLabel } from "../cliAdversarial/subprocessSafetyChecks.js";
|
|
8
|
+
// ---------------------------------------------------------------------------
|
|
9
|
+
// Fuzz targets — parsers, helpers, and path normalization
|
|
10
|
+
//
|
|
11
|
+
// Each target must not crash on any input. Expected validation errors are OK.
|
|
12
|
+
// Targets are designed to be pure functions — no filesystem, no network.
|
|
13
|
+
// ---------------------------------------------------------------------------
|
|
14
|
+
// Manifest reader — expects JSON with schemaVersion, generatedAt, rootDir, files
|
|
15
|
+
export const manifestReaderTarget = {
|
|
16
|
+
id: "manifest-reader",
|
|
17
|
+
name: "Manifest JSON parser",
|
|
18
|
+
generateInput(prng, iteration) {
|
|
19
|
+
if (iteration % 3 === 0)
|
|
20
|
+
return validManifestJson();
|
|
21
|
+
const strategy = randomChoice(prng, ALL_MUTATION_STRATEGIES);
|
|
22
|
+
return mutateJson(prng, validManifestJson(), strategy);
|
|
23
|
+
},
|
|
24
|
+
run(input) {
|
|
25
|
+
// The manifest reader is the JSON.parse step that produces structured data.
|
|
26
|
+
// We replicate minimal parsing logic (same shape as what runAdversarialCheck does).
|
|
27
|
+
try {
|
|
28
|
+
JSON.parse(input);
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
return "expected: invalid JSON";
|
|
32
|
+
}
|
|
33
|
+
return undefined;
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
// Code-graph reader — expects JSON with nodes/edges arrays
|
|
37
|
+
export const codeGraphReaderTarget = {
|
|
38
|
+
id: "code-graph-reader",
|
|
39
|
+
name: "Code-graph JSON parser",
|
|
40
|
+
generateInput(prng, iteration) {
|
|
41
|
+
if (iteration % 3 === 0)
|
|
42
|
+
return validCodeGraphJson();
|
|
43
|
+
const strategy = randomChoice(prng, ALL_MUTATION_STRATEGIES);
|
|
44
|
+
return mutateJson(prng, validCodeGraphJson(), strategy);
|
|
45
|
+
},
|
|
46
|
+
run(input) {
|
|
47
|
+
try {
|
|
48
|
+
const parsed = JSON.parse(input);
|
|
49
|
+
if (!Array.isArray(parsed["nodes"]))
|
|
50
|
+
return "expected: missing nodes array";
|
|
51
|
+
if (!Array.isArray(parsed["edges"]))
|
|
52
|
+
return "expected: missing edges array";
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
return "expected: invalid JSON";
|
|
56
|
+
}
|
|
57
|
+
return undefined;
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
// npm audit JSON parser
|
|
61
|
+
export const npmAuditParserTarget = {
|
|
62
|
+
id: "npm-audit-parser",
|
|
63
|
+
name: "npm audit JSON parser",
|
|
64
|
+
generateInput(prng, iteration) {
|
|
65
|
+
if (iteration % 5 === 0) {
|
|
66
|
+
return JSON.stringify({
|
|
67
|
+
auditReportVersion: 2,
|
|
68
|
+
vulnerabilities: {},
|
|
69
|
+
metadata: { vulnerabilities: { total: 0 } },
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
const strategy = randomChoice(prng, ALL_MUTATION_STRATEGIES);
|
|
73
|
+
return mutateJson(prng, randomJsonString(prng, 3), strategy);
|
|
74
|
+
},
|
|
75
|
+
run(input) {
|
|
76
|
+
parseNpmAudit(input, "fuzz-test");
|
|
77
|
+
return undefined;
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
// npm ls JSON parser
|
|
81
|
+
export const npmLsParserTarget = {
|
|
82
|
+
id: "npm-ls-parser",
|
|
83
|
+
name: "npm ls JSON parser",
|
|
84
|
+
generateInput(prng, iteration) {
|
|
85
|
+
if (iteration % 5 === 0) {
|
|
86
|
+
return JSON.stringify({
|
|
87
|
+
name: "my-dev-kit-lab",
|
|
88
|
+
version: "0.1.2",
|
|
89
|
+
dependencies: {},
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
const strategy = randomChoice(prng, ALL_MUTATION_STRATEGIES);
|
|
93
|
+
return mutateJson(prng, randomJsonString(prng, 2), strategy);
|
|
94
|
+
},
|
|
95
|
+
run(input) {
|
|
96
|
+
parseNpmLs(input, "fuzz-test");
|
|
97
|
+
return undefined;
|
|
98
|
+
},
|
|
99
|
+
};
|
|
100
|
+
// npm outdated JSON parser
|
|
101
|
+
export const npmOutdatedParserTarget = {
|
|
102
|
+
id: "npm-outdated-parser",
|
|
103
|
+
name: "npm outdated JSON parser",
|
|
104
|
+
generateInput(prng, iteration) {
|
|
105
|
+
if (iteration % 5 === 0) {
|
|
106
|
+
return JSON.stringify({});
|
|
107
|
+
}
|
|
108
|
+
const strategy = randomChoice(prng, ALL_MUTATION_STRATEGIES);
|
|
109
|
+
return mutateJson(prng, randomJsonString(prng, 2), strategy);
|
|
110
|
+
},
|
|
111
|
+
run(input) {
|
|
112
|
+
parseNpmOutdated(input, "fuzz-test");
|
|
113
|
+
return undefined;
|
|
114
|
+
},
|
|
115
|
+
};
|
|
116
|
+
// npm pack --dry-run output parser
|
|
117
|
+
export const npmPackDryRunParserTarget = {
|
|
118
|
+
id: "npm-pack-dry-run-parser",
|
|
119
|
+
name: "npm pack --dry-run output parser",
|
|
120
|
+
generateInput(prng, iteration) {
|
|
121
|
+
if (iteration % 3 === 0) {
|
|
122
|
+
return JSON.stringify([{
|
|
123
|
+
id: "my-dev-kit-lab@0.1.2",
|
|
124
|
+
name: "my-dev-kit-lab",
|
|
125
|
+
version: "0.1.2",
|
|
126
|
+
filename: "my-dev-kit-lab-0.1.2.tgz",
|
|
127
|
+
files: [],
|
|
128
|
+
entryCount: 0,
|
|
129
|
+
bundled: [],
|
|
130
|
+
}]);
|
|
131
|
+
}
|
|
132
|
+
return randomChoice(prng, ALL_MUTATION_STRATEGIES) === "replace-with-empty"
|
|
133
|
+
? ""
|
|
134
|
+
: randomJsonString(prng, 2);
|
|
135
|
+
},
|
|
136
|
+
run(input) {
|
|
137
|
+
parseNpmPackDryRun(input);
|
|
138
|
+
return undefined;
|
|
139
|
+
},
|
|
140
|
+
};
|
|
141
|
+
// DOT label escaping — must never crash on any input
|
|
142
|
+
export const dotLabelEscapingTarget = {
|
|
143
|
+
id: "dot-label-escaping",
|
|
144
|
+
name: "DOT label escaping",
|
|
145
|
+
generateInput(prng, _iteration) {
|
|
146
|
+
return randomString(prng, 256);
|
|
147
|
+
},
|
|
148
|
+
run(input) {
|
|
149
|
+
escapeDotLabel(input);
|
|
150
|
+
return undefined;
|
|
151
|
+
},
|
|
152
|
+
};
|
|
153
|
+
// Path normalization — path inputs must not produce unexpected crashes
|
|
154
|
+
export const pathNormalizationTarget = {
|
|
155
|
+
id: "path-normalization",
|
|
156
|
+
name: "Path normalization (traversal inputs)",
|
|
157
|
+
generateInput(prng, iteration) {
|
|
158
|
+
if (iteration < PATH_TRAVERSAL_INPUTS.length) {
|
|
159
|
+
return PATH_TRAVERSAL_INPUTS[iteration];
|
|
160
|
+
}
|
|
161
|
+
return randomChoice(prng, PATH_TRAVERSAL_INPUTS);
|
|
162
|
+
},
|
|
163
|
+
run(input) {
|
|
164
|
+
path.normalize(input);
|
|
165
|
+
path.resolve("/base", input);
|
|
166
|
+
return undefined;
|
|
167
|
+
},
|
|
168
|
+
};
|
|
169
|
+
// Source windowing — negative, zero, huge, out-of-range window sizes
|
|
170
|
+
export const sourceWindowingTarget = {
|
|
171
|
+
id: "source-windowing",
|
|
172
|
+
name: "Source retrieval windowing (window size edge cases)",
|
|
173
|
+
generateInput(prng, _iteration) {
|
|
174
|
+
const windowSizes = [-1, 0, 1, 5, 100, 10_000, Number.MAX_SAFE_INTEGER, NaN, Infinity];
|
|
175
|
+
const size = randomChoice(prng, windowSizes);
|
|
176
|
+
return JSON.stringify({ startLine: randomInt(prng, -10, 1000), windowSize: size });
|
|
177
|
+
},
|
|
178
|
+
run(input) {
|
|
179
|
+
try {
|
|
180
|
+
const parsed = JSON.parse(input);
|
|
181
|
+
const startLine = Number(parsed["startLine"]);
|
|
182
|
+
const windowSize = Number(parsed["windowSize"]);
|
|
183
|
+
// Simulate windowing math — must not throw
|
|
184
|
+
const safeStart = Math.max(0, isFinite(startLine) ? startLine : 0);
|
|
185
|
+
const safeWindow = isFinite(windowSize) && windowSize > 0 ? Math.min(windowSize, 10_000) : 50;
|
|
186
|
+
void (safeStart + safeWindow);
|
|
187
|
+
}
|
|
188
|
+
catch {
|
|
189
|
+
return "expected: invalid input";
|
|
190
|
+
}
|
|
191
|
+
return undefined;
|
|
192
|
+
},
|
|
193
|
+
};
|
|
194
|
+
export const ALL_FUZZ_TARGETS = [
|
|
195
|
+
manifestReaderTarget,
|
|
196
|
+
codeGraphReaderTarget,
|
|
197
|
+
npmAuditParserTarget,
|
|
198
|
+
npmLsParserTarget,
|
|
199
|
+
npmOutdatedParserTarget,
|
|
200
|
+
npmPackDryRunParserTarget,
|
|
201
|
+
dotLabelEscapingTarget,
|
|
202
|
+
pathNormalizationTarget,
|
|
203
|
+
sourceWindowingTarget,
|
|
204
|
+
];
|
|
Binary file
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export { SECURITY_SEVERITIES, RELEASE_VERDICTS, SECURITY_CHECK_STATUSES, SECURITY_CHECK_CATEGORIES, } from "./types.js";
|
|
2
|
+
export { DEFAULT_SECURITY_CONFIG } from "./config.js";
|
|
3
|
+
export { SECURITY_TEST_MATRIX } from "./testMatrix.js";
|
|
4
|
+
export { runSecurityCommand, skippedResult, resolveNpmCommand } from "./commandRunner.js";
|
|
5
|
+
export { writeCheckResult } from "./artifacts.js";
|
|
6
|
+
export { parseNpmAudit } from "./dependencies/parseNpmAudit.js";
|
|
7
|
+
export { parseNpmLs } from "./dependencies/parseNpmLs.js";
|
|
8
|
+
export { parseNpmOutdated } from "./dependencies/parseNpmOutdated.js";
|
|
9
|
+
export { runOsvScanner } from "./dependencies/runOsvScanner.js";
|
|
10
|
+
export { runDependencyChecks } from "./dependencies/runDependencyChecks.js";
|
|
11
|
+
export { parseNpmPackDryRun } from "./packageChecks/parseNpmPackDryRun.js";
|
|
12
|
+
export { detectForbiddenContents } from "./packageChecks/forbiddenPackageContents.js";
|
|
13
|
+
export { runPackageChecks } from "./packageChecks/runPackageChecks.js";
|
|
14
|
+
export { createTempWorkspace, snapshotDir, diffSnapshots, findWritesOutside, findNewFiles } from "./cliAdversarial/tempWorkspace.js";
|
|
15
|
+
export { getAdversarialCliTarget, buildCliCommand } from "./cliAdversarial/adversarialCliConfig.js";
|
|
16
|
+
export { ALL_PATH_TEST_INPUTS, PATH_TRAVERSAL_CASES, ABSOLUTE_PATH_CASES, SPACES_PATH_CASES, METACHAR_PATH_CASES, UNICODE_PATH_CASES, LONG_NAME_CASES, MISSING_PATH_CASES } from "./cliAdversarial/pathCases.js";
|
|
17
|
+
export { runAdversarialCheck, skippedCheck, makeFinding } from "./cliAdversarial/runAdversarialCheck.js";
|
|
18
|
+
export { checkRootPathTraversal, checkOutPathTraversal, checkIndexPathTraversal, checkPathWithSpaces, checkUnicodePath, checkSafeAbsolutePath, checkHarnessEscapeDetection } from "./cliAdversarial/pathBoundaryChecks.js";
|
|
19
|
+
export { checkSourceFilesNotModified, checkWritesLimitedToOutput, checkIndexWriteContainment, checkArtifactCleanupSafe } from "./cliAdversarial/readOnlyBoundaryChecks.js";
|
|
20
|
+
export { MALFORMED_MANIFEST_CASES, MALFORMED_CODE_GRAPH_CASES, UNSUPPORTED_SCHEMA_VERSION_CASES, placeMalformedArtifact, placeMalformedManifest, placeMalformedCodeGraph, placeUnsupportedSchemaManifest } from "./cliAdversarial/malformedArtifactFixtures.js";
|
|
21
|
+
export { checkMalformedManifest, checkAllMalformedManifestCases, checkMalformedCodeGraph, checkUnsupportedSchemaVersion, checkMissingIndexDirectory } from "./cliAdversarial/malformedArtifactChecks.js";
|
|
22
|
+
export { checkJsonOutputIsParseable, checkStderrNotInStdout, checkFailureProducesJsonError, checkProgressNotInJsonStdout } from "./cliAdversarial/jsonStdoutChecks.js";
|
|
23
|
+
export { DOT_LABEL_TEST_CASES, escapeDotLabel, checkSubprocessNoShellInterpolation, checkDotLabelEscaping } from "./cliAdversarial/subprocessSafetyChecks.js";
|
|
24
|
+
export { checkHugeSourceFile, checkManyFiles, checkDeeplyNestedSource } from "./cliAdversarial/dataVolumeChecks.js";
|
|
25
|
+
export { runCodeqlCheck } from "./staticScans/codeql.js";
|
|
26
|
+
export { runSemgrepCheck, parseSemgrepJson } from "./staticScans/semgrep.js";
|
|
27
|
+
export { createPrng, randomInt, randomChoice, randomString, randomJsonValue, randomJsonString, mutateJson, validManifestJson, validCodeGraphJson } from "./fuzz/randomInput.js";
|
|
28
|
+
export { ALL_MUTATION_STRATEGIES, PATH_TRAVERSAL_INPUTS } from "./fuzz/randomInput.js";
|
|
29
|
+
export { runFuzzTarget, runAllFuzzTargets } from "./fuzz/fuzzHarness.js";
|
|
30
|
+
export { ALL_FUZZ_TARGETS, manifestReaderTarget, codeGraphReaderTarget, dotLabelEscapingTarget, pathNormalizationTarget, sourceWindowingTarget } from "./fuzz/fuzzTargets.js";
|
|
31
|
+
export { calculateVerdict, verdictToHumanLabel } from "./validate/verdict.js";
|
|
32
|
+
export { resolveValidationTarget, reportFilenamePrefix, targetDescription } from "./validate/resolveTarget.js";
|
|
33
|
+
export { runSecurityValidation } from "./validate/runSecurityValidation.js";
|
|
34
|
+
export { renderTextReport, renderJsonReport } from "./report/renderSecurityReport.js";
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
// Normalize a tarball path for pattern matching.
|
|
2
|
+
// npm pack prefixes files with "package/", which we strip.
|
|
3
|
+
function normalizeTarballPath(p) {
|
|
4
|
+
const normalized = p.replace(/\\/g, "/");
|
|
5
|
+
return normalized.startsWith("package/") ? normalized.slice("package/".length) : normalized;
|
|
6
|
+
}
|
|
7
|
+
// Check whether a file path matches a forbidden pattern.
|
|
8
|
+
// Patterns ending with "/" match directory prefixes.
|
|
9
|
+
// Patterns starting with "*" are suffix matches.
|
|
10
|
+
// Exact string matches are also supported.
|
|
11
|
+
function matchesForbiddenPattern(normalizedPath, pattern) {
|
|
12
|
+
const p = pattern.toLowerCase();
|
|
13
|
+
const f = normalizedPath.toLowerCase();
|
|
14
|
+
if (p.endsWith("/")) {
|
|
15
|
+
return f.startsWith(p) || f === p.slice(0, -1);
|
|
16
|
+
}
|
|
17
|
+
if (p.startsWith("*.")) {
|
|
18
|
+
return f.endsWith(p.slice(1));
|
|
19
|
+
}
|
|
20
|
+
if (p.includes("*")) {
|
|
21
|
+
// Simple glob: split on * and check prefix/suffix
|
|
22
|
+
const parts = p.split("*").filter(Boolean);
|
|
23
|
+
if (parts.length === 1)
|
|
24
|
+
return f.includes(parts[0]);
|
|
25
|
+
return f.startsWith(parts[0]) && f.endsWith(parts[parts.length - 1]);
|
|
26
|
+
}
|
|
27
|
+
return f === p || f.startsWith(p + "/");
|
|
28
|
+
}
|
|
29
|
+
// Detect files in the tarball file list that match forbidden patterns.
|
|
30
|
+
export function detectForbiddenContents(options) {
|
|
31
|
+
const matches = [];
|
|
32
|
+
for (const rawFile of options.files) {
|
|
33
|
+
const normalized = normalizeTarballPath(rawFile);
|
|
34
|
+
const isException = options.allowedExceptions.some((ex) => matchesForbiddenPattern(normalized, ex));
|
|
35
|
+
if (isException)
|
|
36
|
+
continue;
|
|
37
|
+
for (const pattern of options.forbiddenPatterns) {
|
|
38
|
+
if (matchesForbiddenPattern(normalized, pattern)) {
|
|
39
|
+
matches.push({ file: normalized, pattern });
|
|
40
|
+
break;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
const findings = matches.map((m, i) => ({
|
|
45
|
+
id: `${options.checkId}-forbidden-${i}`,
|
|
46
|
+
title: `Forbidden file in npm tarball: ${m.file}`,
|
|
47
|
+
severity: isCriticalForbidden(m.file) ? "blocker" : "major",
|
|
48
|
+
category: "package-content",
|
|
49
|
+
description: `The file '${m.file}' matches the forbidden pattern '${m.pattern}' and must not be included in the published package`,
|
|
50
|
+
evidence: `File: ${m.file}, Matched pattern: ${m.pattern}`,
|
|
51
|
+
affectedFiles: [m.file],
|
|
52
|
+
recommendation: `Add '${m.file}' to the .npmignore file or remove it from the 'files' field in package.json`,
|
|
53
|
+
releaseImpact: isCriticalForbidden(m.file)
|
|
54
|
+
? "Blocker: this file must not be published"
|
|
55
|
+
: "Major: this file should not be in the published package",
|
|
56
|
+
}));
|
|
57
|
+
return { matches, findings };
|
|
58
|
+
}
|
|
59
|
+
function isCriticalForbidden(file) {
|
|
60
|
+
const f = file.toLowerCase();
|
|
61
|
+
return (f.includes(".env") ||
|
|
62
|
+
f.endsWith(".pem") ||
|
|
63
|
+
f.endsWith(".key") ||
|
|
64
|
+
f.endsWith(".p12") ||
|
|
65
|
+
f.startsWith("lab-output/") ||
|
|
66
|
+
f.startsWith(".my-dev-kit/"));
|
|
67
|
+
}
|