@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,487 @@
1
+ /**
2
+ * PHASE 16 — Evidence Hardening: Mandatory Evidence Packages
3
+ * PHASE 21.1 — Evidence Law Hard Lock: Unbreakable invariant enforcement
4
+ *
5
+ * Central evidence builder that creates standardized Evidence Packages
6
+ * for all findings. Ensures completeness and enforces Evidence Law.
7
+ *
8
+ * HARD LOCK RULES:
9
+ * - buildEvidencePackage() MUST return complete evidencePackage or throw EvidenceBuildError
10
+ * - No silent failures allowed
11
+ * - CONFIRMED findings without complete evidencePackage is IMPOSSIBLE
12
+ */
13
+
14
+ /**
15
+ * PHASE 16: Evidence Package Schema
16
+ *
17
+ * @typedef {Object} EvidencePackage
18
+ * @property {Object} trigger - What triggered the finding (AST/DOM source + context)
19
+ * @property {Object} before - Before state (screenshot, URL, DOM signature)
20
+ * @property {Object} action - Action trace (interaction + timing)
21
+ * @property {Object} after - After state (screenshot, URL, DOM signature)
22
+ * @property {Object} signals - All sensor signals (network, console, ui feedback, route)
23
+ * @property {Object} justification - Confidence reasons and verdict rationale
24
+ * @property {Array<string>} missingEvidence - Fields that are missing (if any)
25
+ * @property {boolean} isComplete - Whether all required fields are present
26
+ */
27
+
28
+ /**
29
+ * PHASE 16: Required fields for CONFIRMED findings
30
+ * PHASE 21.1: Hard lock - these fields are MANDATORY for CONFIRMED
31
+ */
32
+ const REQUIRED_FIELDS_CONFIRMED = [
33
+ 'trigger.source',
34
+ 'before.screenshot',
35
+ 'after.screenshot',
36
+ 'before.url',
37
+ 'after.url',
38
+ 'action.interaction',
39
+ 'signals.network',
40
+ 'signals.uiSignals',
41
+ ];
42
+
43
+ /**
44
+ * PHASE 21.1: Evidence Build Error
45
+ * Thrown when evidence building fails - must NOT be silently caught
46
+ */
47
+ export class EvidenceBuildError extends Error {
48
+ constructor(message, missingFields = [], evidencePackage = null) {
49
+ super(message);
50
+ this.name = 'EvidenceBuildError';
51
+ this.code = 'EVIDENCE_BUILD_FAILED';
52
+ this.missingFields = missingFields;
53
+ this.evidencePackage = evidencePackage;
54
+ }
55
+ }
56
+
57
+ /**
58
+ * PHASE 16: Build standardized Evidence Package
59
+ *
60
+ * @param {Object} params - Evidence building parameters
61
+ * @param {Object} params.expectation - Promise/expectation
62
+ * @param {Object} params.trace - Interaction trace
63
+ * @param {Object} params.evidence - Existing evidence (optional, will be merged)
64
+ * @param {Object} params.confidence - Confidence result (optional)
65
+ * @returns {Object} Evidence Package
66
+ */
67
+ export function buildEvidencePackage({ expectation, trace, evidence = {}, confidence = null }) {
68
+ const evidencePackage = {
69
+ trigger: buildTrigger(expectation, trace),
70
+ before: buildBeforeState(trace),
71
+ action: buildActionTrace(trace),
72
+ after: buildAfterState(trace),
73
+ signals: buildSignals(trace),
74
+ justification: buildJustification(expectation, trace, confidence),
75
+ missingEvidence: [],
76
+ isComplete: false,
77
+ };
78
+
79
+ // Check completeness
80
+ const missing = checkMissingEvidence(evidencePackage);
81
+ evidencePackage.missingEvidence = missing;
82
+ evidencePackage.isComplete = missing.length === 0;
83
+
84
+ // Merge with existing evidence if provided
85
+ if (evidence && Object.keys(evidence).length > 0) {
86
+ evidencePackage.trigger = { ...evidencePackage.trigger, ...(evidence.trigger || {}) };
87
+ evidencePackage.before = { ...evidencePackage.before, ...(evidence.before || {}) };
88
+ evidencePackage.after = { ...evidencePackage.after, ...(evidence.after || {}) };
89
+ evidencePackage.signals = { ...evidencePackage.signals, ...(evidence.signals || {}) };
90
+ }
91
+
92
+ return evidencePackage;
93
+ }
94
+
95
+ /**
96
+ * Build trigger section (AST/DOM source + context)
97
+ */
98
+ function buildTrigger(expectation, trace) {
99
+ const trigger = {
100
+ source: null,
101
+ context: null,
102
+ astSource: null,
103
+ domSelector: null,
104
+ };
105
+
106
+ if (expectation) {
107
+ trigger.source = {
108
+ file: expectation.source?.file || null,
109
+ line: expectation.source?.line || null,
110
+ column: expectation.source?.column || null,
111
+ };
112
+ trigger.context = expectation.source?.context || null;
113
+ trigger.astSource = expectation.source?.astSource ||
114
+ expectation.metadata?.astSource || null;
115
+ }
116
+
117
+ if (trace.interaction) {
118
+ trigger.domSelector = trace.interaction.selector || null;
119
+ }
120
+
121
+ return trigger;
122
+ }
123
+
124
+ /**
125
+ * Build before state (screenshot, URL, DOM signature)
126
+ */
127
+ function buildBeforeState(trace) {
128
+ const before = {
129
+ screenshot: null,
130
+ url: null,
131
+ domSignature: null,
132
+ };
133
+
134
+ if (trace.before) {
135
+ before.screenshot = trace.before.screenshot || null;
136
+ before.url = trace.before.url || null;
137
+ }
138
+
139
+ // Also check sensors for before state
140
+ if (trace.sensors?.navigation?.beforeUrl) {
141
+ before.url = trace.sensors.navigation.beforeUrl;
142
+ }
143
+
144
+ // DOM signature (hash)
145
+ if (trace.dom?.beforeHash) {
146
+ before.domSignature = trace.dom.beforeHash;
147
+ }
148
+
149
+ return before;
150
+ }
151
+
152
+ /**
153
+ * Build action trace (interaction + timing)
154
+ */
155
+ function buildActionTrace(trace) {
156
+ const action = {
157
+ interaction: null,
158
+ timing: null,
159
+ };
160
+
161
+ if (trace.interaction) {
162
+ action.interaction = {
163
+ type: trace.interaction.type || null,
164
+ selector: trace.interaction.selector || null,
165
+ label: trace.interaction.label || null,
166
+ href: trace.interaction.href || null,
167
+ };
168
+ }
169
+
170
+ // Timing information
171
+ if (trace.timing) {
172
+ action.timing = trace.timing;
173
+ } else if (trace.sensors?.timing) {
174
+ action.timing = trace.sensors.timing;
175
+ }
176
+
177
+ return action;
178
+ }
179
+
180
+ /**
181
+ * Build after state (screenshot, URL, DOM signature)
182
+ */
183
+ function buildAfterState(trace) {
184
+ const after = {
185
+ screenshot: null,
186
+ url: null,
187
+ domSignature: null,
188
+ };
189
+
190
+ if (trace.after) {
191
+ after.screenshot = trace.after.screenshot || null;
192
+ after.url = trace.after.url || null;
193
+ }
194
+
195
+ // Also check sensors for after state
196
+ if (trace.sensors?.navigation?.afterUrl) {
197
+ after.url = trace.sensors.navigation.afterUrl;
198
+ }
199
+
200
+ // DOM signature (hash)
201
+ if (trace.dom?.afterHash) {
202
+ after.domSignature = trace.dom.afterHash;
203
+ }
204
+
205
+ return after;
206
+ }
207
+
208
+ /**
209
+ * Build signals section (network, console, ui feedback, route)
210
+ */
211
+ function buildSignals(trace) {
212
+ const signals = {
213
+ network: null,
214
+ console: null,
215
+ uiSignals: null,
216
+ uiFeedback: null,
217
+ navigation: null,
218
+ route: null,
219
+ };
220
+
221
+ const sensors = trace.sensors || {};
222
+
223
+ if (sensors.network) {
224
+ signals.network = {
225
+ totalRequests: sensors.network.totalRequests || 0,
226
+ failedRequests: sensors.network.failedRequests || 0,
227
+ successfulRequests: sensors.network.successfulRequests || 0,
228
+ topFailedUrls: sensors.network.topFailedUrls || [],
229
+ hasNetworkActivity: sensors.network.hasNetworkActivity || false,
230
+ };
231
+ }
232
+
233
+ if (sensors.console) {
234
+ signals.console = {
235
+ errorCount: sensors.console.errorCount || 0,
236
+ errors: sensors.console.errors || 0,
237
+ warnings: sensors.console.warnings || 0,
238
+ hasErrors: sensors.console.hasErrors || false,
239
+ };
240
+ }
241
+
242
+ if (sensors.uiSignals) {
243
+ signals.uiSignals = {
244
+ changed: sensors.uiSignals.diff?.changed || false,
245
+ hasLoadingIndicator: sensors.uiSignals.after?.hasLoadingIndicator || false,
246
+ hasDialog: sensors.uiSignals.after?.hasDialog || false,
247
+ hasErrorSignal: sensors.uiSignals.after?.hasErrorSignal || false,
248
+ };
249
+ }
250
+
251
+ if (sensors.uiFeedback) {
252
+ signals.uiFeedback = {
253
+ overallUiFeedbackScore: sensors.uiFeedback.overallUiFeedbackScore || 0,
254
+ signals: sensors.uiFeedback.signals || {},
255
+ };
256
+ }
257
+
258
+ if (sensors.navigation) {
259
+ signals.navigation = {
260
+ urlChanged: sensors.navigation.urlChanged || false,
261
+ routerStateChanged: sensors.navigation.routerStateChanged || false,
262
+ beforeUrl: sensors.navigation.beforeUrl || null,
263
+ afterUrl: sensors.navigation.afterUrl || null,
264
+ };
265
+ }
266
+
267
+ return signals;
268
+ }
269
+
270
+ /**
271
+ * Build justification (confidence reasons and verdict rationale)
272
+ */
273
+ function buildJustification(expectation, trace, confidence) {
274
+ const justification = {
275
+ confidenceReasons: null,
276
+ verdictRationale: null,
277
+ confidenceScore: null,
278
+ confidenceLevel: null,
279
+ };
280
+
281
+ if (confidence) {
282
+ justification.confidenceReasons = confidence.topReasons || confidence.reasons || [];
283
+ justification.confidenceScore = confidence.score01 || confidence.score || null; // Contract v1: score01 canonical
284
+ justification.confidenceLevel = confidence.level || null;
285
+ }
286
+
287
+ // Verdict rationale from finding reason
288
+ if (trace.reason) {
289
+ justification.verdictRationale = trace.reason;
290
+ }
291
+
292
+ return justification;
293
+ }
294
+
295
+ /**
296
+ * Check for missing evidence fields
297
+ */
298
+ function checkMissingEvidence(evidencePackage) {
299
+ const missing = [];
300
+
301
+ // Check required fields for CONFIRMED findings
302
+ if (!evidencePackage.trigger.source || !evidencePackage.trigger.source.file) {
303
+ missing.push('trigger.source');
304
+ }
305
+
306
+ if (!evidencePackage.before.screenshot) {
307
+ missing.push('before.screenshot');
308
+ }
309
+
310
+ if (!evidencePackage.after.screenshot) {
311
+ missing.push('after.screenshot');
312
+ }
313
+
314
+ if (!evidencePackage.before.url) {
315
+ missing.push('before.url');
316
+ }
317
+
318
+ if (!evidencePackage.after.url) {
319
+ missing.push('after.url');
320
+ }
321
+
322
+ if (!evidencePackage.action.interaction || !evidencePackage.action.interaction.type) {
323
+ missing.push('action.interaction');
324
+ }
325
+
326
+ if (!evidencePackage.signals.network) {
327
+ missing.push('signals.network');
328
+ }
329
+
330
+ if (!evidencePackage.signals.uiSignals) {
331
+ missing.push('signals.uiSignals');
332
+ }
333
+
334
+ return missing;
335
+ }
336
+
337
+ /**
338
+ * PHASE 16: Validate evidence package completeness for CONFIRMED findings
339
+ *
340
+ * @param {Object} evidencePackage - Evidence package to validate
341
+ * @param {string} severity - Finding severity (CONFIRMED or SUSPECTED)
342
+ * @returns {Object} { isComplete, missingFields, shouldDowngrade }
343
+ */
344
+ export function validateEvidencePackage(evidencePackage, severity) {
345
+ const missing = checkMissingEvidence(evidencePackage);
346
+ const isComplete = missing.length === 0;
347
+
348
+ // CONFIRMED findings MUST have complete evidence
349
+ const shouldDowngrade = severity === 'CONFIRMED' && !isComplete;
350
+
351
+ return {
352
+ isComplete,
353
+ missingFields: missing,
354
+ shouldDowngrade,
355
+ downgradeReason: shouldDowngrade
356
+ ? `Evidence Law Violation: Missing required evidence fields: ${missing.join(', ')}`
357
+ : null,
358
+ };
359
+ }
360
+
361
+ /**
362
+ * PHASE 21.1: Strict validation for CONFIRMED findings
363
+ *
364
+ * HARD LOCK: If finding is CONFIRMED and evidencePackage is incomplete,
365
+ * this function throws EvidenceBuildError (fail closed, not open).
366
+ *
367
+ * @param {Object} evidencePackage - Evidence package to validate
368
+ * @param {string} severity - Finding severity (must be CONFIRMED for strict validation)
369
+ * @throws {EvidenceBuildError} If CONFIRMED finding has incomplete evidencePackage
370
+ */
371
+ export function validateEvidencePackageStrict(evidencePackage, severity) {
372
+ if (!evidencePackage || typeof evidencePackage !== 'object') {
373
+ throw new EvidenceBuildError(
374
+ 'Evidence Law Violation: evidencePackage is missing or invalid',
375
+ REQUIRED_FIELDS_CONFIRMED,
376
+ null
377
+ );
378
+ }
379
+
380
+ if (severity === 'CONFIRMED' || severity === 'CONFIRMED') {
381
+ const missing = checkMissingEvidence(evidencePackage);
382
+ const isComplete = missing.length === 0;
383
+
384
+ if (!isComplete) {
385
+ throw new EvidenceBuildError(
386
+ `Evidence Law Violation: CONFIRMED finding requires complete evidencePackage. Missing fields: ${missing.join(', ')}`,
387
+ missing,
388
+ evidencePackage
389
+ );
390
+ }
391
+
392
+ // Additional strict check: evidencePackage.isComplete must be true
393
+ if (evidencePackage.isComplete !== true) {
394
+ throw new EvidenceBuildError(
395
+ `Evidence Law Violation: CONFIRMED finding has evidencePackage.isComplete !== true`,
396
+ missing.length > 0 ? missing : REQUIRED_FIELDS_CONFIRMED,
397
+ evidencePackage
398
+ );
399
+ }
400
+ }
401
+
402
+ return {
403
+ isComplete: evidencePackage.isComplete === true,
404
+ missingFields: checkMissingEvidence(evidencePackage),
405
+ valid: true
406
+ };
407
+ }
408
+
409
+ /**
410
+ * PHASE 16: Build evidence package and enforce completeness
411
+ * PHASE 21.1: Hard lock - throws EvidenceBuildError on failure (no silent failures)
412
+ * PHASE 22: Evidence System Hardening - records evidence intent and capture failures
413
+ *
414
+ * @param {Object} finding - Finding object
415
+ * @param {Object} params - Evidence building parameters
416
+ * @param {Array<Object>} captureFailures - Array of EvidenceCaptureFailure objects (optional)
417
+ * @returns {Object} Finding with evidencePackage and potentially downgraded severity
418
+ * @throws {EvidenceBuildError} If evidence building fails or CONFIRMED finding has incomplete evidence
419
+ */
420
+ export function buildAndEnforceEvidencePackage(finding, params, captureFailures = []) {
421
+ // PHASE 21.1: Build evidence package - throws if it fails
422
+ let evidencePackage;
423
+ try {
424
+ evidencePackage = buildEvidencePackage(params);
425
+ } catch (error) {
426
+ // Re-throw as EvidenceBuildError if not already
427
+ if (error instanceof EvidenceBuildError) {
428
+ throw error;
429
+ }
430
+ throw new EvidenceBuildError(
431
+ `Evidence building failed: ${error.message}`,
432
+ REQUIRED_FIELDS_CONFIRMED,
433
+ null
434
+ );
435
+ }
436
+
437
+ // PHASE 22: Record capture failures in evidence package metadata
438
+ if (captureFailures && captureFailures.length > 0) {
439
+ evidencePackage.captureFailures = captureFailures.map(f => f.toJSON ? f.toJSON() : f);
440
+ }
441
+
442
+ // PHASE 21.1: Strict validation for CONFIRMED findings
443
+ const severity = finding.severity || finding.status || 'SUSPECTED';
444
+ if (severity === 'CONFIRMED') {
445
+ // Hard lock: CONFIRMED findings MUST have complete evidencePackage
446
+ validateEvidencePackageStrict(evidencePackage, severity);
447
+ // If we get here, evidencePackage is complete
448
+ }
449
+
450
+ // For SUSPECTED findings, allow partial evidence
451
+ const validation = validateEvidencePackage(evidencePackage, severity);
452
+
453
+ // PHASE 22: Downgrade CONFIRMED if evidence incomplete (with evidence intent tracking)
454
+ let finalSeverity = severity;
455
+ let downgradeReason = null;
456
+
457
+ if (validation.shouldDowngrade) {
458
+ finalSeverity = 'SUSPECTED';
459
+ downgradeReason = validation.downgradeReason;
460
+
461
+ // PHASE 22: Record evidence intent in downgrade reason
462
+ if (captureFailures && captureFailures.length > 0) {
463
+ const failureCodes = captureFailures.map(f => f.reasonCode || 'UNKNOWN').join(', ');
464
+ downgradeReason += ` [Evidence Intent: Capture failures: ${failureCodes}]`;
465
+ }
466
+
467
+ // PHASE 22: Record missing fields in downgrade reason
468
+ if (validation.missingFields && validation.missingFields.length > 0) {
469
+ downgradeReason += ` [Missing fields: ${validation.missingFields.join(', ')}]`;
470
+ }
471
+ }
472
+
473
+ // Attach evidence package to finding
474
+ return {
475
+ ...finding,
476
+ severity: finalSeverity,
477
+ evidencePackage,
478
+ evidenceCompleteness: {
479
+ isComplete: validation.isComplete,
480
+ missingFields: validation.missingFields,
481
+ downgraded: validation.shouldDowngrade,
482
+ downgradeReason,
483
+ captureFailures: captureFailures.length > 0 ? captureFailures.map(f => f.toJSON ? f.toJSON() : f) : []
484
+ },
485
+ };
486
+ }
487
+
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Execution Mode Context Manager
3
+ *
4
+ * Manages execution mode detection and provides context throughout the scan pipeline.
5
+ * Responsible for:
6
+ * - Detecting mode at scan start
7
+ * - Passing mode through all phases
8
+ * - Applying ceilings to confidence calculations
9
+ * - Injecting mode explanations into output
10
+ */
11
+
12
+ import { detectExecutionMode, applyConfidenceCeiling, CONFIDENCE_CEILINGS as _CONFIDENCE_CEILINGS } from './execution-mode-detector.js';
13
+
14
+ /**
15
+ * Execution mode context object
16
+ */
17
+ class ExecutionModeContext {
18
+ constructor(mode, ceiling, explanation, reason) {
19
+ this.mode = mode;
20
+ this.ceiling = ceiling;
21
+ this.explanation = explanation;
22
+ this.reason = reason;
23
+ }
24
+
25
+ /**
26
+ * Apply ceiling to a confidence score
27
+ * @param {number} score - Confidence score (0..1)
28
+ * @returns {number} - Capped score
29
+ */
30
+ applyCeiling(score) {
31
+ return applyConfidenceCeiling(score, this.mode);
32
+ }
33
+
34
+ /**
35
+ * Get CLI explanation for output
36
+ * @returns {string} - Human-readable explanation
37
+ */
38
+ getCliExplanation() {
39
+ const ceilingPct = Math.round(this.ceiling * 100);
40
+ if (this.mode === 'WEB_SCAN_LIMITED') {
41
+ return `Running ${this.mode} mode: analyzing URL without access to source code. Confidence limited to ${ceilingPct}%.`;
42
+ } else {
43
+ return `Running ${this.mode} mode: full project analysis with source code available.`;
44
+ }
45
+ }
46
+
47
+ /**
48
+ * Get JSON-serializable context
49
+ * @returns {Object}
50
+ */
51
+ toJSON() {
52
+ return {
53
+ mode: this.mode,
54
+ ceiling: this.ceiling,
55
+ explanation: this.explanation,
56
+ reason: this.reason
57
+ };
58
+ }
59
+ }
60
+
61
+ /**
62
+ * Detect and create execution mode context for a scan
63
+ * @param {string} srcPath - Source code path
64
+ * @param {string} url - Target URL
65
+ * @returns {ExecutionModeContext}
66
+ */
67
+ export function createExecutionModeContext(srcPath, url) {
68
+ const modeInfo = detectExecutionMode(srcPath, url);
69
+ return new ExecutionModeContext(
70
+ modeInfo.mode,
71
+ modeInfo.ceiling,
72
+ modeInfo.explanation,
73
+ modeInfo.reason
74
+ );
75
+ }
76
+
77
+ export { ExecutionModeContext };