@veraxhq/verax 0.2.1 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (213) hide show
  1. package/README.md +10 -6
  2. package/bin/verax.js +11 -11
  3. package/package.json +29 -8
  4. package/src/cli/commands/baseline.js +103 -0
  5. package/src/cli/commands/default.js +51 -6
  6. package/src/cli/commands/doctor.js +29 -0
  7. package/src/cli/commands/ga.js +246 -0
  8. package/src/cli/commands/gates.js +95 -0
  9. package/src/cli/commands/inspect.js +4 -2
  10. package/src/cli/commands/release-check.js +215 -0
  11. package/src/cli/commands/run.js +45 -6
  12. package/src/cli/commands/security-check.js +212 -0
  13. package/src/cli/commands/truth.js +113 -0
  14. package/src/cli/entry.js +30 -20
  15. package/src/cli/util/angular-component-extractor.js +179 -0
  16. package/src/cli/util/angular-navigation-detector.js +141 -0
  17. package/src/cli/util/angular-network-detector.js +161 -0
  18. package/src/cli/util/angular-state-detector.js +162 -0
  19. package/src/cli/util/ast-interactive-detector.js +544 -0
  20. package/src/cli/util/ast-network-detector.js +603 -0
  21. package/src/cli/util/ast-promise-extractor.js +581 -0
  22. package/src/cli/util/ast-usestate-detector.js +602 -0
  23. package/src/cli/util/atomic-write.js +12 -1
  24. package/src/cli/util/bootstrap-guard.js +86 -0
  25. package/src/cli/util/console-reporter.js +72 -0
  26. package/src/cli/util/detection-engine.js +105 -41
  27. package/src/cli/util/determinism-runner.js +124 -0
  28. package/src/cli/util/determinism-writer.js +129 -0
  29. package/src/cli/util/digest-engine.js +359 -0
  30. package/src/cli/util/dom-diff.js +226 -0
  31. package/src/cli/util/evidence-engine.js +287 -0
  32. package/src/cli/util/expectation-extractor.js +151 -5
  33. package/src/cli/util/findings-writer.js +3 -0
  34. package/src/cli/util/framework-detector.js +572 -0
  35. package/src/cli/util/idgen.js +1 -1
  36. package/src/cli/util/interaction-planner.js +529 -0
  37. package/src/cli/util/learn-writer.js +2 -0
  38. package/src/cli/util/ledger-writer.js +110 -0
  39. package/src/cli/util/monorepo-resolver.js +162 -0
  40. package/src/cli/util/observation-engine.js +127 -278
  41. package/src/cli/util/observe-writer.js +2 -0
  42. package/src/cli/util/project-discovery.js +284 -0
  43. package/src/cli/util/project-writer.js +2 -0
  44. package/src/cli/util/run-id.js +23 -27
  45. package/src/cli/util/run-resolver.js +64 -0
  46. package/src/cli/util/run-result.js +778 -0
  47. package/src/cli/util/selector-resolver.js +235 -0
  48. package/src/cli/util/source-requirement.js +55 -0
  49. package/src/cli/util/summary-writer.js +2 -0
  50. package/src/cli/util/svelte-navigation-detector.js +163 -0
  51. package/src/cli/util/svelte-network-detector.js +80 -0
  52. package/src/cli/util/svelte-sfc-extractor.js +146 -0
  53. package/src/cli/util/svelte-state-detector.js +242 -0
  54. package/src/cli/util/trust-activation-integration.js +496 -0
  55. package/src/cli/util/trust-activation-wrapper.js +85 -0
  56. package/src/cli/util/trust-integration-hooks.js +164 -0
  57. package/src/cli/util/types.js +153 -0
  58. package/src/cli/util/url-validation.js +40 -0
  59. package/src/cli/util/vue-navigation-detector.js +178 -0
  60. package/src/cli/util/vue-sfc-extractor.js +161 -0
  61. package/src/cli/util/vue-state-detector.js +215 -0
  62. package/src/types/fs-augment.d.ts +23 -0
  63. package/src/types/global.d.ts +137 -0
  64. package/src/types/internal-types.d.ts +35 -0
  65. package/src/verax/cli/init.js +4 -18
  66. package/src/verax/core/action-classifier.js +4 -3
  67. package/src/verax/core/artifacts/registry.js +139 -0
  68. package/src/verax/core/artifacts/verifier.js +990 -0
  69. package/src/verax/core/baseline/baseline.enforcer.js +137 -0
  70. package/src/verax/core/baseline/baseline.snapshot.js +233 -0
  71. package/src/verax/core/capabilities/gates.js +505 -0
  72. package/src/verax/core/capabilities/registry.js +475 -0
  73. package/src/verax/core/confidence/confidence-compute.js +144 -0
  74. package/src/verax/core/confidence/confidence-invariants.js +234 -0
  75. package/src/verax/core/confidence/confidence-report-writer.js +112 -0
  76. package/src/verax/core/confidence/confidence-weights.js +44 -0
  77. package/src/verax/core/confidence/confidence.defaults.js +65 -0
  78. package/src/verax/core/confidence/confidence.loader.js +80 -0
  79. package/src/verax/core/confidence/confidence.schema.js +94 -0
  80. package/src/verax/core/confidence-engine-refactor.js +489 -0
  81. package/src/verax/core/confidence-engine.js +625 -0
  82. package/src/verax/core/contracts/index.js +29 -0
  83. package/src/verax/core/contracts/types.js +186 -0
  84. package/src/verax/core/contracts/validators.js +456 -0
  85. package/src/verax/core/decisions/decision.trace.js +278 -0
  86. package/src/verax/core/determinism/contract-writer.js +89 -0
  87. package/src/verax/core/determinism/contract.js +139 -0
  88. package/src/verax/core/determinism/diff.js +405 -0
  89. package/src/verax/core/determinism/engine.js +222 -0
  90. package/src/verax/core/determinism/finding-identity.js +149 -0
  91. package/src/verax/core/determinism/normalize.js +466 -0
  92. package/src/verax/core/determinism/report-writer.js +93 -0
  93. package/src/verax/core/determinism/run-fingerprint.js +123 -0
  94. package/src/verax/core/dynamic-route-intelligence.js +529 -0
  95. package/src/verax/core/evidence/evidence-capture-service.js +308 -0
  96. package/src/verax/core/evidence/evidence-intent-ledger.js +166 -0
  97. package/src/verax/core/evidence-builder.js +487 -0
  98. package/src/verax/core/execution-mode-context.js +77 -0
  99. package/src/verax/core/execution-mode-detector.js +192 -0
  100. package/src/verax/core/failures/exit-codes.js +88 -0
  101. package/src/verax/core/failures/failure-summary.js +76 -0
  102. package/src/verax/core/failures/failure.factory.js +225 -0
  103. package/src/verax/core/failures/failure.ledger.js +133 -0
  104. package/src/verax/core/failures/failure.types.js +196 -0
  105. package/src/verax/core/failures/index.js +10 -0
  106. package/src/verax/core/ga/ga-report-writer.js +43 -0
  107. package/src/verax/core/ga/ga.artifact.js +49 -0
  108. package/src/verax/core/ga/ga.contract.js +435 -0
  109. package/src/verax/core/ga/ga.enforcer.js +87 -0
  110. package/src/verax/core/guardrails/guardrails-report-writer.js +109 -0
  111. package/src/verax/core/guardrails/policy.defaults.js +210 -0
  112. package/src/verax/core/guardrails/policy.loader.js +84 -0
  113. package/src/verax/core/guardrails/policy.schema.js +110 -0
  114. package/src/verax/core/guardrails/truth-reconciliation.js +136 -0
  115. package/src/verax/core/guardrails-engine.js +505 -0
  116. package/src/verax/core/incremental-store.js +1 -0
  117. package/src/verax/core/integrity/budget.js +138 -0
  118. package/src/verax/core/integrity/determinism.js +342 -0
  119. package/src/verax/core/integrity/integrity.js +208 -0
  120. package/src/verax/core/integrity/poisoning.js +108 -0
  121. package/src/verax/core/integrity/transaction.js +140 -0
  122. package/src/verax/core/observe/run-timeline.js +318 -0
  123. package/src/verax/core/perf/perf.contract.js +186 -0
  124. package/src/verax/core/perf/perf.display.js +65 -0
  125. package/src/verax/core/perf/perf.enforcer.js +91 -0
  126. package/src/verax/core/perf/perf.monitor.js +209 -0
  127. package/src/verax/core/perf/perf.report.js +200 -0
  128. package/src/verax/core/pipeline-tracker.js +243 -0
  129. package/src/verax/core/product-definition.js +127 -0
  130. package/src/verax/core/release/provenance.builder.js +130 -0
  131. package/src/verax/core/release/release-report-writer.js +40 -0
  132. package/src/verax/core/release/release.enforcer.js +164 -0
  133. package/src/verax/core/release/reproducibility.check.js +222 -0
  134. package/src/verax/core/release/sbom.builder.js +292 -0
  135. package/src/verax/core/replay-validator.js +2 -0
  136. package/src/verax/core/replay.js +4 -0
  137. package/src/verax/core/report/cross-index.js +195 -0
  138. package/src/verax/core/report/human-summary.js +362 -0
  139. package/src/verax/core/route-intelligence.js +420 -0
  140. package/src/verax/core/run-id.js +6 -3
  141. package/src/verax/core/run-manifest.js +4 -3
  142. package/src/verax/core/security/secrets.scan.js +329 -0
  143. package/src/verax/core/security/security-report.js +50 -0
  144. package/src/verax/core/security/security.enforcer.js +128 -0
  145. package/src/verax/core/security/supplychain.defaults.json +38 -0
  146. package/src/verax/core/security/supplychain.policy.js +334 -0
  147. package/src/verax/core/security/vuln.scan.js +265 -0
  148. package/src/verax/core/truth/truth.certificate.js +252 -0
  149. package/src/verax/core/ui-feedback-intelligence.js +481 -0
  150. package/src/verax/detect/conditional-ui-silent-failure.js +84 -0
  151. package/src/verax/detect/confidence-engine.js +62 -34
  152. package/src/verax/detect/confidence-helper.js +34 -0
  153. package/src/verax/detect/dynamic-route-findings.js +338 -0
  154. package/src/verax/detect/expectation-chain-detector.js +417 -0
  155. package/src/verax/detect/expectation-model.js +2 -2
  156. package/src/verax/detect/failure-cause-inference.js +293 -0
  157. package/src/verax/detect/findings-writer.js +131 -35
  158. package/src/verax/detect/flow-detector.js +2 -2
  159. package/src/verax/detect/form-silent-failure.js +98 -0
  160. package/src/verax/detect/index.js +46 -5
  161. package/src/verax/detect/invariants-enforcer.js +147 -0
  162. package/src/verax/detect/journey-stall-detector.js +558 -0
  163. package/src/verax/detect/navigation-silent-failure.js +82 -0
  164. package/src/verax/detect/problem-aggregator.js +361 -0
  165. package/src/verax/detect/route-findings.js +219 -0
  166. package/src/verax/detect/summary-writer.js +477 -0
  167. package/src/verax/detect/test-failure-cause-inference.js +314 -0
  168. package/src/verax/detect/ui-feedback-findings.js +207 -0
  169. package/src/verax/detect/view-switch-correlator.js +242 -0
  170. package/src/verax/flow/flow-engine.js +2 -1
  171. package/src/verax/flow/flow-spec.js +0 -6
  172. package/src/verax/index.js +4 -0
  173. package/src/verax/intel/ts-program.js +1 -0
  174. package/src/verax/intel/vue-navigation-extractor.js +3 -0
  175. package/src/verax/learn/action-contract-extractor.js +3 -0
  176. package/src/verax/learn/ast-contract-extractor.js +1 -1
  177. package/src/verax/learn/flow-extractor.js +1 -0
  178. package/src/verax/learn/project-detector.js +5 -0
  179. package/src/verax/learn/react-router-extractor.js +2 -0
  180. package/src/verax/learn/source-instrumenter.js +1 -0
  181. package/src/verax/learn/state-extractor.js +2 -1
  182. package/src/verax/learn/static-extractor.js +1 -0
  183. package/src/verax/observe/coverage-gaps.js +132 -0
  184. package/src/verax/observe/expectation-handler.js +126 -0
  185. package/src/verax/observe/incremental-skip.js +46 -0
  186. package/src/verax/observe/index.js +51 -155
  187. package/src/verax/observe/interaction-executor.js +192 -0
  188. package/src/verax/observe/interaction-runner.js +782 -513
  189. package/src/verax/observe/network-firewall.js +86 -0
  190. package/src/verax/observe/observation-builder.js +169 -0
  191. package/src/verax/observe/observe-context.js +205 -0
  192. package/src/verax/observe/observe-helpers.js +192 -0
  193. package/src/verax/observe/observe-runner.js +230 -0
  194. package/src/verax/observe/observers/budget-observer.js +185 -0
  195. package/src/verax/observe/observers/console-observer.js +102 -0
  196. package/src/verax/observe/observers/coverage-observer.js +107 -0
  197. package/src/verax/observe/observers/interaction-observer.js +471 -0
  198. package/src/verax/observe/observers/navigation-observer.js +132 -0
  199. package/src/verax/observe/observers/network-observer.js +87 -0
  200. package/src/verax/observe/observers/safety-observer.js +82 -0
  201. package/src/verax/observe/observers/ui-feedback-observer.js +99 -0
  202. package/src/verax/observe/page-traversal.js +138 -0
  203. package/src/verax/observe/snapshot-ops.js +94 -0
  204. package/src/verax/observe/ui-feedback-detector.js +742 -0
  205. package/src/verax/scan-summary-writer.js +2 -0
  206. package/src/verax/shared/artifact-manager.js +25 -5
  207. package/src/verax/shared/caching.js +1 -0
  208. package/src/verax/shared/css-spinner-rules.js +204 -0
  209. package/src/verax/shared/expectation-tracker.js +1 -0
  210. package/src/verax/shared/view-switch-rules.js +208 -0
  211. package/src/verax/shared/zip-artifacts.js +6 -0
  212. package/src/verax/shared/config-loader.js +0 -169
  213. /package/src/verax/shared/{expectation-proof.js → expectation-validation.js} +0 -0
