@veraxhq/verax 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/README.md +14 -18
- package/bin/verax.js +7 -0
- package/package.json +3 -3
- package/src/cli/commands/baseline.js +104 -0
- package/src/cli/commands/default.js +79 -25
- package/src/cli/commands/ga.js +243 -0
- package/src/cli/commands/gates.js +95 -0
- package/src/cli/commands/inspect.js +131 -2
- package/src/cli/commands/release-check.js +213 -0
- package/src/cli/commands/run.js +246 -35
- package/src/cli/commands/security-check.js +211 -0
- package/src/cli/commands/truth.js +114 -0
- package/src/cli/entry.js +304 -67
- package/src/cli/util/angular-component-extractor.js +179 -0
- package/src/cli/util/angular-navigation-detector.js +141 -0
- package/src/cli/util/angular-network-detector.js +161 -0
- package/src/cli/util/angular-state-detector.js +162 -0
- package/src/cli/util/ast-interactive-detector.js +546 -0
- package/src/cli/util/ast-network-detector.js +603 -0
- package/src/cli/util/ast-usestate-detector.js +602 -0
- package/src/cli/util/bootstrap-guard.js +86 -0
- package/src/cli/util/determinism-runner.js +123 -0
- package/src/cli/util/determinism-writer.js +129 -0
- package/src/cli/util/env-url.js +4 -0
- package/src/cli/util/expectation-extractor.js +369 -73
- package/src/cli/util/findings-writer.js +126 -16
- package/src/cli/util/learn-writer.js +3 -1
- package/src/cli/util/observe-writer.js +3 -1
- package/src/cli/util/paths.js +3 -12
- package/src/cli/util/project-discovery.js +3 -0
- package/src/cli/util/project-writer.js +3 -1
- package/src/cli/util/run-resolver.js +64 -0
- package/src/cli/util/source-requirement.js +55 -0
- package/src/cli/util/summary-writer.js +1 -0
- package/src/cli/util/svelte-navigation-detector.js +163 -0
- package/src/cli/util/svelte-network-detector.js +80 -0
- package/src/cli/util/svelte-sfc-extractor.js +147 -0
- package/src/cli/util/svelte-state-detector.js +243 -0
- package/src/cli/util/vue-navigation-detector.js +177 -0
- package/src/cli/util/vue-sfc-extractor.js +162 -0
- package/src/cli/util/vue-state-detector.js +215 -0
- package/src/verax/cli/finding-explainer.js +56 -3
- package/src/verax/core/artifacts/registry.js +154 -0
- package/src/verax/core/artifacts/verifier.js +980 -0
- package/src/verax/core/baseline/baseline.enforcer.js +137 -0
- package/src/verax/core/baseline/baseline.snapshot.js +231 -0
- package/src/verax/core/capabilities/gates.js +499 -0
- package/src/verax/core/capabilities/registry.js +475 -0
- package/src/verax/core/confidence/confidence-compute.js +137 -0
- package/src/verax/core/confidence/confidence-invariants.js +234 -0
- package/src/verax/core/confidence/confidence-report-writer.js +112 -0
- package/src/verax/core/confidence/confidence-weights.js +44 -0
- package/src/verax/core/confidence/confidence.defaults.js +65 -0
- package/src/verax/core/confidence/confidence.loader.js +79 -0
- package/src/verax/core/confidence/confidence.schema.js +94 -0
- package/src/verax/core/confidence-engine-refactor.js +484 -0
- package/src/verax/core/confidence-engine.js +486 -0
- package/src/verax/core/confidence-engine.js.backup +471 -0
- package/src/verax/core/contracts/index.js +29 -0
- package/src/verax/core/contracts/types.js +185 -0
- package/src/verax/core/contracts/validators.js +381 -0
- package/src/verax/core/decision-snapshot.js +30 -3
- package/src/verax/core/decisions/decision.trace.js +276 -0
- package/src/verax/core/determinism/contract-writer.js +89 -0
- package/src/verax/core/determinism/contract.js +139 -0
- package/src/verax/core/determinism/diff.js +364 -0
- package/src/verax/core/determinism/engine.js +221 -0
- package/src/verax/core/determinism/finding-identity.js +148 -0
- package/src/verax/core/determinism/normalize.js +438 -0
- package/src/verax/core/determinism/report-writer.js +92 -0
- package/src/verax/core/determinism/run-fingerprint.js +118 -0
- package/src/verax/core/dynamic-route-intelligence.js +528 -0
- package/src/verax/core/evidence/evidence-capture-service.js +307 -0
- package/src/verax/core/evidence/evidence-intent-ledger.js +165 -0
- package/src/verax/core/evidence-builder.js +487 -0
- package/src/verax/core/execution-mode-context.js +77 -0
- package/src/verax/core/execution-mode-detector.js +190 -0
- package/src/verax/core/failures/exit-codes.js +86 -0
- package/src/verax/core/failures/failure-summary.js +76 -0
- package/src/verax/core/failures/failure.factory.js +225 -0
- package/src/verax/core/failures/failure.ledger.js +132 -0
- package/src/verax/core/failures/failure.types.js +196 -0
- package/src/verax/core/failures/index.js +10 -0
- package/src/verax/core/ga/ga-report-writer.js +43 -0
- package/src/verax/core/ga/ga.artifact.js +49 -0
- package/src/verax/core/ga/ga.contract.js +434 -0
- package/src/verax/core/ga/ga.enforcer.js +86 -0
- package/src/verax/core/guardrails/guardrails-report-writer.js +109 -0
- package/src/verax/core/guardrails/policy.defaults.js +210 -0
- package/src/verax/core/guardrails/policy.loader.js +83 -0
- package/src/verax/core/guardrails/policy.schema.js +110 -0
- package/src/verax/core/guardrails/truth-reconciliation.js +136 -0
- package/src/verax/core/guardrails-engine.js +505 -0
- package/src/verax/core/observe/run-timeline.js +316 -0
- package/src/verax/core/perf/perf.contract.js +186 -0
- package/src/verax/core/perf/perf.display.js +65 -0
- package/src/verax/core/perf/perf.enforcer.js +91 -0
- package/src/verax/core/perf/perf.monitor.js +209 -0
- package/src/verax/core/perf/perf.report.js +198 -0
- package/src/verax/core/pipeline-tracker.js +238 -0
- package/src/verax/core/product-definition.js +127 -0
- package/src/verax/core/release/provenance.builder.js +271 -0
- package/src/verax/core/release/release-report-writer.js +40 -0
- package/src/verax/core/release/release.enforcer.js +159 -0
- package/src/verax/core/release/reproducibility.check.js +221 -0
- package/src/verax/core/release/sbom.builder.js +283 -0
- package/src/verax/core/report/cross-index.js +192 -0
- package/src/verax/core/report/human-summary.js +222 -0
- package/src/verax/core/route-intelligence.js +419 -0
- package/src/verax/core/security/secrets.scan.js +326 -0
- package/src/verax/core/security/security-report.js +50 -0
- package/src/verax/core/security/security.enforcer.js +124 -0
- package/src/verax/core/security/supplychain.defaults.json +38 -0
- package/src/verax/core/security/supplychain.policy.js +326 -0
- package/src/verax/core/security/vuln.scan.js +265 -0
- package/src/verax/core/truth/truth.certificate.js +250 -0
- package/src/verax/core/ui-feedback-intelligence.js +515 -0
- package/src/verax/detect/confidence-engine.js +628 -40
- package/src/verax/detect/confidence-helper.js +33 -0
- package/src/verax/detect/detection-engine.js +18 -1
- package/src/verax/detect/dynamic-route-findings.js +335 -0
- package/src/verax/detect/expectation-chain-detector.js +417 -0
- package/src/verax/detect/expectation-model.js +3 -1
- package/src/verax/detect/findings-writer.js +141 -5
- package/src/verax/detect/index.js +229 -5
- package/src/verax/detect/journey-stall-detector.js +558 -0
- package/src/verax/detect/route-findings.js +218 -0
- package/src/verax/detect/ui-feedback-findings.js +207 -0
- package/src/verax/detect/verdict-engine.js +57 -3
- package/src/verax/detect/view-switch-correlator.js +242 -0
- package/src/verax/index.js +413 -45
- package/src/verax/learn/action-contract-extractor.js +682 -64
- package/src/verax/learn/route-validator.js +4 -1
- package/src/verax/observe/index.js +88 -843
- package/src/verax/observe/interaction-runner.js +25 -8
- package/src/verax/observe/observe-context.js +205 -0
- package/src/verax/observe/observe-helpers.js +191 -0
- package/src/verax/observe/observe-runner.js +226 -0
- package/src/verax/observe/observers/budget-observer.js +185 -0
- package/src/verax/observe/observers/console-observer.js +102 -0
- package/src/verax/observe/observers/coverage-observer.js +107 -0
- package/src/verax/observe/observers/interaction-observer.js +471 -0
- package/src/verax/observe/observers/navigation-observer.js +132 -0
- package/src/verax/observe/observers/network-observer.js +87 -0
- package/src/verax/observe/observers/safety-observer.js +82 -0
- package/src/verax/observe/observers/ui-feedback-observer.js +99 -0
- package/src/verax/observe/ui-feedback-detector.js +742 -0
- package/src/verax/observe/ui-signal-sensor.js +148 -2
- package/src/verax/scan-summary-writer.js +42 -8
- package/src/verax/shared/artifact-manager.js +8 -5
- package/src/verax/shared/css-spinner-rules.js +204 -0
- package/src/verax/shared/view-switch-rules.js +208 -0
package/src/cli/commands/run.js
CHANGED
|
@@ -17,6 +17,9 @@ import { detectFindings } from '../util/detection-engine.js';
|
|
|
17
17
|
import { writeFindingsJson } from '../util/findings-writer.js';
|
|
18
18
|
import { writeSummaryJson } from '../util/summary-writer.js';
|
|
19
19
|
import { computeRuntimeBudget, withTimeout } from '../util/runtime-budget.js';
|
|
20
|
+
import { assertHasLocalSource } from '../util/source-requirement.js';
|
|
21
|
+
import { runWithDeterminism } from '../util/determinism-runner.js';
|
|
22
|
+
import { runDeterminismCheck } from '../../verax/core/determinism/engine.js';
|
|
20
23
|
|
|
21
24
|
const __filename = fileURLToPath(import.meta.url);
|
|
22
25
|
const __dirname = dirname(__filename);
|
|
@@ -27,7 +30,7 @@ function getVersion() {
|
|
|
27
30
|
const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'));
|
|
28
31
|
return pkg.version;
|
|
29
32
|
} catch {
|
|
30
|
-
return '0.
|
|
33
|
+
return '0.3.0';
|
|
31
34
|
}
|
|
32
35
|
}
|
|
33
36
|
|
|
@@ -36,17 +39,68 @@ function getVersion() {
|
|
|
36
39
|
* Strict, non-interactive CLI mode with explicit flags
|
|
37
40
|
*/
|
|
38
41
|
export async function runCommand(options) {
|
|
42
|
+
return await runCommandInternal(options);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Internal run command implementation
|
|
47
|
+
*/
|
|
48
|
+
async function runCommandInternal(options) {
|
|
39
49
|
const {
|
|
40
50
|
url,
|
|
51
|
+
fixture,
|
|
41
52
|
src = '.',
|
|
42
53
|
out = '.verax',
|
|
43
54
|
json = false,
|
|
44
55
|
verbose = false,
|
|
56
|
+
determinism = false,
|
|
57
|
+
determinismRuns = 2,
|
|
45
58
|
} = options;
|
|
46
59
|
|
|
60
|
+
// PHASE 25: Support fixture mode for determinism
|
|
61
|
+
let resolvedUrl = url;
|
|
62
|
+
let fixtureId = null;
|
|
63
|
+
|
|
64
|
+
if (fixture && !url) {
|
|
65
|
+
// Extract URL from fixture
|
|
66
|
+
const { resolve } = await import('path');
|
|
67
|
+
const { existsSync, readFileSync } = await import('fs');
|
|
68
|
+
const { fileURLToPath } = await import('url');
|
|
69
|
+
const { dirname } = await import('path');
|
|
70
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
71
|
+
const __dirname = dirname(__filename);
|
|
72
|
+
|
|
73
|
+
const fixturePath = resolve(__dirname, '..', '..', '..', 'test', 'fixtures', 'realistic', fixture);
|
|
74
|
+
if (existsSync(fixturePath)) {
|
|
75
|
+
// Try to read package.json or index.html to extract URL
|
|
76
|
+
const packagePath = resolve(fixturePath, 'package.json');
|
|
77
|
+
const indexPath = resolve(fixturePath, 'index.html');
|
|
78
|
+
|
|
79
|
+
if (existsSync(packagePath)) {
|
|
80
|
+
try {
|
|
81
|
+
const pkg = JSON.parse(readFileSync(packagePath, 'utf-8'));
|
|
82
|
+
if (pkg.verax && pkg.verax.url) {
|
|
83
|
+
resolvedUrl = pkg.verax.url;
|
|
84
|
+
fixtureId = fixture;
|
|
85
|
+
}
|
|
86
|
+
} catch {
|
|
87
|
+
// Ignore parse errors
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// If no URL found, use default localhost URL for fixture
|
|
92
|
+
if (!resolvedUrl) {
|
|
93
|
+
resolvedUrl = `http://localhost:5173`; // Default Vite dev server
|
|
94
|
+
fixtureId = fixture;
|
|
95
|
+
}
|
|
96
|
+
} else {
|
|
97
|
+
throw new DataError(`Fixture not found: ${fixture}`);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
47
101
|
// Validate required arguments
|
|
48
|
-
if (!
|
|
49
|
-
throw new UsageError('Missing required argument: --url <url>');
|
|
102
|
+
if (!resolvedUrl) {
|
|
103
|
+
throw new UsageError('Missing required argument: --url <url> or --fixture <fixture>');
|
|
50
104
|
}
|
|
51
105
|
|
|
52
106
|
const projectRoot = resolve(process.cwd());
|
|
@@ -56,6 +110,9 @@ export async function runCommand(options) {
|
|
|
56
110
|
if (!existsSync(srcPath)) {
|
|
57
111
|
throw new DataError(`Source directory not found: ${srcPath}`);
|
|
58
112
|
}
|
|
113
|
+
|
|
114
|
+
// Enforce local source availability (no URL-only scans)
|
|
115
|
+
assertHasLocalSource(srcPath);
|
|
59
116
|
|
|
60
117
|
// Create event emitter
|
|
61
118
|
const events = new RunEventEmitter();
|
|
@@ -92,6 +149,8 @@ export async function runCommand(options) {
|
|
|
92
149
|
try {
|
|
93
150
|
const failedAt = new Date().toISOString();
|
|
94
151
|
atomicWriteJson(paths.runStatusJson, {
|
|
152
|
+
contractVersion: 1,
|
|
153
|
+
artifactVersions: paths.artifactVersions,
|
|
95
154
|
status: 'FAILED',
|
|
96
155
|
runId,
|
|
97
156
|
startedAt,
|
|
@@ -100,6 +159,8 @@ export async function runCommand(options) {
|
|
|
100
159
|
});
|
|
101
160
|
|
|
102
161
|
atomicWriteJson(paths.runMetaJson, {
|
|
162
|
+
contractVersion: 1,
|
|
163
|
+
artifactVersions: paths.artifactVersions,
|
|
103
164
|
veraxVersion: getVersion(),
|
|
104
165
|
nodeVersion: process.version,
|
|
105
166
|
platform: process.platform,
|
|
@@ -120,7 +181,7 @@ export async function runCommand(options) {
|
|
|
120
181
|
startedAt,
|
|
121
182
|
completedAt: failedAt,
|
|
122
183
|
command: 'run',
|
|
123
|
-
url,
|
|
184
|
+
url: resolvedUrl,
|
|
124
185
|
notes: `Run timed out: ${reason}`,
|
|
125
186
|
}, {
|
|
126
187
|
expectationsTotal: 0,
|
|
@@ -145,6 +206,70 @@ export async function runCommand(options) {
|
|
|
145
206
|
});
|
|
146
207
|
};
|
|
147
208
|
|
|
209
|
+
// PHASE 25: If determinism mode, wrap execution
|
|
210
|
+
if (determinism) {
|
|
211
|
+
const scanFn = async (runConfig) => {
|
|
212
|
+
// Execute a single scan run
|
|
213
|
+
const singleRunId = generateRunId();
|
|
214
|
+
const singlePaths = getRunPaths(projectRoot, out, singleRunId);
|
|
215
|
+
ensureRunDirectories(singlePaths);
|
|
216
|
+
|
|
217
|
+
// Execute scan (reuse existing logic but with single run)
|
|
218
|
+
// This is a simplified version - in production, you'd extract the scan logic
|
|
219
|
+
const { scan } = await import('../../verax/index.js');
|
|
220
|
+
const scanResult = await scan(
|
|
221
|
+
projectRoot,
|
|
222
|
+
resolvedUrl,
|
|
223
|
+
null, // manifestPath
|
|
224
|
+
null, // scanBudgetOverride
|
|
225
|
+
{}, // safetyFlags
|
|
226
|
+
singleRunId
|
|
227
|
+
);
|
|
228
|
+
|
|
229
|
+
return {
|
|
230
|
+
runId: singleRunId,
|
|
231
|
+
artifactPaths: {
|
|
232
|
+
findings: singlePaths.findingsJson,
|
|
233
|
+
runStatus: singlePaths.runStatusJson,
|
|
234
|
+
summary: singlePaths.summaryJson,
|
|
235
|
+
learn: singlePaths.learnJson,
|
|
236
|
+
observe: singlePaths.observeJson,
|
|
237
|
+
runDir: singlePaths.baseDir
|
|
238
|
+
}
|
|
239
|
+
};
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
const determinismResult = await runWithDeterminism(scanFn, {
|
|
243
|
+
runs: determinismRuns,
|
|
244
|
+
out,
|
|
245
|
+
url: resolvedUrl,
|
|
246
|
+
fixture: fixtureId,
|
|
247
|
+
src,
|
|
248
|
+
verbose,
|
|
249
|
+
json
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
// PHASE 25: Output determinism report path
|
|
253
|
+
if (!json) {
|
|
254
|
+
console.log(`\nDeterminism check complete.`);
|
|
255
|
+
console.log(`Verdict: ${determinismResult.verdict}`);
|
|
256
|
+
console.log(`Report: ${determinismResult.reportPath}`);
|
|
257
|
+
} else {
|
|
258
|
+
console.log(JSON.stringify({
|
|
259
|
+
type: 'determinism:complete',
|
|
260
|
+
verdict: determinismResult.verdict,
|
|
261
|
+
reportPath: determinismResult.reportPath,
|
|
262
|
+
summary: determinismResult.summary
|
|
263
|
+
}));
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
return {
|
|
267
|
+
success: determinismResult.verdict === 'DETERMINISTIC' || determinismResult.verdict === 'NON_DETERMINISTIC_EXPECTED',
|
|
268
|
+
verdict: determinismResult.verdict,
|
|
269
|
+
reportPath: determinismResult.reportPath
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
|
|
148
273
|
try {
|
|
149
274
|
// Generate run ID
|
|
150
275
|
runId = generateRunId();
|
|
@@ -197,6 +322,8 @@ export async function runCommand(options) {
|
|
|
197
322
|
startedAt = now.toISOString();
|
|
198
323
|
|
|
199
324
|
atomicWriteJson(paths.runStatusJson, {
|
|
325
|
+
contractVersion: 1,
|
|
326
|
+
artifactVersions: paths.artifactVersions,
|
|
200
327
|
status: 'RUNNING',
|
|
201
328
|
runId,
|
|
202
329
|
startedAt,
|
|
@@ -204,13 +331,16 @@ export async function runCommand(options) {
|
|
|
204
331
|
|
|
205
332
|
// Write metadata
|
|
206
333
|
atomicWriteJson(paths.runMetaJson, {
|
|
334
|
+
contractVersion: 1,
|
|
335
|
+
artifactVersions: paths.artifactVersions,
|
|
207
336
|
veraxVersion: getVersion(),
|
|
208
337
|
nodeVersion: process.version,
|
|
209
338
|
platform: process.platform,
|
|
210
339
|
cwd: projectRoot,
|
|
211
340
|
command: 'run',
|
|
212
|
-
args: { url, src, out },
|
|
213
|
-
url,
|
|
341
|
+
args: { url: resolvedUrl, fixture: fixtureId, src, out },
|
|
342
|
+
url: resolvedUrl,
|
|
343
|
+
fixtureId: fixtureId,
|
|
214
344
|
src: srcPath,
|
|
215
345
|
startedAt,
|
|
216
346
|
completedAt: null,
|
|
@@ -402,29 +532,6 @@ export async function runCommand(options) {
|
|
|
402
532
|
|
|
403
533
|
const completedAt = new Date().toISOString();
|
|
404
534
|
|
|
405
|
-
// Write completed status
|
|
406
|
-
atomicWriteJson(paths.runStatusJson, {
|
|
407
|
-
status: 'COMPLETE',
|
|
408
|
-
runId,
|
|
409
|
-
startedAt,
|
|
410
|
-
completedAt,
|
|
411
|
-
});
|
|
412
|
-
|
|
413
|
-
// Update metadata with completion time
|
|
414
|
-
atomicWriteJson(paths.runMetaJson, {
|
|
415
|
-
veraxVersion: getVersion(),
|
|
416
|
-
nodeVersion: process.version,
|
|
417
|
-
platform: process.platform,
|
|
418
|
-
cwd: projectRoot,
|
|
419
|
-
command: 'run',
|
|
420
|
-
args: { url, src, out },
|
|
421
|
-
url,
|
|
422
|
-
src: srcPath,
|
|
423
|
-
startedAt,
|
|
424
|
-
completedAt,
|
|
425
|
-
error: null,
|
|
426
|
-
});
|
|
427
|
-
|
|
428
535
|
const runDurationMs = completedAt && startedAt ? (Date.parse(completedAt) - Date.parse(startedAt)) : 0;
|
|
429
536
|
const metrics = {
|
|
430
537
|
learnMs: observeData?.timings?.learnMs || 0,
|
|
@@ -439,6 +546,9 @@ export async function runCommand(options) {
|
|
|
439
546
|
UNKNOWN: 0,
|
|
440
547
|
};
|
|
441
548
|
|
|
549
|
+
// Write detect results (or empty if detection failed)
|
|
550
|
+
const findingsResult = writeFindingsJson(paths.baseDir, detectData);
|
|
551
|
+
|
|
442
552
|
// Write summary with stable digest
|
|
443
553
|
writeSummaryJson(paths.summaryJson, {
|
|
444
554
|
runId,
|
|
@@ -462,9 +572,6 @@ export async function runCommand(options) {
|
|
|
462
572
|
...findingsCounts,
|
|
463
573
|
});
|
|
464
574
|
|
|
465
|
-
// Write detect results (or empty if detection failed)
|
|
466
|
-
writeFindingsJson(paths.baseDir, detectData);
|
|
467
|
-
|
|
468
575
|
// Write traces (include all events including heartbeats)
|
|
469
576
|
const allEvents = events.getEvents();
|
|
470
577
|
const tracesContent = allEvents
|
|
@@ -480,6 +587,77 @@ export async function runCommand(options) {
|
|
|
480
587
|
|
|
481
588
|
// Write observe results
|
|
482
589
|
writeObserveJson(paths.baseDir, observeData);
|
|
590
|
+
|
|
591
|
+
// PHASE 6: Verify artifacts after all writers finish
|
|
592
|
+
const { verifyRun } = await import('../../verax/core/artifacts/verifier.js');
|
|
593
|
+
const verification = verifyRun(paths.baseDir, paths.artifactVersions);
|
|
594
|
+
|
|
595
|
+
// Determine final status based on verification
|
|
596
|
+
let finalStatus = 'COMPLETE';
|
|
597
|
+
if (!verification.ok) {
|
|
598
|
+
finalStatus = 'INVALID';
|
|
599
|
+
} else if (verification.warnings.length > 0) {
|
|
600
|
+
finalStatus = 'VALID_WITH_WARNINGS';
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
// PHASE 21.2: Compute determinism summary for run.status.json
|
|
604
|
+
let determinismSummary = null;
|
|
605
|
+
try {
|
|
606
|
+
const decisionsPath = resolve(paths.baseDir, 'decisions.json');
|
|
607
|
+
if (existsSync(decisionsPath)) {
|
|
608
|
+
const decisions = JSON.parse(readFileSync(decisionsPath, 'utf-8'));
|
|
609
|
+
const { DecisionRecorder } = await import('../../verax/core/determinism-model.js');
|
|
610
|
+
const recorder = DecisionRecorder.fromExport(decisions);
|
|
611
|
+
const { computeDeterminismVerdict } = await import('../../verax/core/determinism/contract.js');
|
|
612
|
+
const verdict = computeDeterminismVerdict(recorder);
|
|
613
|
+
|
|
614
|
+
determinismSummary = {
|
|
615
|
+
verdict: verdict.verdict, // DETERMINISTIC or NON_DETERMINISTIC
|
|
616
|
+
message: verdict.message,
|
|
617
|
+
adaptiveEventsCount: verdict.adaptiveEvents.length
|
|
618
|
+
};
|
|
619
|
+
}
|
|
620
|
+
} catch (error) {
|
|
621
|
+
// Ignore errors - determinism summary is optional
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
// Write completed status with contract + enforcement snapshot + verification + determinism
|
|
625
|
+
atomicWriteJson(paths.runStatusJson, {
|
|
626
|
+
contractVersion: 1,
|
|
627
|
+
artifactVersions: paths.artifactVersions,
|
|
628
|
+
status: finalStatus,
|
|
629
|
+
runId,
|
|
630
|
+
startedAt,
|
|
631
|
+
completedAt,
|
|
632
|
+
enforcement: findingsResult?.payload?.enforcement || null,
|
|
633
|
+
verification: {
|
|
634
|
+
ok: verification.ok,
|
|
635
|
+
status: finalStatus,
|
|
636
|
+
errorsCount: verification.errors.length,
|
|
637
|
+
warningsCount: verification.warnings.length,
|
|
638
|
+
verifiedAt: verification.verifiedAt
|
|
639
|
+
},
|
|
640
|
+
// PHASE 21.2: Determinism summary
|
|
641
|
+
determinismSummary: determinismSummary
|
|
642
|
+
});
|
|
643
|
+
|
|
644
|
+
// Update metadata with completion time
|
|
645
|
+
atomicWriteJson(paths.runMetaJson, {
|
|
646
|
+
contractVersion: 1,
|
|
647
|
+
artifactVersions: paths.artifactVersions,
|
|
648
|
+
veraxVersion: getVersion(),
|
|
649
|
+
nodeVersion: process.version,
|
|
650
|
+
platform: process.platform,
|
|
651
|
+
cwd: projectRoot,
|
|
652
|
+
command: 'run',
|
|
653
|
+
args: { url: resolvedUrl, fixture: fixtureId, src, out },
|
|
654
|
+
url: resolvedUrl,
|
|
655
|
+
fixtureId: fixtureId,
|
|
656
|
+
src: srcPath,
|
|
657
|
+
startedAt,
|
|
658
|
+
completedAt,
|
|
659
|
+
error: null,
|
|
660
|
+
});
|
|
483
661
|
|
|
484
662
|
events.emit('phase:completed', {
|
|
485
663
|
phase: 'Finalize Artifacts',
|
|
@@ -511,6 +689,34 @@ export async function runCommand(options) {
|
|
|
511
689
|
console.log('\nRun complete.');
|
|
512
690
|
console.log(`Run ID: ${runId}`);
|
|
513
691
|
console.log(`Artifacts: ${paths.baseDir}`);
|
|
692
|
+
|
|
693
|
+
// PHASE 21.2: Display determinism truth
|
|
694
|
+
if (determinismSummary) {
|
|
695
|
+
console.log('');
|
|
696
|
+
if (determinismSummary.verdict === 'DETERMINISTIC') {
|
|
697
|
+
console.log('Deterministic: YES');
|
|
698
|
+
} else {
|
|
699
|
+
console.log(`Deterministic: NO (${determinismSummary.message})`);
|
|
700
|
+
if (determinismSummary.adaptiveEventsCount > 0) {
|
|
701
|
+
console.log(` Adaptive events detected: ${determinismSummary.adaptiveEventsCount}`);
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
// PHASE 6: Display verification results
|
|
707
|
+
const { formatVerificationOutput } = await import('../../verax/core/artifacts/verifier.js');
|
|
708
|
+
const verificationOutput = formatVerificationOutput(verification, verbose);
|
|
709
|
+
console.log('');
|
|
710
|
+
console.log(verificationOutput);
|
|
711
|
+
|
|
712
|
+
// PHASE 21.9: Display performance metrics
|
|
713
|
+
const { loadPerformanceReport } = await import('../../verax/core/perf/perf.report.js');
|
|
714
|
+
const { formatPerformanceMetrics } = await import('../../verax/core/perf/perf.display.js');
|
|
715
|
+
const perfReport = loadPerformanceReport(projectRoot, runId);
|
|
716
|
+
if (perfReport) {
|
|
717
|
+
console.log('');
|
|
718
|
+
console.log(formatPerformanceMetrics(perfReport));
|
|
719
|
+
}
|
|
514
720
|
}
|
|
515
721
|
|
|
516
722
|
return { runId, paths, success: true };
|
|
@@ -528,6 +734,8 @@ export async function runCommand(options) {
|
|
|
528
734
|
try {
|
|
529
735
|
const failedAt = new Date().toISOString();
|
|
530
736
|
atomicWriteJson(paths.runStatusJson, {
|
|
737
|
+
contractVersion: 1,
|
|
738
|
+
artifactVersions: paths.artifactVersions,
|
|
531
739
|
status: 'FAILED',
|
|
532
740
|
runId,
|
|
533
741
|
startedAt,
|
|
@@ -537,13 +745,16 @@ export async function runCommand(options) {
|
|
|
537
745
|
|
|
538
746
|
// Update metadata
|
|
539
747
|
atomicWriteJson(paths.runMetaJson, {
|
|
748
|
+
contractVersion: 1,
|
|
749
|
+
artifactVersions: paths.artifactVersions,
|
|
540
750
|
veraxVersion: getVersion(),
|
|
541
751
|
nodeVersion: process.version,
|
|
542
752
|
platform: process.platform,
|
|
543
753
|
cwd: projectRoot,
|
|
544
754
|
command: 'run',
|
|
545
|
-
args: { url, src, out },
|
|
546
|
-
url,
|
|
755
|
+
args: { url: resolvedUrl || url, fixture: fixtureId, src, out },
|
|
756
|
+
url: resolvedUrl || url,
|
|
757
|
+
fixtureId: fixtureId,
|
|
547
758
|
src: srcPath,
|
|
548
759
|
startedAt,
|
|
549
760
|
completedAt: failedAt,
|
|
@@ -558,7 +769,7 @@ export async function runCommand(options) {
|
|
|
558
769
|
startedAt,
|
|
559
770
|
completedAt: failedAt,
|
|
560
771
|
command: 'run',
|
|
561
|
-
url,
|
|
772
|
+
url: resolvedUrl || url,
|
|
562
773
|
notes: `Run failed: ${error.message}`,
|
|
563
774
|
}, {
|
|
564
775
|
expectationsTotal: 0,
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PHASE 21.8 — Security Check CLI Command
|
|
3
|
+
*
|
|
4
|
+
* Checks security baseline: secrets, vulnerabilities, supply-chain.
|
|
5
|
+
* Exit codes: 0 = SECURITY-OK, 6 = SECURITY-BLOCKED, 70 = Internal corruption
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { scanSecrets, writeSecretsReport } from '../../verax/core/security/secrets.scan.js';
|
|
9
|
+
import { scanVulnerabilities, writeVulnReport } from '../../verax/core/security/vuln.scan.js';
|
|
10
|
+
import { evaluateSupplyChainPolicy, writeSupplyChainReport } from '../../verax/core/security/supplychain.policy.js';
|
|
11
|
+
import { writeSecurityReport } from '../../verax/core/security/security-report.js';
|
|
12
|
+
import { resolve } from 'path';
|
|
13
|
+
import { mkdirSync, existsSync } from 'fs';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Security check command
|
|
17
|
+
*
|
|
18
|
+
* @param {Object} options - Options
|
|
19
|
+
* @param {boolean} [options.json] - Output as JSON
|
|
20
|
+
*/
|
|
21
|
+
export async function securityCheckCommand(options = {}) {
|
|
22
|
+
const { json = false } = options;
|
|
23
|
+
const projectDir = resolve(process.cwd());
|
|
24
|
+
|
|
25
|
+
const status = {
|
|
26
|
+
secrets: { ok: false, hasSecrets: false, blockers: [], tool: 'VERAX_SECRETS_SCANNER' },
|
|
27
|
+
vulnerabilities: { ok: false, blocking: false, blockers: [], warnings: [], tool: null, availability: 'UNKNOWN' },
|
|
28
|
+
supplychain: { ok: false, violations: [], blockers: [], tool: 'VERAX_SUPPLYCHAIN_POLICY' }
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
// 1. Scan for secrets
|
|
32
|
+
try {
|
|
33
|
+
const secretsResult = await scanSecrets(projectDir);
|
|
34
|
+
writeSecretsReport(projectDir, secretsResult);
|
|
35
|
+
|
|
36
|
+
status.secrets.ok = secretsResult.ok;
|
|
37
|
+
status.secrets.hasSecrets = secretsResult.hasSecrets;
|
|
38
|
+
|
|
39
|
+
if (secretsResult.hasSecrets) {
|
|
40
|
+
const critical = secretsResult.findings.filter(f => f.severity === 'CRITICAL');
|
|
41
|
+
const high = secretsResult.findings.filter(f => f.severity === 'HIGH');
|
|
42
|
+
|
|
43
|
+
if (critical.length > 0) {
|
|
44
|
+
status.secrets.blockers.push(`${critical.length} CRITICAL secret(s) detected`);
|
|
45
|
+
}
|
|
46
|
+
if (high.length > 0) {
|
|
47
|
+
status.secrets.blockers.push(`${high.length} HIGH severity secret(s) detected`);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Add sample findings (first 3)
|
|
51
|
+
const sampleFindings = secretsResult.findings.slice(0, 3).map(f =>
|
|
52
|
+
`${f.type} in ${f.file}:${f.line}`
|
|
53
|
+
);
|
|
54
|
+
status.secrets.blockers.push(`Sample findings: ${sampleFindings.join(', ')}`);
|
|
55
|
+
}
|
|
56
|
+
} catch (error) {
|
|
57
|
+
status.secrets.blockers.push(`Secrets scan failed: ${error.message}`);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// 2. Scan vulnerabilities
|
|
61
|
+
let vulnResult = null;
|
|
62
|
+
try {
|
|
63
|
+
vulnResult = await scanVulnerabilities(projectDir);
|
|
64
|
+
writeVulnReport(projectDir, vulnResult);
|
|
65
|
+
|
|
66
|
+
status.vulnerabilities.ok = !vulnResult.blocking;
|
|
67
|
+
status.vulnerabilities.blocking = vulnResult.blocking;
|
|
68
|
+
status.vulnerabilities.tool = vulnResult.tool || null;
|
|
69
|
+
status.vulnerabilities.availability = vulnResult.availability || 'UNKNOWN';
|
|
70
|
+
status.vulnerabilities.osvAvailable = vulnResult.osvAvailable || false;
|
|
71
|
+
|
|
72
|
+
if (vulnResult.availability === 'NOT_AVAILABLE') {
|
|
73
|
+
status.vulnerabilities.warnings.push('OSV scanner not available, using npm audit fallback');
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (vulnResult.blocking) {
|
|
77
|
+
if (vulnResult.summary.critical > 0) {
|
|
78
|
+
status.vulnerabilities.blockers.push(`${vulnResult.summary.critical} CRITICAL vulnerability/vulnerabilities`);
|
|
79
|
+
}
|
|
80
|
+
if (vulnResult.summary.high > 0) {
|
|
81
|
+
status.vulnerabilities.blockers.push(`${vulnResult.summary.high} HIGH severity vulnerability/vulnerabilities`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (vulnResult.summary.medium > 0 && !vulnResult.blocking) {
|
|
86
|
+
status.vulnerabilities.warnings.push(`${vulnResult.summary.medium} MEDIUM severity vulnerability/vulnerabilities (non-blocking)`);
|
|
87
|
+
}
|
|
88
|
+
} catch (error) {
|
|
89
|
+
status.vulnerabilities.blockers.push(`Vulnerability scan failed: ${error.message}`);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// 3. Check supply-chain policy
|
|
93
|
+
let supplyChainResult = null;
|
|
94
|
+
try {
|
|
95
|
+
supplyChainResult = await evaluateSupplyChainPolicy(projectDir);
|
|
96
|
+
writeSupplyChainReport(projectDir, supplyChainResult);
|
|
97
|
+
|
|
98
|
+
status.supplychain.ok = supplyChainResult.ok;
|
|
99
|
+
status.supplychain.violations = supplyChainResult.violations;
|
|
100
|
+
|
|
101
|
+
if (!supplyChainResult.ok) {
|
|
102
|
+
for (const violation of supplyChainResult.violations) {
|
|
103
|
+
status.supplychain.blockers.push(violation.message);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
} catch (error) {
|
|
107
|
+
status.supplychain.blockers.push(`Supply-chain check failed: ${error.message}`);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Determine overall status
|
|
111
|
+
const allOk = status.secrets.ok && status.vulnerabilities.ok && status.supplychain.ok;
|
|
112
|
+
|
|
113
|
+
// Write unified security report
|
|
114
|
+
let secretsReport = null;
|
|
115
|
+
try {
|
|
116
|
+
const secretsPath = resolve(projectDir, 'release', 'security.secrets.report.json');
|
|
117
|
+
if (existsSync(secretsPath)) {
|
|
118
|
+
const { readFileSync } = await import('fs');
|
|
119
|
+
secretsReport = JSON.parse(readFileSync(secretsPath, 'utf-8'));
|
|
120
|
+
}
|
|
121
|
+
} catch {
|
|
122
|
+
// Ignore
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
let vulnReportData = vulnResult;
|
|
126
|
+
let supplyChainReportData = supplyChainResult;
|
|
127
|
+
|
|
128
|
+
const unifiedReport = {
|
|
129
|
+
securityOk: allOk,
|
|
130
|
+
status,
|
|
131
|
+
summary: {
|
|
132
|
+
secrets: status.secrets.ok ? 'OK' : 'BLOCKED',
|
|
133
|
+
vulnerabilities: status.vulnerabilities.ok ? 'OK' : (status.vulnerabilities.blocking ? 'BLOCKED' : (status.vulnerabilities.availability === 'NOT_AVAILABLE' ? 'NOT_AVAILABLE' : 'WARN')),
|
|
134
|
+
supplychain: status.supplychain.ok ? 'OK' : 'BLOCKED'
|
|
135
|
+
},
|
|
136
|
+
secretsReport,
|
|
137
|
+
vulnReport: vulnReportData,
|
|
138
|
+
supplyChainReport: supplyChainReportData
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
const unifiedReportPath = writeSecurityReport(projectDir, unifiedReport);
|
|
142
|
+
const hasInternalCorruption =
|
|
143
|
+
status.secrets.blockers.some(b => b.includes('corruption') || b.includes('Internal')) ||
|
|
144
|
+
status.vulnerabilities.blockers.some(b => b.includes('corruption')) ||
|
|
145
|
+
status.supplychain.blockers.some(b => b.includes('corruption'));
|
|
146
|
+
|
|
147
|
+
// Output
|
|
148
|
+
if (json) {
|
|
149
|
+
console.log(JSON.stringify({
|
|
150
|
+
securityOk: allOk,
|
|
151
|
+
status,
|
|
152
|
+
summary: {
|
|
153
|
+
secrets: status.secrets.ok ? 'OK' : 'BLOCKED',
|
|
154
|
+
vulnerabilities: status.vulnerabilities.ok ? 'OK' : (status.vulnerabilities.blocking ? 'BLOCKED' : (status.vulnerabilities.availability === 'NOT_AVAILABLE' ? 'NOT_AVAILABLE' : 'WARN')),
|
|
155
|
+
supplychain: status.supplychain.ok ? 'OK' : 'BLOCKED'
|
|
156
|
+
},
|
|
157
|
+
unifiedReportPath: unifiedReportPath
|
|
158
|
+
}, null, 2));
|
|
159
|
+
} else {
|
|
160
|
+
console.log('\n' + '='.repeat(80));
|
|
161
|
+
console.log('SECURITY BASELINE CHECK');
|
|
162
|
+
console.log('='.repeat(80));
|
|
163
|
+
|
|
164
|
+
console.log(`\nSecrets: ${status.secrets.ok ? '✅ OK' : '❌ BLOCKED'}`);
|
|
165
|
+
if (status.secrets.blockers.length > 0) {
|
|
166
|
+
for (const blocker of status.secrets.blockers) {
|
|
167
|
+
console.log(` - ${blocker}`);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
console.log(`\nVulnerabilities: ${status.vulnerabilities.ok ? '✅ OK' : (status.vulnerabilities.blocking ? '❌ BLOCKED' : '⚠️ WARN')}`);
|
|
172
|
+
if (status.vulnerabilities.blockers.length > 0) {
|
|
173
|
+
for (const blocker of status.vulnerabilities.blockers) {
|
|
174
|
+
console.log(` - ${blocker}`);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
if (status.vulnerabilities.warnings.length > 0) {
|
|
178
|
+
for (const warning of status.vulnerabilities.warnings) {
|
|
179
|
+
console.log(` ⚠️ ${warning}`);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
console.log(`\nSupply-chain: ${status.supplychain.ok ? '✅ OK' : '❌ BLOCKED'}`);
|
|
184
|
+
if (status.supplychain.blockers.length > 0) {
|
|
185
|
+
for (const blocker of status.supplychain.blockers) {
|
|
186
|
+
console.log(` - ${blocker}`);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
console.log(`\nOverall: ${allOk ? '✅ SECURITY-OK' : '❌ SECURITY-BLOCKED'}`);
|
|
191
|
+
console.log(`\nSee unified report: ${unifiedReportPath}`);
|
|
192
|
+
console.log('='.repeat(80) + '\n');
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Exit codes: 0 = SECURITY-OK, 6 = SECURITY-BLOCKED, 70 = Internal corruption
|
|
196
|
+
// NOT_AVAILABLE tools exit 0 only if policy allows (strict mode would exit 2)
|
|
197
|
+
const hasNotAvailable = status.vulnerabilities.availability === 'NOT_AVAILABLE';
|
|
198
|
+
const strictMode = process.env.VERAX_SECURITY_STRICT === '1';
|
|
199
|
+
|
|
200
|
+
if (allOk) {
|
|
201
|
+
process.exit(0);
|
|
202
|
+
} else if (hasNotAvailable && !strictMode) {
|
|
203
|
+
// NOT_AVAILABLE is not a blocker unless strict mode
|
|
204
|
+
process.exit(0);
|
|
205
|
+
} else if (hasInternalCorruption) {
|
|
206
|
+
process.exit(70);
|
|
207
|
+
} else {
|
|
208
|
+
process.exit(6);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|