@vibecheckai/cli 3.2.0 → 3.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/bin/runners/lib/agent-firewall/change-packet/builder.js +214 -0
  2. package/bin/runners/lib/agent-firewall/change-packet/schema.json +228 -0
  3. package/bin/runners/lib/agent-firewall/change-packet/store.js +200 -0
  4. package/bin/runners/lib/agent-firewall/claims/claim-types.js +21 -0
  5. package/bin/runners/lib/agent-firewall/claims/extractor.js +214 -0
  6. package/bin/runners/lib/agent-firewall/claims/patterns.js +24 -0
  7. package/bin/runners/lib/agent-firewall/evidence/auth-evidence.js +88 -0
  8. package/bin/runners/lib/agent-firewall/evidence/contract-evidence.js +75 -0
  9. package/bin/runners/lib/agent-firewall/evidence/env-evidence.js +118 -0
  10. package/bin/runners/lib/agent-firewall/evidence/resolver.js +102 -0
  11. package/bin/runners/lib/agent-firewall/evidence/route-evidence.js +142 -0
  12. package/bin/runners/lib/agent-firewall/evidence/side-effect-evidence.js +145 -0
  13. package/bin/runners/lib/agent-firewall/fs-hook/daemon.js +19 -0
  14. package/bin/runners/lib/agent-firewall/fs-hook/installer.js +87 -0
  15. package/bin/runners/lib/agent-firewall/fs-hook/watcher.js +184 -0
  16. package/bin/runners/lib/agent-firewall/git-hook/pre-commit.js +163 -0
  17. package/bin/runners/lib/agent-firewall/ide-extension/cursor.js +107 -0
  18. package/bin/runners/lib/agent-firewall/ide-extension/vscode.js +68 -0
  19. package/bin/runners/lib/agent-firewall/ide-extension/windsurf.js +66 -0
  20. package/bin/runners/lib/agent-firewall/interceptor/base.js +304 -0
  21. package/bin/runners/lib/agent-firewall/interceptor/cursor.js +35 -0
  22. package/bin/runners/lib/agent-firewall/interceptor/vscode.js +35 -0
  23. package/bin/runners/lib/agent-firewall/interceptor/windsurf.js +34 -0
  24. package/bin/runners/lib/agent-firewall/policy/default-policy.json +84 -0
  25. package/bin/runners/lib/agent-firewall/policy/engine.js +72 -0
  26. package/bin/runners/lib/agent-firewall/policy/loader.js +143 -0
  27. package/bin/runners/lib/agent-firewall/policy/rules/auth-drift.js +50 -0
  28. package/bin/runners/lib/agent-firewall/policy/rules/contract-drift.js +50 -0
  29. package/bin/runners/lib/agent-firewall/policy/rules/fake-success.js +61 -0
  30. package/bin/runners/lib/agent-firewall/policy/rules/ghost-env.js +50 -0
  31. package/bin/runners/lib/agent-firewall/policy/rules/ghost-route.js +50 -0
  32. package/bin/runners/lib/agent-firewall/policy/rules/scope.js +93 -0
  33. package/bin/runners/lib/agent-firewall/policy/rules/unsafe-side-effect.js +57 -0
  34. package/bin/runners/lib/agent-firewall/policy/schema.json +183 -0
  35. package/bin/runners/lib/agent-firewall/policy/verdict.js +54 -0
  36. package/bin/runners/lib/agent-firewall/truthpack/index.js +67 -0
  37. package/bin/runners/lib/agent-firewall/truthpack/loader.js +116 -0
  38. package/bin/runners/lib/agent-firewall/unblock/planner.js +337 -0
  39. package/bin/runners/lib/analysis-core.js +198 -180
  40. package/bin/runners/lib/analyzers.js +1119 -536
  41. package/bin/runners/lib/cli-output.js +236 -210
  42. package/bin/runners/lib/detectors-v2.js +547 -785
  43. package/bin/runners/lib/fingerprint.js +377 -0
  44. package/bin/runners/lib/route-truth.js +1167 -322
  45. package/bin/runners/lib/scan-output.js +144 -738
  46. package/bin/runners/lib/ship-output-enterprise.js +239 -0
  47. package/bin/runners/lib/terminal-ui.js +188 -770
  48. package/bin/runners/lib/truth.js +1004 -321
  49. package/bin/runners/lib/unified-output.js +162 -158
  50. package/bin/runners/runAgent.js +161 -0
  51. package/bin/runners/runFirewall.js +134 -0
  52. package/bin/runners/runFirewallHook.js +56 -0
  53. package/bin/runners/runScan.js +113 -10
  54. package/bin/runners/runShip.js +7 -8
  55. package/bin/runners/runTruth.js +89 -0
  56. package/mcp-server/agent-firewall-interceptor.js +164 -0
  57. package/mcp-server/index.js +347 -313
  58. package/mcp-server/truth-context.js +131 -90
  59. package/mcp-server/truth-firewall-tools.js +1412 -1045
  60. package/package.json +1 -1