@@ -0,0 +1,95 @@
1
+ /**
2
+ * PHASE 19 — Capability Gates CLI Command
3
+ *
4
+ * Evaluates all capability gates and reports PASS/FAIL.
5
+ */
6
+
7
+ import {
8
+ evaluateAllCapabilityGates,
9
+ buildGateContext,
10
+ } from '../../verax/core/capabilities/gates.js';
11
+ import { resolve } from 'path';
12
+ import { fileURLToPath } from 'url';
13
+ import { dirname } from 'path';
14
+
15
+ const __filename = fileURLToPath(import.meta.url);
16
+ const __dirname = dirname(__filename);
17
+
18
+ /**
19
+ * PHASE 19: `verax gates` command
20
+ *
21
+ * @param {Object} options - Options
22
+ * @param {boolean} options.json - Output as JSON
23
+ * @param {boolean} options.verbose - Verbose output
24
+ */
25
+ export async function gatesCommand(options = { json: false, verbose: false }) {
26
+ const { json = false, verbose = false } = options;
27
+
28
+ // Pass explicit project root to ensure correct path resolution
29
+ const projectRoot = resolve(__dirname, '../../..');
30
+ const context = await buildGateContext({ projectRoot });
31
+ const result = evaluateAllCapabilityGates(context);
32
+
33
+ if (json) {
34
+ console.log(JSON.stringify({
35
+ pass: result.pass,
36
+ summary: result.summary,
37
+ perCapability: result.perCapability,
38
+ }, null, 2));
39
+ } else {
40
+ // Human-readable output
41
+ console.log('\n' + '='.repeat(80));
42
+ console.log('CAPABILITY GATES EVALUATION');
43
+ console.log('='.repeat(80));
44
+ console.log(`\nStatus: ${result.pass ? '✅ PASS' : '❌ FAIL'}`);
45
+ console.log(`Total Capabilities: ${result.summary.total}`);
46
+ console.log(`Passing: ${result.summary.pass}`);
47
+ console.log(`Failing: ${result.summary.fail}`);
48
+
49
+ if (!result.pass) {
50
+ console.log('\n' + '-'.repeat(80));
51
+ console.log('FAILING CAPABILITIES:');
52
+ console.log('-'.repeat(80));
53
+
54
+ for (const failure of result.summary.allFailures) {
55
+ console.log(`\n❌ ${failure.capabilityId}`);
56
+ for (const gateFailure of failure.failures) {
57
+ console.log(` └─ ${gateFailure.reasonCode}: ${gateFailure.message}`);
58
+ }
59
+ if (failure.warnings && failure.warnings.length > 0) {
60
+ for (const warning of failure.warnings) {
61
+ console.log(` ⚠️ ${warning.reasonCode}: ${warning.message}`);
62
+ }
63
+ }
64
+ }
65
+
66
+ if (verbose) {
67
+ console.log('\n' + '-'.repeat(80));
68
+ console.log('REQUIREMENTS BY MATURITY LEVEL:');
69
+ console.log('-'.repeat(80));
70
+ console.log('\nEXPERIMENTAL:');
71
+ console.log(' - Must exist in registry');
72
+ console.log(' - Must have at least 1 test matrix entry');
73
+ console.log('\nPARTIAL:');
74
+ console.log(' - All EXPERIMENTAL requirements');
75
+ console.log(' - Must have at least 1 realistic fixture mapping');
76
+ console.log(' - Must have documentation');
77
+ console.log('\nSTABLE:');
78
+ console.log(' - All PARTIAL requirements');
79
+ console.log(' - Must have determinism test coverage');
80
+ console.log(' - Must have artifact assertions in test matrix');
81
+ console.log(' - Must have guardrails coverage (if category requires it)');
82
+ }
83
+ } else {
84
+ console.log('\n✅ All capabilities meet their required gates!');
85
+ }
86
+
87
+ console.log('='.repeat(80) + '\n');
88
+ }
89
+
90
+ // Exit code: 0 if PASS, 2 if FAIL
91
+ if (!result.pass) {
92
+ process.exit(2);
93
+ }
94
+ }
95
+
@@ -37,13 +37,15 @@ export async function inspectCommand(runPath, options = {}) {
37
37
  let summary, findings;
38
38
 
39
39
  try {
40
- summary = JSON.parse(readFileSync(`${fullPath}/summary.json`, 'utf8'));
40
+ // @ts-expect-error - readFileSync with encoding returns string
41
+ summary = JSON.parse(readFileSync(`${fullPath}/summary.json`, 'utf-8'));
41
42
  } catch (error) {
42
43
  throw new DataError(`Failed to parse summary.json: ${error.message}`);
43
44
  }
44
45
 
45
46
  try {
46
- findings = JSON.parse(readFileSync(`${fullPath}/findings.json`, 'utf8'));
47
+ // @ts-expect-error - readFileSync with encoding returns string
48
+ findings = JSON.parse(readFileSync(`${fullPath}/findings.json`, 'utf-8'));
47
49
  } catch (error) {
48
50
  throw new DataError(`Failed to parse findings.json: ${error.message}`);
49
51
  }
@@ -0,0 +1,215 @@
1
+ /**
2
+ * PHASE 21.7 — Release Check CLI Command
3
+ *
4
+ * Checks release readiness: GA status, Provenance, SBOM, Reproducibility.
5
+ * Exit codes: 0 = RELEASE-READY, 5 = RELEASE-BLOCKED, 70 = Internal corruption
6
+ */
7
+
8
+ import { buildProvenance, writeProvenance } from '../../verax/core/release/provenance.builder.js';
9
+ import { buildSBOM, writeSBOM } from '../../verax/core/release/sbom.builder.js';
10
+ import { checkReproducibility, writeReproducibilityReport } from '../../verax/core/release/reproducibility.check.js';
11
+ import { checkGAStatus } from '../../verax/core/ga/ga.enforcer.js';
12
+ import { writeReleaseReport } from '../../verax/core/release/release-report-writer.js';
13
+ import { findLatestRunId } from '../util/run-resolver.js';
14
+ import { resolve } from 'path';
15
+ import { existsSync, readFileSync } from 'fs';
16
+
17
+ /**
18
+ * Check release readiness
19
+ *
20
+ * @param {Object} options - Options
21
+ * @param {boolean} [options.json] - Output as JSON
22
+ */
23
+ export async function releaseCheckCommand(options = {}) {
24
+ const { json = false } = options;
25
+ const projectDir = resolve(process.cwd());
26
+
27
+ const status = {
28
+ ga: { ok: false, status: 'UNKNOWN', blockers: [] },
29
+ provenance: { ok: false, exists: false, blockers: [] },
30
+ sbom: { ok: false, exists: false, blockers: [] },
31
+ reproducibility: { ok: false, verdict: 'UNKNOWN', blockers: [] }
32
+ };
33
+
34
+ // 1. Check GA status
35
+ try {
36
+ const runId = findLatestRunId(projectDir);
37
+ if (runId) {
38
+ const gaCheck = checkGAStatus(projectDir, runId);
39
+ status.ga.ok = gaCheck.ready;
40
+ status.ga.status = gaCheck.ready ? 'GA-READY' : 'GA-BLOCKED';
41
+ if (!gaCheck.ready && gaCheck.status?.blockers) {
42
+ status.ga.blockers = gaCheck.status.blockers.map(b => b.message);
43
+ }
44
+ } else {
45
+ status.ga.blockers.push('No runs found. Run a scan first.');
46
+ }
47
+ } catch (error) {
48
+ status.ga.blockers.push(`GA check failed: ${error.message}`);
49
+ }
50
+
51
+ // 2. Check Provenance
52
+ try {
53
+ const provenancePath = resolve(projectDir, 'release', 'release.provenance.json');
54
+ if (existsSync(provenancePath)) {
55
+ status.provenance.exists = true;
56
+ // @ts-expect-error - readFileSync with encoding returns string
57
+ const provenance = JSON.parse(readFileSync(provenancePath, 'utf-8'));
58
+
59
+ // Validate provenance structure
60
+ if (!provenance.version || !provenance.git || !provenance.env) {
61
+ status.provenance.blockers.push('Invalid provenance structure');
62
+ } else if (provenance.git.dirty) {
63
+ status.provenance.blockers.push('Provenance indicates dirty git repository');
64
+ } else if (provenance.gaStatus !== 'GA-READY') {
65
+ status.provenance.blockers.push(`GA status is ${provenance.gaStatus}, not GA-READY`);
66
+ } else {
67
+ status.provenance.ok = true;
68
+ }
69
+ } else {
70
+ // Try to build it
71
+ try {
72
+ const provenance = await buildProvenance(projectDir);
73
+ writeProvenance(projectDir, provenance);
74
+ status.provenance.exists = true;
75
+ status.provenance.ok = true;
76
+ } catch (error) {
77
+ status.provenance.blockers.push(`Cannot build provenance: ${error.message}`);
78
+ }
79
+ }
80
+ } catch (error) {
81
+ status.provenance.blockers.push(`Provenance check failed: ${error.message}`);
82
+ }
83
+
84
+ // 3. Check SBOM
85
+ try {
86
+ const sbomPath = resolve(projectDir, 'release', 'sbom.json');
87
+ if (existsSync(sbomPath)) {
88
+ status.sbom.exists = true;
89
+ // @ts-expect-error - readFileSync with encoding returns string
90
+ const sbom = JSON.parse(readFileSync(sbomPath, 'utf-8'));
91
+
92
+ // Validate SBOM structure
93
+ if (!sbom.bomFormat || !sbom.components || !Array.isArray(sbom.components)) {
94
+ status.sbom.blockers.push('Invalid SBOM structure');
95
+ } else if (sbom.components.length === 0) {
96
+ status.sbom.blockers.push('SBOM has no components');
97
+ } else {
98
+ status.sbom.ok = true;
99
+ }
100
+ } else {
101
+ // Try to build it
102
+ try {
103
+ const sbom = await buildSBOM(projectDir);
104
+ writeSBOM(projectDir, sbom);
105
+ status.sbom.exists = true;
106
+ status.sbom.ok = true;
107
+ } catch (error) {
108
+ status.sbom.blockers.push(`Cannot build SBOM: ${error.message}`);
109
+ }
110
+ }
111
+ } catch (error) {
112
+ status.sbom.blockers.push(`SBOM check failed: ${error.message}`);
113
+ }
114
+
115
+ // 4. Check Reproducibility
116
+ try {
117
+ const report = await checkReproducibility(projectDir);
118
+ writeReproducibilityReport(projectDir, report);
119
+
120
+ if (report.verdict === 'REPRODUCIBLE') {
121
+ status.reproducibility.ok = true;
122
+ status.reproducibility.verdict = 'REPRODUCIBLE';
123
+ } else {
124
+ status.reproducibility.verdict = 'NON_REPRODUCIBLE';
125
+ if (report.differences && report.differences.length > 0) {
126
+ status.reproducibility.blockers = report.differences.map(d => d.message);
127
+ } else {
128
+ status.reproducibility.blockers.push('Build is not reproducible');
129
+ }
130
+ }
131
+ } catch (error) {
132
+ status.reproducibility.blockers.push(`Reproducibility check failed: ${error.message}`);
133
+ }
134
+
135
+ // Determine overall status
136
+ const allOk = status.ga.ok && status.provenance.ok && status.sbom.ok && status.reproducibility.ok;
137
+ const hasInternalCorruption =
138
+ status.ga.blockers.some(b => b.includes('corruption') || b.includes('INTERNAL')) ||
139
+ status.provenance.blockers.some(b => b.includes('corruption')) ||
140
+ status.sbom.blockers.some(b => b.includes('corruption'));
141
+
142
+ // Write release report
143
+ const releaseStatus = {
144
+ releaseReady: allOk,
145
+ status,
146
+ summary: {
147
+ ga: status.ga.ok ? 'OK' : 'BLOCKED',
148
+ provenance: status.provenance.ok ? 'OK' : 'BLOCKED',
149
+ sbom: status.sbom.ok ? 'OK' : 'BLOCKED',
150
+ reproducibility: status.reproducibility.ok ? 'OK' : 'BLOCKED'
151
+ }
152
+ };
153
+ const releaseReportPath = writeReleaseReport(projectDir, releaseStatus);
154
+
155
+ // Output
156
+ if (json) {
157
+ console.log(JSON.stringify({
158
+ releaseReady: allOk,
159
+ status,
160
+ summary: {
161
+ ga: status.ga.ok ? 'OK' : 'BLOCKED',
162
+ provenance: status.provenance.ok ? 'OK' : 'BLOCKED',
163
+ sbom: status.sbom.ok ? 'OK' : 'BLOCKED',
164
+ reproducibility: status.reproducibility.ok ? 'OK' : 'BLOCKED'
165
+ },
166
+ reportPath: releaseReportPath
167
+ }, null, 2));
168
+ } else {
169
+ console.log('\n' + '='.repeat(80));
170
+ console.log('RELEASE READINESS CHECK');
171
+ console.log('='.repeat(80));
172
+
173
+ console.log(`\nGA Status: ${status.ga.ok ? '✅ READY' : '❌ BLOCKED'}`);
174
+ if (status.ga.blockers.length > 0) {
175
+ for (const blocker of status.ga.blockers) {
176
+ console.log(` - ${blocker}`);
177
+ }
178
+ }
179
+
180
+ console.log(`\nProvenance: ${status.provenance.ok ? '✅ OK' : '❌ BLOCKED'}`);
181
+ if (status.provenance.blockers.length > 0) {
182
+ for (const blocker of status.provenance.blockers) {
183
+ console.log(` - ${blocker}`);
184
+ }
185
+ }
186
+
187
+ console.log(`\nSBOM: ${status.sbom.ok ? '✅ OK' : '❌ BLOCKED'}`);
188
+ if (status.sbom.blockers.length > 0) {
189
+ for (const blocker of status.sbom.blockers) {
190
+ console.log(` - ${blocker}`);
191
+ }
192
+ }
193
+
194
+ console.log(`\nReproducibility: ${status.reproducibility.ok ? '✅ REPRODUCIBLE' : '❌ NON_REPRODUCIBLE'}`);
195
+ if (status.reproducibility.blockers.length > 0) {
196
+ for (const blocker of status.reproducibility.blockers) {
197
+ console.log(` - ${blocker}`);
198
+ }
199
+ }
200
+
201
+ console.log(`\nOverall: ${allOk ? '✅ RELEASE-READY' : '❌ RELEASE-BLOCKED'}`);
202
+ console.log(`\nSee report: ${releaseReportPath}`);
203
+ console.log('='.repeat(80) + '\n');
204
+ }
205
+
206
+ // Exit codes: 0 = RELEASE-READY, 2 = RELEASE-BLOCKED, 70 = Internal corruption
207
+ if (allOk) {
208
+ process.exit(0);
209
+ } else if (hasInternalCorruption) {
210
+ process.exit(70);
211
+ } else {
212
+ process.exit(2);
213
+ }
214
+ }
215
+
@@ -17,6 +17,8 @@ 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 { saveDigest } from '../util/digest-engine.js';
21
+ import { ARTIFACT_REGISTRY, getArtifactVersions } from '../../verax/core/artifacts/registry.js';
20
22
 
21
23
  const __filename = fileURLToPath(import.meta.url);
22
24
  const __dirname = dirname(__filename);
@@ -24,7 +26,8 @@ const __dirname = dirname(__filename);
24
26
  function getVersion() {
25
27
  try {
26
28
  const pkgPath = resolve(__dirname, '../../../package.json');
27
- const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'));
29
+ // @ts-expect-error - readFileSync with encoding returns string
30
+ const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
28
31
  return pkg.version;
29
32
  } catch {
30
33
  return '0.2.0';
@@ -92,6 +95,8 @@ export async function runCommand(options) {
92
95
  try {
93
96
  const failedAt = new Date().toISOString();
94
97
  atomicWriteJson(paths.runStatusJson, {
98
+ contractVersion: 1,
99
+ artifactVersions: getArtifactVersions(),
95
100
  status: 'FAILED',
96
101
  runId,
97
102
  startedAt,
@@ -100,6 +105,7 @@ export async function runCommand(options) {
100
105
  });
101
106
 
102
107
  atomicWriteJson(paths.runMetaJson, {
108
+ contractVersion: ARTIFACT_REGISTRY.runMeta.contractVersion,
103
109
  veraxVersion: getVersion(),
104
110
  nodeVersion: process.version,
105
111
  platform: process.platform,
@@ -147,7 +153,7 @@ export async function runCommand(options) {
147
153
 
148
154
  try {
149
155
  // Generate run ID
150
- runId = generateRunId();
156
+ runId = generateRunId(url);
151
157
  if (verbose && !json) console.log(`Run ID: ${runId}`);
152
158
 
153
159
  paths = getRunPaths(projectRoot, out, runId);
@@ -197,6 +203,8 @@ export async function runCommand(options) {
197
203
  startedAt = now.toISOString();
198
204
 
199
205
  atomicWriteJson(paths.runStatusJson, {
206
+ contractVersion: 1,
207
+ artifactVersions: getArtifactVersions(),
200
208
  status: 'RUNNING',
201
209
  runId,
202
210
  startedAt,
@@ -204,6 +212,7 @@ export async function runCommand(options) {
204
212
 
205
213
  // Write metadata
206
214
  atomicWriteJson(paths.runMetaJson, {
215
+ contractVersion: ARTIFACT_REGISTRY.runMeta.contractVersion,
207
216
  veraxVersion: getVersion(),
208
217
  nodeVersion: process.version,
209
218
  platform: process.platform,
@@ -291,7 +300,8 @@ export async function runCommand(options) {
291
300
  paths.evidenceDir,
292
301
  (progress) => {
293
302
  events.emit(progress.event, progress);
294
- }
303
+ },
304
+ {}
295
305
  ),
296
306
  'Observe'
297
307
  );
@@ -404,6 +414,8 @@ export async function runCommand(options) {
404
414
 
405
415
  // Write completed status
406
416
  atomicWriteJson(paths.runStatusJson, {
417
+ contractVersion: 1,
418
+ artifactVersions: getArtifactVersions(),
407
419
  status: 'COMPLETE',
408
420
  runId,
409
421
  startedAt,
@@ -412,6 +424,7 @@ export async function runCommand(options) {
412
424
 
413
425
  // Update metadata with completion time
414
426
  atomicWriteJson(paths.runMetaJson, {
427
+ contractVersion: ARTIFACT_REGISTRY.runMeta.contractVersion,
415
428
  veraxVersion: getVersion(),
416
429
  nodeVersion: process.version,
417
430
  platform: process.platform,
@@ -481,6 +494,11 @@ export async function runCommand(options) {
481
494
  // Write observe results
482
495
  writeObserveJson(paths.baseDir, observeData);
483
496
 
497
+ // H5: Write deterministic digest for reproducibility proof
498
+ if (observeData && observeData.digest) {
499
+ saveDigest(resolve(paths.baseDir, 'run.digest.json'), observeData.digest);
500
+ }
501
+
484
502
  events.emit('phase:completed', {
485
503
  phase: 'Finalize Artifacts',
486
504
  message: 'Run artifacts written',
@@ -508,9 +526,27 @@ export async function runCommand(options) {
508
526
 
509
527
  // Print summary if not JSON mode
510
528
  if (!json) {
511
- console.log('\nRun complete.');
512
- console.log(`Run ID: ${runId}`);
513
- console.log(`Artifacts: ${paths.baseDir}`);
529
+ const relativePath = paths.baseDir.replace(/\\/g, '/').split('/').slice(-1)[0];
530
+ console.log('');
531
+ console.log('VERAX — Silent Failure Detection');
532
+ console.log('');
533
+ console.log(`✔ URL: ${url}`);
534
+ console.log('');
535
+ console.log('Learn phase:');
536
+ console.log(` → Extracted ${expectations.length} promises`);
537
+ console.log('');
538
+ console.log('Observe phase:');
539
+ console.log(` → Executed ${observeData.stats?.attempted || 0} interactions`);
540
+ console.log(` → Observed: ${observeData.stats?.observed || 0}/${observeData.stats?.attempted || 0}`);
541
+ console.log('');
542
+ console.log('Detect phase:');
543
+ console.log(` → Silent failures: ${detectData.stats?.silentFailures || 0}`);
544
+ console.log(` → Unproven: ${detectData.stats?.unproven || 0}`);
545
+ console.log(` → Coverage gaps: ${detectData.stats?.coverageGaps || 0}`);
546
+ console.log('');
547
+ console.log('Artifacts written to:');
548
+ console.log(` .verax/runs/${relativePath}/`);
549
+ console.log('');
514
550
  }
515
551
 
516
552
  return { runId, paths, success: true };
@@ -528,6 +564,8 @@ export async function runCommand(options) {
528
564
  try {
529
565
  const failedAt = new Date().toISOString();
530
566
  atomicWriteJson(paths.runStatusJson, {
567
+ contractVersion: 1,
568
+ artifactVersions: getArtifactVersions(),
531
569
  status: 'FAILED',
532
570
  runId,
533
571
  startedAt,
@@ -537,6 +575,7 @@ export async function runCommand(options) {
537
575
 
538
576
  // Update metadata
539
577
  atomicWriteJson(paths.runMetaJson, {
578
+ contractVersion: ARTIFACT_REGISTRY.runMeta.contractVersion,
540
579
  veraxVersion: getVersion(),
541
580
  nodeVersion: process.version,
542
581
  platform: process.platform,
@@ -0,0 +1,212 @@
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 as _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
+ // @ts-expect-error - readFileSync with encoding returns string
120
+ secretsReport = JSON.parse(readFileSync(secretsPath, 'utf-8'));
121
+ }
122
+ } catch {
123
+ // Ignore
124
+ }
125
+
126
+ let vulnReportData = vulnResult;
127
+ let supplyChainReportData = supplyChainResult;
128
+
129
+ const unifiedReport = {
130
+ securityOk: allOk,
131
+ status,
132
+ summary: {
133
+ secrets: status.secrets.ok ? 'OK' : 'BLOCKED',
134
+ vulnerabilities: status.vulnerabilities.ok ? 'OK' : (status.vulnerabilities.blocking ? 'BLOCKED' : (status.vulnerabilities.availability === 'NOT_AVAILABLE' ? 'NOT_AVAILABLE' : 'WARN')),
135
+ supplychain: status.supplychain.ok ? 'OK' : 'BLOCKED'
136
+ },
137
+ secretsReport,
138
+ vulnReport: vulnReportData,
139
+ supplyChainReport: supplyChainReportData
140
+ };
141
+
142
+ const unifiedReportPath = writeSecurityReport(projectDir, unifiedReport);
143
+ const hasInternalCorruption =
144
+ status.secrets.blockers.some(b => b.includes('corruption') || b.includes('Internal')) ||
145
+ status.vulnerabilities.blockers.some(b => b.includes('corruption')) ||
146
+ status.supplychain.blockers.some(b => b.includes('corruption'));
147
+
148
+ // Output
149
+ if (json) {
150
+ console.log(JSON.stringify({
151
+ securityOk: allOk,
152
+ status,
153
+ summary: {
154
+ secrets: status.secrets.ok ? 'OK' : 'BLOCKED',
155
+ vulnerabilities: status.vulnerabilities.ok ? 'OK' : (status.vulnerabilities.blocking ? 'BLOCKED' : (status.vulnerabilities.availability === 'NOT_AVAILABLE' ? 'NOT_AVAILABLE' : 'WARN')),
156
+ supplychain: status.supplychain.ok ? 'OK' : 'BLOCKED'
157
+ },
158
+ unifiedReportPath: unifiedReportPath
159
+ }, null, 2));
160
+ } else {
161
+ console.log('\n' + '='.repeat(80));
162
+ console.log('SECURITY BASELINE CHECK');
163
+ console.log('='.repeat(80));
164
+
165
+ console.log(`\nSecrets: ${status.secrets.ok ? '✅ OK' : '❌ BLOCKED'}`);
166
+ if (status.secrets.blockers.length > 0) {
167
+ for (const blocker of status.secrets.blockers) {
168
+ console.log(` - ${blocker}`);
169
+ }
170
+ }
171
+
172
+ console.log(`\nVulnerabilities: ${status.vulnerabilities.ok ? '✅ OK' : (status.vulnerabilities.blocking ? '❌ BLOCKED' : '⚠️ WARN')}`);
173
+ if (status.vulnerabilities.blockers.length > 0) {
174
+ for (const blocker of status.vulnerabilities.blockers) {
175
+ console.log(` - ${blocker}`);
176
+ }
177
+ }
178
+ if (status.vulnerabilities.warnings.length > 0) {
179
+ for (const warning of status.vulnerabilities.warnings) {
180
+ console.log(` ⚠️ ${warning}`);
181
+ }
182
+ }
183
+
184
+ console.log(`\nSupply-chain: ${status.supplychain.ok ? '✅ OK' : '❌ BLOCKED'}`);
185
+ if (status.supplychain.blockers.length > 0) {
186
+ for (const blocker of status.supplychain.blockers) {
187
+ console.log(` - ${blocker}`);
188
+ }
189
+ }
190
+
191
+ console.log(`\nOverall: ${allOk ? '✅ SECURITY-OK' : '❌ SECURITY-BLOCKED'}`);
192
+ console.log(`\nSee unified report: ${unifiedReportPath}`);
193
+ console.log('='.repeat(80) + '\n');
194
+ }
195
+
196
+ // Exit codes: 0 = SECURITY-OK, 6 = SECURITY-BLOCKED, 70 = Internal corruption
197
+ // NOT_AVAILABLE tools exit 0 only if policy allows (strict mode would exit 2)
198
+ const hasNotAvailable = status.vulnerabilities.availability === 'NOT_AVAILABLE';
199
+ const strictMode = process.env.VERAX_SECURITY_STRICT === '1';
200
+
201
+ if (allOk) {
202
+ process.exit(0);
203
+ } else if (hasNotAvailable && !strictMode) {
204
+ // NOT_AVAILABLE is not a blocker unless strict mode
205
+ process.exit(0);
206
+ } else if (hasInternalCorruption) {
207
+ process.exit(70);
208
+ } else {
209
+ process.exit(6);
210
+ }
211
+ }
212
+