@dailephd/my-dev-kit-lab 0.2.1 → 0.2.2
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 +35 -24
- package/dist/scripts/security/validate.js +68 -29
- 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 +16 -16
|
@@ -6,10 +6,30 @@ import { runCodeqlCheck } from "../staticScans/codeql.js";
|
|
|
6
6
|
import { runSemgrepCheck } from "../staticScans/semgrep.js";
|
|
7
7
|
import { runAllFuzzTargets } from "../fuzz/fuzzHarness.js";
|
|
8
8
|
import { ALL_FUZZ_TARGETS } from "../fuzz/fuzzTargets.js";
|
|
9
|
-
import { calculateVerdict } from "./verdict.js";
|
|
9
|
+
import { calculateVerdict, summarizeVerdictReasoning } from "./verdict.js";
|
|
10
10
|
import { resolveValidationTarget, targetDescription as describeTarget } from "./resolveTarget.js";
|
|
11
11
|
import { DEFAULT_SECURITY_CONFIG } from "../config.js";
|
|
12
12
|
import { runCliSecuritySuiteCheck } from "./runCliSecuritySuiteCheck.js";
|
|
13
|
+
import { DEFAULT_SECURITY_CHECKS, DEFAULT_SECURITY_PROFILE, IMPLEMENTED_SECURITY_CHECK_IDS, PLANNED_SECURITY_CHECK_IDS, } from "./cliOptions.js";
|
|
14
|
+
import { runAttackScenarios } from "../attackScenarios/attackRunner.js";
|
|
15
|
+
import { toSecurityCheckResult } from "../attackScenarios/attackResult.js";
|
|
16
|
+
import { captureTargetSnapshot } from "../attackScenarios/targetSnapshot.js";
|
|
17
|
+
const NOT_SELECTED_REASON = "Not selected via --checks.";
|
|
18
|
+
function skippedCheck(id, name, category, skippedReason) {
|
|
19
|
+
const now = new Date().toISOString();
|
|
20
|
+
return {
|
|
21
|
+
id,
|
|
22
|
+
name,
|
|
23
|
+
category,
|
|
24
|
+
status: "skipped",
|
|
25
|
+
severity: "skipped",
|
|
26
|
+
startedAt: now,
|
|
27
|
+
finishedAt: now,
|
|
28
|
+
durationMs: 0,
|
|
29
|
+
findings: [],
|
|
30
|
+
skippedReason,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
13
33
|
export async function runSecurityValidation(options) {
|
|
14
34
|
const toolRoot = options.cwd;
|
|
15
35
|
const config = {
|
|
@@ -20,9 +40,15 @@ export async function runSecurityValidation(options) {
|
|
|
20
40
|
};
|
|
21
41
|
// Resolve and validate the target project
|
|
22
42
|
const target = resolveValidationTarget(options.targetPath, toolRoot);
|
|
43
|
+
// v0.2.2 Batch 3: read-only git-status snapshot taken before any checks run,
|
|
44
|
+
// so the target-sandbox attack scenario (which runs last, among the
|
|
45
|
+
// attack-scenario checks) can distinguish pre-existing target dirtiness
|
|
46
|
+
// from changes caused by this validation run.
|
|
47
|
+
const targetSnapshotBefore = captureTargetSnapshot(target.targetRoot, target.hasGit);
|
|
23
48
|
const startedAt = new Date().toISOString();
|
|
24
49
|
const allChecks = [];
|
|
25
50
|
const allFindings = [];
|
|
51
|
+
const selectedChecks = new Set(options.selectedChecks ?? DEFAULT_SECURITY_CHECKS);
|
|
26
52
|
function collect(check) {
|
|
27
53
|
allChecks.push(check);
|
|
28
54
|
allFindings.push(...check.findings);
|
|
@@ -34,57 +60,122 @@ export async function runSecurityValidation(options) {
|
|
|
34
60
|
// --- Dependency checks (runs in target root) ---
|
|
35
61
|
// If target has no package.json/lockfile, runDependencyChecks will surface that
|
|
36
62
|
// as a structured finding rather than crashing.
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
63
|
+
if (selectedChecks.has("deps")) {
|
|
64
|
+
try {
|
|
65
|
+
const depOutput = await runDependencyChecks({ cwd: target.targetRoot, config });
|
|
66
|
+
collectMany(depOutput.checks);
|
|
67
|
+
}
|
|
68
|
+
catch (err) {
|
|
69
|
+
collect(errorCheck("dependency-checks", "Dependency checks", "dependency-audit", err));
|
|
70
|
+
}
|
|
40
71
|
}
|
|
41
|
-
|
|
42
|
-
|
|
72
|
+
else {
|
|
73
|
+
// Preserve the mandatory-check IDs verdict.ts relies on so deselecting this
|
|
74
|
+
// group still shows up as a skipped mandatory check, not a silent gap.
|
|
75
|
+
collect(skippedCheck("npm-audit-full", "npm audit (full)", "dependency-audit", NOT_SELECTED_REASON));
|
|
76
|
+
collect(skippedCheck("npm-audit-runtime", "npm audit (runtime)", "dependency-audit", NOT_SELECTED_REASON));
|
|
43
77
|
}
|
|
44
78
|
// --- Package content checks (runs in target root) ---
|
|
45
79
|
// Not all projects are publishable npm packages; runPackageChecks handles
|
|
46
80
|
// missing package.json gracefully.
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
81
|
+
if (selectedChecks.has("package")) {
|
|
82
|
+
try {
|
|
83
|
+
const pkgOutput = await runPackageChecks({ cwd: target.targetRoot, config });
|
|
84
|
+
collectMany(pkgOutput.checks);
|
|
85
|
+
}
|
|
86
|
+
catch (err) {
|
|
87
|
+
collect(errorCheck("package-content", "Package content checks", "package-content", err));
|
|
88
|
+
}
|
|
50
89
|
}
|
|
51
|
-
|
|
52
|
-
collect(
|
|
90
|
+
else {
|
|
91
|
+
collect(skippedCheck("npm-pack-dry-run", "npm pack --dry-run", "package-content", NOT_SELECTED_REASON));
|
|
53
92
|
}
|
|
54
93
|
// --- Static scans (scan target root; config from tool root) ---
|
|
55
|
-
|
|
56
|
-
|
|
94
|
+
if (selectedChecks.has("static")) {
|
|
95
|
+
try {
|
|
96
|
+
collect(await runCodeqlCheck({ cwd: toolRoot, targetRoot: target.targetRoot, timeoutMs: config.commandTimeoutMs }));
|
|
97
|
+
}
|
|
98
|
+
catch (err) {
|
|
99
|
+
collect(errorCheck("codeql-scan", "CodeQL static analysis", "static-scan", err));
|
|
100
|
+
}
|
|
101
|
+
try {
|
|
102
|
+
collect(await runSemgrepCheck({
|
|
103
|
+
targetRoot: target.targetRoot,
|
|
104
|
+
toolRoot,
|
|
105
|
+
timeoutMs: config.commandTimeoutMs,
|
|
106
|
+
}));
|
|
107
|
+
}
|
|
108
|
+
catch (err) {
|
|
109
|
+
collect(errorCheck("semgrep-scan", "Semgrep static analysis", "static-scan", err));
|
|
110
|
+
}
|
|
57
111
|
}
|
|
58
|
-
|
|
59
|
-
collect(
|
|
112
|
+
else {
|
|
113
|
+
collect(skippedCheck("codeql-scan", "CodeQL static analysis", "static-scan", NOT_SELECTED_REASON));
|
|
114
|
+
collect(skippedCheck("semgrep-scan", "Semgrep static analysis", "static-scan", NOT_SELECTED_REASON));
|
|
60
115
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
116
|
+
// --- Security suite check ---
|
|
117
|
+
// Self-validation runs the lab's own test:security suite.
|
|
118
|
+
// External validation runs the target project's test:security script when present.
|
|
119
|
+
if (selectedChecks.has("cli-adversarial")) {
|
|
120
|
+
collect(await runCliSecuritySuiteCheck({
|
|
64
121
|
toolRoot,
|
|
122
|
+
target,
|
|
65
123
|
timeoutMs: config.commandTimeoutMs,
|
|
66
124
|
}));
|
|
67
125
|
}
|
|
68
|
-
|
|
69
|
-
collect(
|
|
126
|
+
else {
|
|
127
|
+
collect(skippedCheck("cli-adversarial-suite", "Target security test suite", "cli-adversarial", NOT_SELECTED_REASON));
|
|
70
128
|
}
|
|
71
|
-
// --- Security suite check ---
|
|
72
|
-
// Self-validation runs the lab's own test:security suite.
|
|
73
|
-
// External validation runs the target project's test:security script when present.
|
|
74
|
-
collect(await runCliSecuritySuiteCheck({
|
|
75
|
-
toolRoot,
|
|
76
|
-
target,
|
|
77
|
-
timeoutMs: config.commandTimeoutMs,
|
|
78
|
-
}));
|
|
79
129
|
// --- Fuzz smoke (always tool-internal; tests library parser/helper code) ---
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
130
|
+
if (selectedChecks.has("fuzz")) {
|
|
131
|
+
try {
|
|
132
|
+
collect(await runAllFuzzTargets(ALL_FUZZ_TARGETS, {
|
|
133
|
+
seed: options.fuzzSeed ?? 0xdeadbeef,
|
|
134
|
+
iterations: options.fuzzIterations ?? 50,
|
|
135
|
+
}));
|
|
136
|
+
}
|
|
137
|
+
catch (err) {
|
|
138
|
+
collect(errorCheck("fuzz-smoke", "Fuzz smoke", "fuzz-smoke", err));
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
collect(skippedCheck("fuzz-smoke", "Fuzz smoke", "fuzz-smoke", NOT_SELECTED_REASON));
|
|
85
143
|
}
|
|
86
|
-
|
|
87
|
-
|
|
144
|
+
// --- Attack-scenario checks (boundary, subprocess, secrets, network) ---
|
|
145
|
+
// v0.2.2 Batch 2: routed through the attack-scenario runner. The registry
|
|
146
|
+
// is empty in Batch 2, so these currently resolve to explicit "no
|
|
147
|
+
// scenarios registered yet" skipped results — never "passed".
|
|
148
|
+
const profile = options.profile ?? DEFAULT_SECURITY_PROFILE;
|
|
149
|
+
const attackScenarioChecks = PLANNED_SECURITY_CHECK_IDS.filter((id) => selectedChecks.has(id));
|
|
150
|
+
let attackResults = [];
|
|
151
|
+
if (attackScenarioChecks.length > 0) {
|
|
152
|
+
try {
|
|
153
|
+
attackResults = await runAttackScenarios({
|
|
154
|
+
selectedChecks: attackScenarioChecks,
|
|
155
|
+
profile,
|
|
156
|
+
toolRoot,
|
|
157
|
+
target,
|
|
158
|
+
config,
|
|
159
|
+
targetSnapshotBefore,
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
catch (err) {
|
|
163
|
+
attackResults = attackScenarioChecks.map((checkId) => ({
|
|
164
|
+
scenarioId: `${checkId}-attack-runner-error`,
|
|
165
|
+
scenarioTitle: `Attack scenario runner error for '${checkId}'`,
|
|
166
|
+
checkId,
|
|
167
|
+
profileId: profile,
|
|
168
|
+
status: "blocked",
|
|
169
|
+
severity: "major",
|
|
170
|
+
confidence: "low",
|
|
171
|
+
evidence: [],
|
|
172
|
+
category: "cli-adversarial",
|
|
173
|
+
errorSummary: err instanceof Error ? err.message : String(err),
|
|
174
|
+
}));
|
|
175
|
+
}
|
|
176
|
+
for (const result of attackResults) {
|
|
177
|
+
collect(toSecurityCheckResult(result));
|
|
178
|
+
}
|
|
88
179
|
}
|
|
89
180
|
const finishedAt = new Date().toISOString();
|
|
90
181
|
// --- Tool root metadata ---
|
|
@@ -102,6 +193,11 @@ export async function runSecurityValidation(options) {
|
|
|
102
193
|
// Use defaults.
|
|
103
194
|
}
|
|
104
195
|
const { verdict, recommendedNextStep } = calculateVerdict(allChecks, allFindings);
|
|
196
|
+
const verdictReasonSummary = summarizeVerdictReasoning(allChecks, allFindings);
|
|
197
|
+
// "Full release gate" = every classic pre-v0.2.2 check group was selected.
|
|
198
|
+
// Attack-scenario checks (boundary/subprocess/secrets/network) are
|
|
199
|
+
// additional coverage, not part of that traditional baseline gate.
|
|
200
|
+
const isFullReleaseGate = IMPLEMENTED_SECURITY_CHECK_IDS.every((id) => selectedChecks.has(id));
|
|
105
201
|
return {
|
|
106
202
|
toolRoot,
|
|
107
203
|
toolPackageName,
|
|
@@ -119,6 +215,9 @@ export async function runSecurityValidation(options) {
|
|
|
119
215
|
findings: allFindings,
|
|
120
216
|
verdict,
|
|
121
217
|
recommendedNextStep,
|
|
218
|
+
attackResults,
|
|
219
|
+
verdictReasonSummary,
|
|
220
|
+
isFullReleaseGate,
|
|
122
221
|
};
|
|
123
222
|
}
|
|
124
223
|
function errorCheck(id, name, category, err) {
|
|
@@ -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.2.
|
|
3
|
+
"version": "0.2.2",
|
|
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",
|
|
@@ -79,17 +79,17 @@
|
|
|
79
79
|
"test:fuzz:smoke": "tsx scripts/security/runFuzzSmoke.ts",
|
|
80
80
|
"security:validate": "tsx scripts/security/validate.ts"
|
|
81
81
|
},
|
|
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
|
-
}
|
|
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
|
+
}
|