@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.
- package/LICENSE +21 -0
- package/README.md +36 -24
- package/dist/scripts/audits/runAudit.js +101 -0
- package/dist/scripts/security/validate.js +68 -29
- package/dist/src/audits/codeRot/codeRotDetectors.js +17 -0
- package/dist/src/audits/codeRot/detectors/architectureDriftDetector.js +271 -0
- package/dist/src/audits/codeRot/detectors/crossPlatformRotDetector.js +214 -0
- package/dist/src/audits/codeRot/detectors/deadCodeCandidateDetector.js +344 -0
- package/dist/src/audits/codeRot/detectors/dependencyEnvironmentRotDetector.js +244 -0
- package/dist/src/audits/codeRot/detectors/docsCodeMismatchDetector.js +219 -0
- package/dist/src/audits/codeRot/detectors/duplicateImplementationDetector.js +273 -0
- package/dist/src/audits/codeRot/detectors/packageReleaseRotDetector.js +325 -0
- package/dist/src/audits/codeRot/detectors/securityValidationAssumptionRotDetector.js +385 -0
- package/dist/src/audits/codeRot/detectors/staleCommandReferenceDetector.js +135 -0
- package/dist/src/audits/codeRot/detectors/testRotDetector.js +348 -0
- package/dist/src/audits/codeRot/utils/boundedRead.js +23 -0
- package/dist/src/audits/codeRot/utils/docClaimPatterns.js +62 -0
- package/dist/src/audits/codeRot/utils/docCommandReferences.js +64 -0
- package/dist/src/audits/codeRot/utils/filePatternUtils.js +47 -0
- package/dist/src/audits/codeRot/utils/issueFactories.js +38 -0
- package/dist/src/audits/codeRot/utils/textLines.js +12 -0
- package/dist/src/audits/core/auditConfig.js +125 -0
- package/dist/src/audits/core/auditExitCode.js +75 -0
- package/dist/src/audits/core/auditIssue.js +20 -0
- package/dist/src/audits/core/auditRegistry.js +40 -0
- package/dist/src/audits/core/auditRunner.js +71 -0
- package/dist/src/audits/core/auditTarget.js +19 -0
- package/dist/src/audits/core/auditTypes.js +30 -0
- package/dist/src/audits/core/projectInventory.js +308 -0
- package/dist/src/audits/core/sourceOfTruth.js +339 -0
- package/dist/src/audits/core/textLines.js +24 -0
- package/dist/src/audits/report/auditReportModel.js +241 -0
- package/dist/src/audits/report/auditReportPaths.js +13 -0
- package/dist/src/audits/report/renderAuditJsonReport.js +29 -0
- package/dist/src/audits/report/renderAuditTextReport.js +165 -0
- package/dist/src/audits/report/sanitizeAuditText.js +39 -0
- package/dist/src/audits/report/writeAuditReports.js +27 -0
- package/dist/src/core/resolveCommand.js +53 -2
- package/dist/src/securityValidation/attackScenarios/attackProfile.js +20 -0
- package/dist/src/securityValidation/attackScenarios/attackResult.js +37 -0
- package/dist/src/securityValidation/attackScenarios/attackRunner.js +153 -0
- package/dist/src/securityValidation/attackScenarios/attackScenario.js +21 -0
- package/dist/src/securityValidation/attackScenarios/boundedSourceScan.js +100 -0
- package/dist/src/securityValidation/attackScenarios/exploitEvidence.js +69 -0
- package/dist/src/securityValidation/attackScenarios/payloadCorpus.js +0 -0
- package/dist/src/securityValidation/attackScenarios/profiles/localToolProfile.js +10 -0
- package/dist/src/securityValidation/attackScenarios/profiles/nodeCliPackageProfile.js +20 -0
- package/dist/src/securityValidation/attackScenarios/profiles/npmPackageProfile.js +10 -0
- package/dist/src/securityValidation/attackScenarios/reportSchemaGuard.js +41 -0
- package/dist/src/securityValidation/attackScenarios/scenarios/configInjectionScenario.js +144 -0
- package/dist/src/securityValidation/attackScenarios/scenarios/networkAssumptionScenario.js +129 -0
- package/dist/src/securityValidation/attackScenarios/scenarios/outputBoundaryScenario.js +69 -0
- package/dist/src/securityValidation/attackScenarios/scenarios/packageBoundaryScenario.js +118 -0
- package/dist/src/securityValidation/attackScenarios/scenarios/pathTraversalScenario.js +119 -0
- package/dist/src/securityValidation/attackScenarios/scenarios/reportPoisoningScenario.js +176 -0
- package/dist/src/securityValidation/attackScenarios/scenarios/secretLeakageScenario.js +143 -0
- package/dist/src/securityValidation/attackScenarios/scenarios/subprocessInjectionScenario.js +117 -0
- package/dist/src/securityValidation/attackScenarios/scenarios/targetSandboxScenario.js +118 -0
- package/dist/src/securityValidation/attackScenarios/targetSnapshot.js +70 -0
- package/dist/src/securityValidation/packageChecks/forbiddenPackageContents.js +4 -1
- package/dist/src/securityValidation/report/renderSecurityReport.js +135 -13
- package/dist/src/securityValidation/types.js +7 -0
- package/dist/src/securityValidation/validate/cliOptions.js +182 -0
- package/dist/src/securityValidation/validate/runSecurityValidation.js +134 -35
- package/dist/src/securityValidation/validate/verdict.js +147 -0
- package/package.json +18 -17
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { VERDICT_IMPACTS } from "../types.js";
|
|
1
2
|
// ---------------------------------------------------------------------------
|
|
2
3
|
// Verdict calculation
|
|
3
4
|
//
|
|
@@ -71,3 +72,149 @@ export function verdictToHumanLabel(verdict) {
|
|
|
71
72
|
return "inconclusive: audit environment incomplete";
|
|
72
73
|
}
|
|
73
74
|
}
|
|
75
|
+
// ---------------------------------------------------------------------------
|
|
76
|
+
// v0.2.2 Batch 5 — fail-on severity threshold
|
|
77
|
+
//
|
|
78
|
+
// --fail-on uses a separate vocabulary (blocker/high/medium/low) from
|
|
79
|
+
// SecuritySeverity (blocker/major/minor/informational/skipped) — Batch 1
|
|
80
|
+
// deliberately kept these distinct. This maps one onto the other so a
|
|
81
|
+
// --fail-on breach can be detected without renaming any existing severity.
|
|
82
|
+
// Mapping (conservative — never understates a blocker-level finding):
|
|
83
|
+
// blocker fail-on -> only "blocker" severity findings breach it
|
|
84
|
+
// high fail-on -> "blocker" or "major" ("major" is treated as "high")
|
|
85
|
+
// medium fail-on -> "blocker", "major", or "minor" ("minor" -> "medium")
|
|
86
|
+
// low fail-on -> "blocker", "major", "minor", or "informational"
|
|
87
|
+
// "skipped" severity never breaches any threshold — a skipped optional tool
|
|
88
|
+
// is not a finding.
|
|
89
|
+
// ---------------------------------------------------------------------------
|
|
90
|
+
const SEVERITY_THRESHOLD_RANK = {
|
|
91
|
+
blocker: 4,
|
|
92
|
+
major: 3,
|
|
93
|
+
minor: 2,
|
|
94
|
+
informational: 1,
|
|
95
|
+
skipped: 0,
|
|
96
|
+
};
|
|
97
|
+
const FAIL_ON_THRESHOLD_RANK = {
|
|
98
|
+
blocker: 4,
|
|
99
|
+
high: 3,
|
|
100
|
+
medium: 2,
|
|
101
|
+
low: 1,
|
|
102
|
+
};
|
|
103
|
+
export function severityMeetsFailOnThreshold(severity, threshold) {
|
|
104
|
+
return SEVERITY_THRESHOLD_RANK[severity] >= FAIL_ON_THRESHOLD_RANK[threshold];
|
|
105
|
+
}
|
|
106
|
+
// True when at least one finding's severity breaches the given --fail-on
|
|
107
|
+
// threshold. Callers should OR this with the existing verdict-based exit
|
|
108
|
+
// decision, never use it to replace/weaken that decision — this only adds
|
|
109
|
+
// stricter exit-1 triggers for --fail-on medium/low (which the default
|
|
110
|
+
// verdict policy doesn't already cover via its blocker/major rules); for
|
|
111
|
+
// --fail-on blocker/high it is always a no-op given the existing policy.
|
|
112
|
+
export function findingsBreachFailOnThreshold(findings, threshold) {
|
|
113
|
+
return findings.some((f) => severityMeetsFailOnThreshold(f.severity, threshold));
|
|
114
|
+
}
|
|
115
|
+
export const VERDICT_REASON_CATEGORIES = [
|
|
116
|
+
"release-blocker",
|
|
117
|
+
"target-project-blocker",
|
|
118
|
+
"tool-framework-blocker",
|
|
119
|
+
"environment-inconclusive",
|
|
120
|
+
"optional-skipped-tool",
|
|
121
|
+
"adversarial-scenario-skipped",
|
|
122
|
+
"scanner-finding",
|
|
123
|
+
"adversarial-scenario-failure",
|
|
124
|
+
"required-evidence-missing",
|
|
125
|
+
"informational-evidence",
|
|
126
|
+
];
|
|
127
|
+
// v0.2.2 Batch 6: scenario blocker-impact classification now comes directly
|
|
128
|
+
// from SecurityCheckResult.verdictImpact (carried through from the
|
|
129
|
+
// originating AttackScenario's static declaration — see attackScenario.ts /
|
|
130
|
+
// attackResult.ts). verdict.ts no longer owns a separate scenario-id map, so
|
|
131
|
+
// there is nothing to keep in sync when a new scenario is registered — as
|
|
132
|
+
// long as the scenario declares verdictImpact, categorization is correct
|
|
133
|
+
// automatically. Checks with no verdictImpact but a scenario-shaped id
|
|
134
|
+
// (placeholder/error results, or a scenario that doesn't declare one) fall
|
|
135
|
+
// back to the generic "adversarial-scenario-failure"/"-skipped" buckets.
|
|
136
|
+
//
|
|
137
|
+
// A check is "scenario-shaped" if it carries a verdictImpact OR its id
|
|
138
|
+
// matches the synthetic placeholder/error patterns produced by
|
|
139
|
+
// attackRunner.ts for unregistered checks or runner crashes (those never
|
|
140
|
+
// carry verdictImpact since there's no real AttackScenario object).
|
|
141
|
+
function isAttackScenarioCheckId(id) {
|
|
142
|
+
return id.endsWith("-no-scenarios-registered") || id.endsWith("-attack-runner-error");
|
|
143
|
+
}
|
|
144
|
+
// Categorizes a single check for verdict-reasoning purposes. Returns null
|
|
145
|
+
// for passed/warning checks — those don't need a blocker-reasoning bucket.
|
|
146
|
+
export function categorizeCheck(check) {
|
|
147
|
+
const scenario = isAttackScenarioCheckId(check.id) || check.verdictImpact !== undefined;
|
|
148
|
+
if (check.status === "skipped") {
|
|
149
|
+
if (scenario)
|
|
150
|
+
return "adversarial-scenario-skipped";
|
|
151
|
+
if (MANDATORY_CHECK_IDS.has(check.id))
|
|
152
|
+
return "required-evidence-missing";
|
|
153
|
+
return "optional-skipped-tool";
|
|
154
|
+
}
|
|
155
|
+
if (check.status === "failed") {
|
|
156
|
+
if (check.verdictImpact && VERDICT_IMPACTS.includes(check.verdictImpact)) {
|
|
157
|
+
return check.verdictImpact;
|
|
158
|
+
}
|
|
159
|
+
if (scenario)
|
|
160
|
+
return "adversarial-scenario-failure";
|
|
161
|
+
if (check.severity === "informational")
|
|
162
|
+
return "informational-evidence";
|
|
163
|
+
return "scanner-finding";
|
|
164
|
+
}
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
// Aggregates categorizeCheck() across all checks, plus a direct informational
|
|
168
|
+
// finding count (informational findings don't get their own check-level
|
|
169
|
+
// bucket since a check can carry multiple findings of mixed severity).
|
|
170
|
+
export function summarizeVerdictReasoning(checks, findings) {
|
|
171
|
+
const summary = {
|
|
172
|
+
releaseBlockerCount: 0,
|
|
173
|
+
targetProjectBlockerCount: 0,
|
|
174
|
+
toolFrameworkBlockerCount: 0,
|
|
175
|
+
environmentInconclusiveCount: 0,
|
|
176
|
+
optionalSkippedToolCount: 0,
|
|
177
|
+
adversarialScenarioSkippedCount: 0,
|
|
178
|
+
scannerFindingCount: 0,
|
|
179
|
+
adversarialScenarioFailureCount: 0,
|
|
180
|
+
requiredEvidenceMissingCount: 0,
|
|
181
|
+
informationalEvidenceCount: 0,
|
|
182
|
+
};
|
|
183
|
+
for (const check of checks) {
|
|
184
|
+
const category = categorizeCheck(check);
|
|
185
|
+
switch (category) {
|
|
186
|
+
case "release-blocker":
|
|
187
|
+
summary.releaseBlockerCount++;
|
|
188
|
+
break;
|
|
189
|
+
case "target-project-blocker":
|
|
190
|
+
summary.targetProjectBlockerCount++;
|
|
191
|
+
break;
|
|
192
|
+
case "tool-framework-blocker":
|
|
193
|
+
summary.toolFrameworkBlockerCount++;
|
|
194
|
+
break;
|
|
195
|
+
case "environment-inconclusive":
|
|
196
|
+
summary.environmentInconclusiveCount++;
|
|
197
|
+
break;
|
|
198
|
+
case "optional-skipped-tool":
|
|
199
|
+
summary.optionalSkippedToolCount++;
|
|
200
|
+
break;
|
|
201
|
+
case "adversarial-scenario-skipped":
|
|
202
|
+
summary.adversarialScenarioSkippedCount++;
|
|
203
|
+
break;
|
|
204
|
+
case "scanner-finding":
|
|
205
|
+
summary.scannerFindingCount++;
|
|
206
|
+
break;
|
|
207
|
+
case "adversarial-scenario-failure":
|
|
208
|
+
summary.adversarialScenarioFailureCount++;
|
|
209
|
+
break;
|
|
210
|
+
case "required-evidence-missing":
|
|
211
|
+
summary.requiredEvidenceMissingCount++;
|
|
212
|
+
break;
|
|
213
|
+
case "informational-evidence":
|
|
214
|
+
case null:
|
|
215
|
+
break;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
summary.informationalEvidenceCount = findings.filter((f) => f.severity === "informational").length;
|
|
219
|
+
return summary;
|
|
220
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dailephd/my-dev-kit-lab",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Evidence, benchmark, and evaluation companion for my-dev-kit.",
|
|
6
6
|
"bin": {
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"claude"
|
|
39
39
|
],
|
|
40
40
|
"author": "Dai Le",
|
|
41
|
-
"license": "
|
|
41
|
+
"license": "MIT",
|
|
42
42
|
"scripts": {
|
|
43
43
|
"build": "node scripts/clean-dist.mjs && tsc -p tsconfig.json",
|
|
44
44
|
"typecheck": "tsc --noEmit",
|
|
@@ -77,19 +77,20 @@
|
|
|
77
77
|
"security:semgrep": "tsx scripts/security/runSemgrep.ts",
|
|
78
78
|
"test:security": "vitest run tests/security/",
|
|
79
79
|
"test:fuzz:smoke": "tsx scripts/security/runFuzzSmoke.ts",
|
|
80
|
-
"security:validate": "tsx scripts/security/validate.ts"
|
|
80
|
+
"security:validate": "tsx scripts/security/validate.ts",
|
|
81
|
+
"audit": "tsx scripts/audits/runAudit.ts"
|
|
81
82
|
},
|
|
82
|
-
"devDependencies": {
|
|
83
|
-
"@types/node": "^24.0.0",
|
|
84
|
-
"playwright": "^1.54.1",
|
|
85
|
-
"tsx": "^4.20.3",
|
|
86
|
-
"typescript": "^5.8.3",
|
|
87
|
-
"vitest": "^3.2.4"
|
|
88
|
-
},
|
|
89
|
-
"overrides": {
|
|
90
|
-
"vite": "7.3.6"
|
|
91
|
-
},
|
|
92
|
-
"engines": {
|
|
93
|
-
"node": ">=20"
|
|
94
|
-
}
|
|
95
|
-
}
|
|
83
|
+
"devDependencies": {
|
|
84
|
+
"@types/node": "^24.0.0",
|
|
85
|
+
"playwright": "^1.54.1",
|
|
86
|
+
"tsx": "^4.20.3",
|
|
87
|
+
"typescript": "^5.8.3",
|
|
88
|
+
"vitest": "^3.2.4"
|
|
89
|
+
},
|
|
90
|
+
"overrides": {
|
|
91
|
+
"vite": "7.3.6"
|
|
92
|
+
},
|
|
93
|
+
"engines": {
|
|
94
|
+
"node": ">=20"
|
|
95
|
+
}
|
|
96
|
+
}
|