@@ -0,0 +1,214 @@
1
+ /**
2
+ * Claim Extractor
3
+ *
4
+ * Extracts "drift-causing claims" from changed files.
5
+ * Uses AST-based extraction with Babel parser.
6
+ */
7
+
8
+ const fs = require("fs");
9
+ const path = require("path");
10
+ const parser = require("@babel/parser");
11
+ const traverse = require("@babel/traverse").default;
12
+ const t = require("@babel/types");
13
+ const { CLAIM_TYPES, CRITICALITY } = require("./claim-types");
14
+ const { ROUTE_LIKE, AUTH_HINTS, SUCCESS_UI } = require("./patterns");
15
+
16
+ function parse(code) {
17
+ return parser.parse(code, {
18
+ sourceType: "unambiguous",
19
+ plugins: ["typescript", "jsx"]
20
+ });
21
+ }
22
+
23
+ function locPtr(fileRel, node) {
24
+ const loc = node && node.loc;
25
+ if (!loc) return null;
26
+ return `${fileRel}:${loc.start.line}-${loc.end.line}`;
27
+ }
28
+
29
+ function pushClaim(out, claim) {
30
+ const key = `${claim.type}|${claim.value}|${claim.pointer || ""}`;
31
+ if (!out._dedupe.has(key)) {
32
+ out._dedupe.add(key);
33
+ out.claims.push(claim);
34
+ }
35
+ }
36
+
37
+ function extractStringLiterals(code) {
38
+ // Cheap scan for route-ish strings even if AST misses template parts.
39
+ const hits = [];
40
+ for (const rx of ROUTE_LIKE) {
41
+ const m = code.match(rx);
42
+ if (m) hits.push(...m);
43
+ }
44
+ return [...new Set(hits)];
45
+ }
46
+
47
+ function classifyFileDomain(fileRel) {
48
+ const s = fileRel.toLowerCase();
49
+ if (s.includes("auth")) return "auth";
50
+ if (s.includes("stripe") || s.includes("payment")) return "payments";
51
+ if (s.includes("routes") || s.includes("router") || s.includes("api")) return "routes";
52
+ if (s.includes("schema") || s.includes("contract") || s.includes("openapi")) return "contracts";
53
+ if (s.includes("ui") || s.includes("components") || s.includes("pages")) return "ui";
54
+ return "general";
55
+ }
56
+
57
+ function extractClaimsFromFile({ repoRoot, fileAbs }) {
58
+ const fileRel = path.relative(repoRoot, fileAbs).replace(/\\/g, "/");
59
+ const code = fs.readFileSync(fileAbs, "utf8");
60
+ const ast = parse(code);
61
+
62
+ const out = { fileRel, domain: classifyFileDomain(fileRel), claims: [], _dedupe: new Set() };
63
+
64
+ // 1) quick string route-ish scan
65
+ for (const r of extractStringLiterals(code)) {
66
+ pushClaim(out, {
67
+ type: CLAIM_TYPES.ROUTE,
68
+ value: r,
69
+ criticality: CRITICALITY.HARD,
70
+ pointer: `${fileRel}:1-1`,
71
+ reason: "route-like string literal"
72
+ });
73
+ }
74
+
75
+ // 2) AST-based env extraction
76
+ traverse(ast, {
77
+ MemberExpression(p) {
78
+ // process.env.X
79
+ const n = p.node;
80
+ if (
81
+ t.isMemberExpression(n.object) &&
82
+ t.isIdentifier(n.object.object, { name: "process" }) &&
83
+ t.isIdentifier(n.object.property, { name: "env" }) &&
84
+ (t.isIdentifier(n.property) || t.isStringLiteral(n.property))
85
+ ) {
86
+ const name = t.isIdentifier(n.property) ? n.property.name : n.property.value;
87
+ pushClaim(out, {
88
+ type: CLAIM_TYPES.ENV,
89
+ value: name,
90
+ criticality: CRITICALITY.HARD,
91
+ pointer: locPtr(fileRel, n),
92
+ reason: "process.env usage"
93
+ });
94
+ }
95
+
96
+ // import.meta.env.X (Vite)
97
+ if (
98
+ t.isMemberExpression(n.object) &&
99
+ t.isMemberExpression(n.object.object) &&
100
+ t.isIdentifier(n.object.object.object, { name: "import" }) &&
101
+ t.isIdentifier(n.object.object.property, { name: "meta" }) &&
102
+ t.isIdentifier(n.object.property, { name: "env" }) &&
103
+ (t.isIdentifier(n.property) || t.isStringLiteral(n.property))
104
+ ) {
105
+ const name = t.isIdentifier(n.property) ? n.property.name : n.property.value;
106
+ pushClaim(out, {
107
+ type: CLAIM_TYPES.ENV,
108
+ value: name,
109
+ criticality: CRITICALITY.HARD,
110
+ pointer: locPtr(fileRel, n),
111
+ reason: "import.meta.env usage"
112
+ });
113
+ }
114
+ },
115
+
116
+ CallExpression(p) {
117
+ const n = p.node;
118
+
119
+ // fetch("/api/...")
120
+ if (t.isIdentifier(n.callee, { name: "fetch" }) && n.arguments[0]) {
121
+ const a0 = n.arguments[0];
122
+ if (t.isStringLiteral(a0)) {
123
+ pushClaim(out, {
124
+ type: CLAIM_TYPES.HTTP_CALL,
125
+ value: a0.value,
126
+ criticality: CRITICALITY.HARD,
127
+ pointer: locPtr(fileRel, n),
128
+ reason: "fetch call"
129
+ });
130
+ }
131
+ }
132
+
133
+ // axios.get("/api/...")
134
+ if (t.isMemberExpression(n.callee) && t.isIdentifier(n.callee.object, { name: "axios" })) {
135
+ const method = t.isIdentifier(n.callee.property) ? n.callee.property.name : "call";
136
+ const a0 = n.arguments[0];
137
+ if (a0 && t.isStringLiteral(a0)) {
138
+ pushClaim(out, {
139
+ type: CLAIM_TYPES.HTTP_CALL,
140
+ value: `${method.toUpperCase()} ${a0.value}`,
141
+ criticality: CRITICALITY.HARD,
142
+ pointer: locPtr(fileRel, n),
143
+ reason: "axios call"
144
+ });
145
+ }
146
+ }
147
+
148
+ // toast.success("Saved")
149
+ if (
150
+ t.isMemberExpression(n.callee) &&
151
+ t.isIdentifier(n.callee.object, { name: "toast" }) &&
152
+ t.isIdentifier(n.callee.property, { name: "success" })
153
+ ) {
154
+ const msg = n.arguments[0] && t.isStringLiteral(n.arguments[0]) ? n.arguments[0].value : "toast.success";
155
+ pushClaim(out, {
156
+ type: CLAIM_TYPES.UI_SUCCESS,
157
+ value: msg,
158
+ criticality: CRITICALITY.SOFT,
159
+ pointer: locPtr(fileRel, n),
160
+ reason: "success UI signal"
161
+ });
162
+ }
163
+ }
164
+ });
165
+
166
+ // 3) auth-ish keyword scan (cheap but effective)
167
+ const lines = code.split(/\r?\n/);
168
+ for (let i = 0; i < lines.length; i++) {
169
+ const line = lines[i];
170
+ if (AUTH_HINTS.some((rx) => rx.test(line))) {
171
+ pushClaim(out, {
172
+ type: CLAIM_TYPES.AUTH,
173
+ value: line.trim().slice(0, 140),
174
+ criticality: CRITICALITY.HARD,
175
+ pointer: `${fileRel}:${i + 1}-${i + 1}`,
176
+ reason: "auth/role/scope hint"
177
+ });
178
+ }
179
+ if (SUCCESS_UI.some((rx) => rx.test(line))) {
180
+ pushClaim(out, {
181
+ type: CLAIM_TYPES.UI_SUCCESS,
182
+ value: line.trim().slice(0, 140),
183
+ criticality: CRITICALITY.SOFT,
184
+ pointer: `${fileRel}:${i + 1}-${i + 1}`,
185
+ reason: "success UI hint"
186
+ });
187
+ }
188
+ }
189
+
190
+ delete out._dedupe;
191
+ return out;
192
+ }
193
+
194
+ function extractClaims({ repoRoot, changedFilesAbs }) {
195
+ const files = changedFilesAbs.filter((f) => fs.existsSync(f));
196
+ const perFile = files.map((fileAbs) => extractClaimsFromFile({ repoRoot, fileAbs }));
197
+
198
+ // Flatten + dedupe across files
199
+ const dedupe = new Set();
200
+ const claims = [];
201
+ for (const f of perFile) {
202
+ for (const c of f.claims) {
203
+ const k = `${c.type}|${c.value}`;
204
+ if (!dedupe.has(k)) {
205
+ dedupe.add(k);
206
+ claims.push({ ...c, file: f.fileRel, domain: f.domain });
207
+ }
208
+ }
209
+ }
210
+
211
+ return { claims, perFile };
212
+ }
213
+
214
+ module.exports = { extractClaims, extractClaimsFromFile };
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Claim Patterns
3
+ *
4
+ * Regex patterns for detecting claims in code.
5
+ */
6
+
7
+ const ROUTE_LIKE = [
8
+ /\/api\/[a-zA-Z0-9_\/\-]+/g,
9
+ /\/health\/[a-zA-Z0-9_\/\-]+/g
10
+ ];
11
+
12
+ const AUTH_HINTS = [
13
+ /\b(admin|owner|staff|superadmin|root)\b/i,
14
+ /\b(role|roles|scope|scopes|permission|permissions)\b/i,
15
+ /\b(auth|authorize|authorization|requireAuth|requireRole|rbac)\b/i
16
+ ];
17
+
18
+ const SUCCESS_UI = [
19
+ /\b(success|saved|updated|complete|done)\b/i,
20
+ /\btoast\.(success|info)\b/i,
21
+ /\benqueueSnackbar\b/i
22
+ ];
23
+
24
+ module.exports = { ROUTE_LIKE, AUTH_HINTS, SUCCESS_UI };
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Auth Evidence Resolver
3
+ *
4
+ * Resolves auth claims against truthpack.auth.json
5
+ * Checks for auth drift (claimed restriction not enforced).
6
+ */
7
+
8
+ "use strict";
9
+
10
+ const { getAuthRules } = require("../truthpack");
11
+
12
+ /**
13
+ * Resolve auth claim evidence
14
+ * @param {string} projectRoot - Project root directory
15
+ * @param {object} claim - Auth claim
16
+ * @returns {object} Evidence result
17
+ */
18
+ function resolve(projectRoot, claim) {
19
+ const authData = getAuthRules(projectRoot);
20
+
21
+ // Extract auth keywords from claim value
22
+ const claimText = claim.value.toLowerCase();
23
+ const hasAuthKeywords = /\b(admin|owner|staff|role|scope|permission|auth|authorize|rbac)\b/i.test(claimText);
24
+
25
+ if (!hasAuthKeywords) {
26
+ // Not an auth-related claim
27
+ return {
28
+ result: "PROVEN",
29
+ sources: [],
30
+ reason: "No auth keywords detected in claim"
31
+ };
32
+ }
33
+
34
+ // Check if auth middleware exists
35
+ const nextMiddleware = authData.nextMiddleware || [];
36
+ const fastifyHooks = authData.fastify?.hooks || [];
37
+
38
+ if (nextMiddleware.length > 0 || fastifyHooks.length > 0) {
39
+ // Auth infrastructure exists
40
+ // Check if claim matches protected patterns
41
+ const matcherPatterns = authData.nextMatcherPatterns || [];
42
+ const claimFile = claim.file || "";
43
+
44
+ // Check if file is in protected path
45
+ const isProtected = matcherPatterns.some(pattern => {
46
+ // Simple pattern matching
47
+ if (pattern.includes("*")) {
48
+ const regex = new RegExp(pattern.replace(/\*/g, ".*"));
49
+ return regex.test(claimFile);
50
+ }
51
+ return claimFile.includes(pattern);
52
+ });
53
+
54
+ if (isProtected) {
55
+ return {
56
+ result: "PROVEN",
57
+ sources: [{
58
+ type: "truthpack.auth",
59
+ pointer: claim.pointer,
60
+ confidence: 0.8
61
+ }],
62
+ reason: "Auth claim matches protected route pattern"
63
+ };
64
+ } else {
65
+ // Auth keywords present but route not protected - potential drift
66
+ return {
67
+ result: "CONTRADICTS",
68
+ sources: [{
69
+ type: "truthpack.auth",
70
+ pointer: claim.pointer,
71
+ confidence: 0.7
72
+ }],
73
+ reason: "Auth keywords present but route not in protected patterns (auth drift)"
74
+ };
75
+ }
76
+ } else {
77
+ // No auth infrastructure - cannot verify
78
+ return {
79
+ result: "UNPROVEN",
80
+ sources: [],
81
+ reason: "No auth middleware found in truthpack"
82
+ };
83
+ }
84
+ }
85
+
86
+ module.exports = {
87
+ resolve
88
+ };
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Contract Evidence Resolver
3
+ *
4
+ * Resolves contract claims against truthpack.contracts.json
5
+ * Checks for contract drift (API shape mismatch).
6
+ */
7
+
8
+ "use strict";
9
+
10
+ const { getContracts } = require("../truthpack");
11
+
12
+ /**
13
+ * Resolve contract claim evidence
14
+ * @param {string} projectRoot - Project root directory
15
+ * @param {object} claim - Contract claim
16
+ * @returns {object} Evidence result
17
+ */
18
+ function resolve(projectRoot, claim) {
19
+ const contracts = getContracts(projectRoot);
20
+
21
+ // Extract contract identifier from claim
22
+ // Contract claims might reference API endpoints, types, or schemas
23
+ const claimValue = claim.value.toLowerCase();
24
+
25
+ // Check if contracts exist
26
+ if (!contracts || Object.keys(contracts).length === 0) {
27
+ return {
28
+ result: "UNPROVEN",
29
+ sources: [],
30
+ reason: "No contracts found in truthpack"
31
+ };
32
+ }
33
+
34
+ // Try to match claim against contract definitions
35
+ // This is a simplified check - full implementation would parse contract schemas
36
+ const contractKeys = Object.keys(contracts);
37
+ const matchingContract = contractKeys.find(key =>
38
+ key.toLowerCase().includes(claimValue) ||
39
+ claimValue.includes(key.toLowerCase())
40
+ );
41
+
42
+ if (matchingContract) {
43
+ return {
44
+ result: "PROVEN",
45
+ sources: [{
46
+ type: "truthpack.contracts",
47
+ pointer: claim.pointer,
48
+ confidence: 0.8
49
+ }],
50
+ reason: `Contract ${matchingContract} found in truthpack`
51
+ };
52
+ }
53
+
54
+ // Check for contract drift by examining the claim context
55
+ // If claim references an API endpoint, check if contract exists for that endpoint
56
+ if (claimValue.includes("api") || claimValue.includes("endpoint")) {
57
+ // Potential contract drift - endpoint referenced but contract not found
58
+ return {
59
+ result: "CONTRADICTS",
60
+ sources: [],
61
+ reason: "API endpoint referenced but contract not found in truthpack (contract drift)"
62
+ };
63
+ }
64
+
65
+ // Cannot verify contract
66
+ return {
67
+ result: "UNPROVEN",
68
+ sources: [],
69
+ reason: "Contract not found in truthpack"
70
+ };
71
+ }
72
+
73
+ module.exports = {
74
+ resolve
75
+ };
@@ -0,0 +1,118 @@
1
+ /**
2
+ * Environment Variable Evidence Resolver
3
+ *
4
+ * Resolves env var claims against truthpack.env.json
5
+ * Checks for ghost env vars (used but not declared).
6
+ */
7
+
8
+ "use strict";
9
+
10
+ const fs = require("fs");
11
+ const path = require("path");
12
+ const { getEnvVars } = require("../truthpack");
13
+
14
+ /**
15
+ * Resolve env var claim evidence
16
+ * @param {string} projectRoot - Project root directory
17
+ * @param {object} claim - Env var claim
18
+ * @returns {object} Evidence result
19
+ */
20
+ function resolve(projectRoot, claim) {
21
+ const envData = getEnvVars(projectRoot);
22
+
23
+ // Check declared env vars
24
+ const declared = envData.declared || [];
25
+ const declaredSet = new Set(declared.map(v => v.name || v));
26
+
27
+ // Check declared sources (env.schema.ts, .env.example, etc.)
28
+ const declaredSources = envData.declaredSources || [];
29
+
30
+ const envVarName = claim.value;
31
+
32
+ // Check if env var is declared
33
+ if (declaredSet.has(envVarName)) {
34
+ // Find source file
35
+ const source = declaredSources.find(s =>
36
+ s.vars && s.vars.includes(envVarName)
37
+ );
38
+
39
+ return {
40
+ result: "PROVEN",
41
+ sources: [{
42
+ type: "truthpack.env",
43
+ pointer: source ? source.file : claim.pointer,
44
+ confidence: 0.9
45
+ }],
46
+ reason: `Environment variable ${envVarName} found in truthpack`
47
+ };
48
+ }
49
+
50
+ // Check if env var exists in .env.example or schema files
51
+ const envExamplePath = path.join(projectRoot, ".env.example");
52
+ const envSchemaPath = findEnvSchemaFile(projectRoot);
53
+
54
+ if (fs.existsSync(envExamplePath)) {
55
+ const envExample = fs.readFileSync(envExamplePath, "utf8");
56
+ if (envExample.includes(envVarName)) {
57
+ return {
58
+ result: "PROVEN",
59
+ sources: [{
60
+ type: "repo.search",
61
+ pointer: ".env.example",
62
+ confidence: 0.7
63
+ }],
64
+ reason: `Environment variable ${envVarName} found in .env.example`
65
+ };
66
+ }
67
+ }
68
+
69
+ if (envSchemaPath && fs.existsSync(envSchemaPath)) {
70
+ const envSchema = fs.readFileSync(envSchemaPath, "utf8");
71
+ if (envSchema.includes(envVarName)) {
72
+ return {
73
+ result: "PROVEN",
74
+ sources: [{
75
+ type: "repo.search",
76
+ pointer: envSchemaPath,
77
+ confidence: 0.8
78
+ }],
79
+ reason: `Environment variable ${envVarName} found in env schema`
80
+ };
81
+ }
82
+ }
83
+
84
+ // Not found - ghost env var
85
+ return {
86
+ result: "UNPROVEN",
87
+ sources: [],
88
+ reason: `Environment variable ${envVarName} not declared (ghost env var)`
89
+ };
90
+ }
91
+
92
+ /**
93
+ * Find env schema file (env.schema.ts, env.ts, etc.)
94
+ * @param {string} projectRoot - Project root directory
95
+ * @returns {string|null} Path to schema file or null
96
+ */
97
+ function findEnvSchemaFile(projectRoot) {
98
+ const candidates = [
99
+ "apps/api/src/config/env.schema.ts",
100
+ "apps/api/src/env.schema.ts",
101
+ "src/config/env.schema.ts",
102
+ "src/env.schema.ts",
103
+ "env.schema.ts"
104
+ ];
105
+
106
+ for (const candidate of candidates) {
107
+ const fullPath = path.join(projectRoot, candidate);
108
+ if (fs.existsSync(fullPath)) {
109
+ return candidate;
110
+ }
111
+ }
112
+
113
+ return null;
114
+ }
115
+
116
+ module.exports = {
117
+ resolve
118
+ };
@@ -0,0 +1,102 @@
1
+ /**
2
+ * Evidence Resolver
3
+ *
4
+ * Main orchestrator for resolving claims against truthpack.
5
+ * Returns PROVEN, UNPROVEN, or CONTRADICTS for each claim.
6
+ */
7
+
8
+ "use strict";
9
+
10
+ const routeEvidence = require("./route-evidence");
11
+ const envEvidence = require("./env-evidence");
12
+ const authEvidence = require("./auth-evidence");
13
+ const contractEvidence = require("./contract-evidence");
14
+ const sideEffectEvidence = require("./side-effect-evidence");
15
+ const { CLAIM_TYPES } = require("../claims/claim-types");
16
+
17
+ /**
18
+ * Resolve evidence for all claims
19
+ * @param {string} projectRoot - Project root directory
20
+ * @param {array} claims - Array of claims to resolve
21
+ * @returns {array} Array of evidence results
22
+ */
23
+ function resolveEvidence(projectRoot, claims) {
24
+ const results = [];
25
+
26
+ for (let i = 0; i < claims.length; i++) {
27
+ const claim = claims[i];
28
+ const claimId = `claim_${i}`;
29
+
30
+ let result;
31
+
32
+ switch (claim.type) {
33
+ case CLAIM_TYPES.ROUTE:
34
+ result = routeEvidence.resolve(projectRoot, claim);
35
+ break;
36
+
37
+ case CLAIM_TYPES.ENV:
38
+ result = envEvidence.resolve(projectRoot, claim);
39
+ break;
40
+
41
+ case CLAIM_TYPES.AUTH:
42
+ result = authEvidence.resolve(projectRoot, claim);
43
+ break;
44
+
45
+ case CLAIM_TYPES.CONTRACT:
46
+ result = contractEvidence.resolve(projectRoot, claim);
47
+ break;
48
+
49
+ case CLAIM_TYPES.SIDE_EFFECT:
50
+ result = sideEffectEvidence.resolve(projectRoot, claim);
51
+ break;
52
+
53
+ case CLAIM_TYPES.HTTP_CALL:
54
+ // HTTP calls are checked as routes
55
+ result = routeEvidence.resolve(projectRoot, {
56
+ ...claim,
57
+ type: CLAIM_TYPES.ROUTE,
58
+ value: extractRouteFromHttpCall(claim.value)
59
+ });
60
+ break;
61
+
62
+ case CLAIM_TYPES.UI_SUCCESS:
63
+ // UI success claims are checked for side effects
64
+ result = sideEffectEvidence.resolve(projectRoot, claim);
65
+ break;
66
+
67
+ default:
68
+ result = {
69
+ claimId,
70
+ result: "UNPROVEN",
71
+ sources: [],
72
+ reason: `Unknown claim type: ${claim.type}`
73
+ };
74
+ }
75
+
76
+ results.push({
77
+ claimId,
78
+ ...result
79
+ });
80
+ }
81
+
82
+ return results;
83
+ }
84
+
85
+ /**
86
+ * Extract route path from HTTP call claim value
87
+ * @param {string} httpCall - HTTP call string (e.g., "GET /api/users")
88
+ * @returns {string} Route path
89
+ */
90
+ function extractRouteFromHttpCall(httpCall) {
91
+ // Handle "GET /api/users" format
92
+ const match = httpCall.match(/\s+(.+)$/);
93
+ if (match) {
94
+ return match[1];
95
+ }
96
+ // Handle "/api/users" format
97
+ return httpCall.startsWith("/") ? httpCall : `/${httpCall}`;
98
+ }
99
+
100
+ module.exports = {
101
+ resolveEvidence
102
+ };