@vibecheckai/cli 3.2.2 → 3.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/.generated +25 -25
- package/bin/dev/run-v2-torture.js +30 -30
- package/bin/runners/ENHANCEMENT_GUIDE.md +121 -121
- package/bin/runners/lib/__tests__/entitlements-v2.test.js +295 -295
- package/bin/runners/lib/agent-firewall/ai/false-positive-analyzer.js +474 -0
- package/bin/runners/lib/agent-firewall/claims/extractor.js +117 -28
- package/bin/runners/lib/agent-firewall/evidence/env-evidence.js +23 -14
- package/bin/runners/lib/agent-firewall/evidence/route-evidence.js +72 -1
- package/bin/runners/lib/agent-firewall/interceptor/base.js +2 -2
- package/bin/runners/lib/agent-firewall/policy/default-policy.json +6 -0
- package/bin/runners/lib/agent-firewall/policy/engine.js +34 -3
- package/bin/runners/lib/agent-firewall/policy/rules/fake-success.js +29 -4
- package/bin/runners/lib/agent-firewall/policy/rules/ghost-route.js +12 -0
- package/bin/runners/lib/agent-firewall/truthpack/loader.js +21 -0
- package/bin/runners/lib/agent-firewall/utils/ignore-checker.js +118 -0
- package/bin/runners/lib/analyzers.js +606 -325
- package/bin/runners/lib/auth-truth.js +193 -193
- package/bin/runners/lib/backup.js +62 -62
- package/bin/runners/lib/billing.js +107 -107
- package/bin/runners/lib/claims.js +118 -118
- package/bin/runners/lib/cli-ui.js +540 -540
- package/bin/runners/lib/contracts/auth-contract.js +202 -202
- package/bin/runners/lib/contracts/env-contract.js +181 -181
- package/bin/runners/lib/contracts/external-contract.js +206 -206
- package/bin/runners/lib/contracts/guard.js +168 -168
- package/bin/runners/lib/contracts/index.js +89 -89
- package/bin/runners/lib/contracts/plan-validator.js +311 -311
- package/bin/runners/lib/contracts/route-contract.js +199 -199
- package/bin/runners/lib/contracts.js +804 -804
- package/bin/runners/lib/detect.js +89 -89
- package/bin/runners/lib/doctor/autofix.js +254 -254
- package/bin/runners/lib/doctor/index.js +37 -37
- package/bin/runners/lib/doctor/modules/dependencies.js +325 -325
- package/bin/runners/lib/doctor/modules/index.js +46 -46
- package/bin/runners/lib/doctor/modules/network.js +250 -250
- package/bin/runners/lib/doctor/modules/project.js +312 -312
- package/bin/runners/lib/doctor/modules/runtime.js +224 -224
- package/bin/runners/lib/doctor/modules/security.js +348 -348
- package/bin/runners/lib/doctor/modules/system.js +213 -213
- package/bin/runners/lib/doctor/modules/vibecheck.js +394 -394
- package/bin/runners/lib/doctor/reporter.js +262 -262
- package/bin/runners/lib/doctor/service.js +262 -262
- package/bin/runners/lib/doctor/types.js +113 -113
- package/bin/runners/lib/doctor/ui.js +263 -263
- package/bin/runners/lib/doctor-v2.js +608 -608
- package/bin/runners/lib/drift.js +425 -425
- package/bin/runners/lib/enforcement.js +72 -72
- package/bin/runners/lib/engines/accessibility-engine.js +190 -0
- package/bin/runners/lib/engines/api-consistency-engine.js +162 -0
- package/bin/runners/lib/engines/ast-cache.js +99 -0
- package/bin/runners/lib/engines/code-quality-engine.js +255 -0
- package/bin/runners/lib/engines/console-logs-engine.js +115 -0
- package/bin/runners/lib/engines/cross-file-analysis-engine.js +268 -0
- package/bin/runners/lib/engines/dead-code-engine.js +198 -0
- package/bin/runners/lib/engines/deprecated-api-engine.js +226 -0
- package/bin/runners/lib/engines/empty-catch-engine.js +150 -0
- package/bin/runners/lib/engines/file-filter.js +131 -0
- package/bin/runners/lib/engines/hardcoded-secrets-engine.js +251 -0
- package/bin/runners/lib/engines/mock-data-engine.js +272 -0
- package/bin/runners/lib/engines/parallel-processor.js +71 -0
- package/bin/runners/lib/engines/performance-issues-engine.js +265 -0
- package/bin/runners/lib/engines/security-vulnerabilities-engine.js +243 -0
- package/bin/runners/lib/engines/todo-fixme-engine.js +115 -0
- package/bin/runners/lib/engines/type-aware-engine.js +152 -0
- package/bin/runners/lib/engines/unsafe-regex-engine.js +225 -0
- package/bin/runners/lib/engines/vibecheck-engines/README.md +53 -0
- package/bin/runners/lib/engines/vibecheck-engines/index.js +15 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/ast-cache.js +164 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/code-quality-engine.js +291 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/console-logs-engine.js +83 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/dead-code-engine.js +198 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/deprecated-api-engine.js +275 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/empty-catch-engine.js +167 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/file-filter.js +217 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/hardcoded-secrets-engine.js +139 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/mock-data-engine.js +140 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/parallel-processor.js +164 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/performance-issues-engine.js +234 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/type-aware-engine.js +217 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/unsafe-regex-engine.js +78 -0
- package/bin/runners/lib/engines/vibecheck-engines/package.json +13 -0
- package/bin/runners/lib/enterprise-detect.js +603 -603
- package/bin/runners/lib/enterprise-init.js +942 -942
- package/bin/runners/lib/env-resolver.js +417 -417
- package/bin/runners/lib/env-template.js +66 -66
- package/bin/runners/lib/env.js +189 -189
- package/bin/runners/lib/extractors/client-calls.js +990 -990
- package/bin/runners/lib/extractors/fastify-route-dump.js +573 -573
- package/bin/runners/lib/extractors/fastify-routes.js +426 -426
- package/bin/runners/lib/extractors/index.js +363 -363
- package/bin/runners/lib/extractors/next-routes.js +524 -524
- package/bin/runners/lib/extractors/proof-graph.js +431 -431
- package/bin/runners/lib/extractors/route-matcher.js +451 -451
- package/bin/runners/lib/extractors/truthpack-v2.js +377 -377
- package/bin/runners/lib/extractors/ui-bindings.js +547 -547
- package/bin/runners/lib/findings-schema.js +281 -281
- package/bin/runners/lib/firewall-prompt.js +50 -50
- package/bin/runners/lib/global-flags.js +213 -213
- package/bin/runners/lib/graph/graph-builder.js +265 -265
- package/bin/runners/lib/graph/html-renderer.js +413 -413
- package/bin/runners/lib/graph/index.js +32 -32
- package/bin/runners/lib/graph/runtime-collector.js +215 -215
- package/bin/runners/lib/graph/static-extractor.js +518 -518
- package/bin/runners/lib/html-report.js +650 -650
- package/bin/runners/lib/interactive-menu.js +1496 -1496
- package/bin/runners/lib/llm.js +75 -75
- package/bin/runners/lib/meter.js +61 -61
- package/bin/runners/lib/missions/evidence.js +126 -126
- package/bin/runners/lib/patch.js +40 -40
- package/bin/runners/lib/permissions/auth-model.js +213 -213
- package/bin/runners/lib/permissions/idor-prover.js +205 -205
- package/bin/runners/lib/permissions/index.js +45 -45
- package/bin/runners/lib/permissions/matrix-builder.js +198 -198
- package/bin/runners/lib/pkgjson.js +28 -28
- package/bin/runners/lib/policy.js +295 -295
- package/bin/runners/lib/preflight.js +142 -142
- package/bin/runners/lib/reality/correlation-detectors.js +359 -359
- package/bin/runners/lib/reality/index.js +318 -318
- package/bin/runners/lib/reality/request-hashing.js +416 -416
- package/bin/runners/lib/reality/request-mapper.js +453 -453
- package/bin/runners/lib/reality/safety-rails.js +463 -463
- package/bin/runners/lib/reality/semantic-snapshot.js +408 -408
- package/bin/runners/lib/reality/toast-detector.js +393 -393
- package/bin/runners/lib/reality-findings.js +84 -84
- package/bin/runners/lib/receipts.js +179 -179
- package/bin/runners/lib/redact.js +29 -29
- package/bin/runners/lib/replay/capsule-manager.js +154 -154
- package/bin/runners/lib/replay/index.js +263 -263
- package/bin/runners/lib/replay/player.js +348 -348
- package/bin/runners/lib/replay/recorder.js +331 -331
- package/bin/runners/lib/report-output.js +187 -187
- package/bin/runners/lib/report.js +135 -135
- package/bin/runners/lib/route-detection.js +1140 -1140
- package/bin/runners/lib/sandbox/index.js +59 -59
- package/bin/runners/lib/sandbox/proof-chain.js +399 -399
- package/bin/runners/lib/sandbox/sandbox-runner.js +205 -205
- package/bin/runners/lib/sandbox/worktree.js +174 -174
- package/bin/runners/lib/scan-output.js +525 -190
- package/bin/runners/lib/schema-validator.js +350 -350
- package/bin/runners/lib/schemas/contracts.schema.json +160 -160
- package/bin/runners/lib/schemas/finding.schema.json +100 -100
- package/bin/runners/lib/schemas/mission-pack.schema.json +206 -206
- package/bin/runners/lib/schemas/proof-graph.schema.json +176 -176
- package/bin/runners/lib/schemas/reality-report.schema.json +162 -162
- package/bin/runners/lib/schemas/share-pack.schema.json +180 -180
- package/bin/runners/lib/schemas/ship-report.schema.json +117 -117
- package/bin/runners/lib/schemas/truthpack-v2.schema.json +303 -303
- package/bin/runners/lib/schemas/validator.js +438 -438
- package/bin/runners/lib/score-history.js +282 -282
- package/bin/runners/lib/share-pack.js +239 -239
- package/bin/runners/lib/snippets.js +67 -67
- package/bin/runners/lib/status-output.js +253 -253
- package/bin/runners/lib/terminal-ui.js +351 -271
- package/bin/runners/lib/upsell.js +510 -510
- package/bin/runners/lib/usage.js +153 -153
- package/bin/runners/lib/validate-patch.js +156 -156
- package/bin/runners/lib/verdict-engine.js +628 -628
- package/bin/runners/reality/engine.js +917 -917
- package/bin/runners/reality/flows.js +122 -122
- package/bin/runners/reality/report.js +378 -378
- package/bin/runners/reality/session.js +193 -193
- package/bin/runners/runGuard.js +168 -168
- package/bin/runners/runProof.zip +0 -0
- package/bin/runners/runProve.js +8 -0
- package/bin/runners/runReality.js +14 -0
- package/bin/runners/runScan.js +17 -1
- package/bin/runners/runTruth.js +15 -3
- package/mcp-server/tier-auth.js +4 -4
- package/mcp-server/tools/index.js +72 -72
- package/package.json +1 -1
|
@@ -1,206 +1,206 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* External Contract Builder
|
|
3
|
-
* Builds external.json contract from truthpack (Stripe, GitHub, etc.)
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
"use strict";
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Build external services contract from truthpack
|
|
10
|
-
*/
|
|
11
|
-
function buildExternalContract(truthpack) {
|
|
12
|
-
const contract = {
|
|
13
|
-
version: "1.0.0",
|
|
14
|
-
generatedAt: new Date().toISOString(),
|
|
15
|
-
services: []
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
// Extract billing/Stripe info
|
|
19
|
-
const billing = truthpack?.billing || {};
|
|
20
|
-
if (billing.hasStripe || billing.webhookCandidates?.length) {
|
|
21
|
-
const stripeService = {
|
|
22
|
-
name: "stripe",
|
|
23
|
-
envVars: extractStripeEnvVars(truthpack),
|
|
24
|
-
usedIn: billing.webhookCandidates?.map(w => w.file) || [],
|
|
25
|
-
webhooks: billing.webhookCandidates?.map(w => ({
|
|
26
|
-
path: w.path,
|
|
27
|
-
verified: w.hasSignatureVerification || false,
|
|
28
|
-
idempotent: w.hasIdempotency || false
|
|
29
|
-
})) || [],
|
|
30
|
-
evidence: billing.webhookCandidates?.flatMap(w => w.evidence || []) || []
|
|
31
|
-
};
|
|
32
|
-
contract.services.push(stripeService);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
// Detect other external services from env vars
|
|
36
|
-
const envVars = truthpack?.env?.vars || [];
|
|
37
|
-
|
|
38
|
-
// GitHub
|
|
39
|
-
const githubVars = envVars.filter(v => /github/i.test(v.name));
|
|
40
|
-
if (githubVars.length) {
|
|
41
|
-
contract.services.push({
|
|
42
|
-
name: "github",
|
|
43
|
-
envVars: githubVars.map(v => v.name),
|
|
44
|
-
usedIn: githubVars.flatMap(v => v.references?.map(r => r.file) || []),
|
|
45
|
-
evidence: githubVars.flatMap(v => v.references || [])
|
|
46
|
-
});
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// SendGrid
|
|
50
|
-
const sendgridVars = envVars.filter(v => /sendgrid/i.test(v.name));
|
|
51
|
-
if (sendgridVars.length) {
|
|
52
|
-
contract.services.push({
|
|
53
|
-
name: "sendgrid",
|
|
54
|
-
envVars: sendgridVars.map(v => v.name),
|
|
55
|
-
usedIn: sendgridVars.flatMap(v => v.references?.map(r => r.file) || []),
|
|
56
|
-
evidence: sendgridVars.flatMap(v => v.references || [])
|
|
57
|
-
});
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// Twilio
|
|
61
|
-
const twilioVars = envVars.filter(v => /twilio/i.test(v.name));
|
|
62
|
-
if (twilioVars.length) {
|
|
63
|
-
contract.services.push({
|
|
64
|
-
name: "twilio",
|
|
65
|
-
envVars: twilioVars.map(v => v.name),
|
|
66
|
-
usedIn: twilioVars.flatMap(v => v.references?.map(r => r.file) || []),
|
|
67
|
-
evidence: twilioVars.flatMap(v => v.references || [])
|
|
68
|
-
});
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// AWS
|
|
72
|
-
const awsVars = envVars.filter(v => /^aws/i.test(v.name));
|
|
73
|
-
if (awsVars.length) {
|
|
74
|
-
contract.services.push({
|
|
75
|
-
name: "aws",
|
|
76
|
-
envVars: awsVars.map(v => v.name),
|
|
77
|
-
usedIn: awsVars.flatMap(v => v.references?.map(r => r.file) || []),
|
|
78
|
-
evidence: awsVars.flatMap(v => v.references || [])
|
|
79
|
-
});
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// Supabase
|
|
83
|
-
const supabaseVars = envVars.filter(v => /supabase/i.test(v.name));
|
|
84
|
-
if (supabaseVars.length) {
|
|
85
|
-
contract.services.push({
|
|
86
|
-
name: "supabase",
|
|
87
|
-
envVars: supabaseVars.map(v => v.name).sort(),
|
|
88
|
-
usedIn: [...new Set(supabaseVars.flatMap(v => v.references?.map(r => r.file) || []))].sort(),
|
|
89
|
-
evidence: supabaseVars.flatMap(v => v.references || [])
|
|
90
|
-
});
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
// Deterministic output: sort services by name, and their internal arrays
|
|
94
|
-
contract.services.sort((a, b) => a.name.localeCompare(b.name));
|
|
95
|
-
for (const svc of contract.services) {
|
|
96
|
-
if (svc.envVars) svc.envVars.sort();
|
|
97
|
-
if (svc.usedIn) svc.usedIn = [...new Set(svc.usedIn)].sort();
|
|
98
|
-
if (svc.webhooks) svc.webhooks.sort((a, b) => a.path.localeCompare(b.path));
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
return contract;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
function extractStripeEnvVars(truthpack) {
|
|
105
|
-
const envVars = truthpack?.env?.vars || [];
|
|
106
|
-
return envVars
|
|
107
|
-
.filter(v => /stripe/i.test(v.name))
|
|
108
|
-
.map(v => v.name);
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
/**
|
|
112
|
-
* Validate external services contract
|
|
113
|
-
*/
|
|
114
|
-
function validateExternalContract(contract) {
|
|
115
|
-
const violations = [];
|
|
116
|
-
|
|
117
|
-
for (const service of contract.services) {
|
|
118
|
-
// Check Stripe webhook verification
|
|
119
|
-
if (service.name === "stripe") {
|
|
120
|
-
for (const webhook of service.webhooks || []) {
|
|
121
|
-
if (!webhook.verified) {
|
|
122
|
-
violations.push({
|
|
123
|
-
type: "unverified_webhook",
|
|
124
|
-
severity: "BLOCK",
|
|
125
|
-
service: "stripe",
|
|
126
|
-
path: webhook.path,
|
|
127
|
-
message: `Stripe webhook at ${webhook.path} missing signature verification`,
|
|
128
|
-
evidence: []
|
|
129
|
-
});
|
|
130
|
-
}
|
|
131
|
-
if (!webhook.idempotent) {
|
|
132
|
-
violations.push({
|
|
133
|
-
type: "non_idempotent_webhook",
|
|
134
|
-
severity: "WARN",
|
|
135
|
-
service: "stripe",
|
|
136
|
-
path: webhook.path,
|
|
137
|
-
message: `Stripe webhook at ${webhook.path} may not be idempotent`,
|
|
138
|
-
evidence: []
|
|
139
|
-
});
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
// Check for missing required env vars
|
|
145
|
-
const requiredVars = getRequiredVarsForService(service.name);
|
|
146
|
-
for (const required of requiredVars) {
|
|
147
|
-
if (!service.envVars.includes(required)) {
|
|
148
|
-
violations.push({
|
|
149
|
-
type: "missing_env",
|
|
150
|
-
severity: "WARN",
|
|
151
|
-
service: service.name,
|
|
152
|
-
envVar: required,
|
|
153
|
-
message: `Service ${service.name} typically requires ${required}`,
|
|
154
|
-
evidence: []
|
|
155
|
-
});
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
return violations;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
function getRequiredVarsForService(name) {
|
|
164
|
-
const requirements = {
|
|
165
|
-
stripe: ["STRIPE_SECRET_KEY", "STRIPE_WEBHOOK_SECRET"],
|
|
166
|
-
github: ["GITHUB_TOKEN"],
|
|
167
|
-
sendgrid: ["SENDGRID_API_KEY"],
|
|
168
|
-
twilio: ["TWILIO_ACCOUNT_SID", "TWILIO_AUTH_TOKEN"],
|
|
169
|
-
supabase: ["SUPABASE_URL", "SUPABASE_ANON_KEY"]
|
|
170
|
-
};
|
|
171
|
-
return requirements[name] || [];
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
/**
|
|
175
|
-
* Diff two external contracts
|
|
176
|
-
*/
|
|
177
|
-
function diffExternalContracts(before, after) {
|
|
178
|
-
const diff = {
|
|
179
|
-
added: [],
|
|
180
|
-
removed: [],
|
|
181
|
-
changed: []
|
|
182
|
-
};
|
|
183
|
-
|
|
184
|
-
const beforeMap = new Map(before.services.map(s => [s.name, s]));
|
|
185
|
-
const afterMap = new Map(after.services.map(s => [s.name, s]));
|
|
186
|
-
|
|
187
|
-
for (const [name, service] of afterMap) {
|
|
188
|
-
if (!beforeMap.has(name)) {
|
|
189
|
-
diff.added.push(service);
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
for (const [name, service] of beforeMap) {
|
|
194
|
-
if (!afterMap.has(name)) {
|
|
195
|
-
diff.removed.push(service);
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
return diff;
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
module.exports = {
|
|
203
|
-
buildExternalContract,
|
|
204
|
-
validateExternalContract,
|
|
205
|
-
diffExternalContracts
|
|
206
|
-
};
|
|
1
|
+
/**
|
|
2
|
+
* External Contract Builder
|
|
3
|
+
* Builds external.json contract from truthpack (Stripe, GitHub, etc.)
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
"use strict";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Build external services contract from truthpack
|
|
10
|
+
*/
|
|
11
|
+
function buildExternalContract(truthpack) {
|
|
12
|
+
const contract = {
|
|
13
|
+
version: "1.0.0",
|
|
14
|
+
generatedAt: new Date().toISOString(),
|
|
15
|
+
services: []
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
// Extract billing/Stripe info
|
|
19
|
+
const billing = truthpack?.billing || {};
|
|
20
|
+
if (billing.hasStripe || billing.webhookCandidates?.length) {
|
|
21
|
+
const stripeService = {
|
|
22
|
+
name: "stripe",
|
|
23
|
+
envVars: extractStripeEnvVars(truthpack),
|
|
24
|
+
usedIn: billing.webhookCandidates?.map(w => w.file) || [],
|
|
25
|
+
webhooks: billing.webhookCandidates?.map(w => ({
|
|
26
|
+
path: w.path,
|
|
27
|
+
verified: w.hasSignatureVerification || false,
|
|
28
|
+
idempotent: w.hasIdempotency || false
|
|
29
|
+
})) || [],
|
|
30
|
+
evidence: billing.webhookCandidates?.flatMap(w => w.evidence || []) || []
|
|
31
|
+
};
|
|
32
|
+
contract.services.push(stripeService);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Detect other external services from env vars
|
|
36
|
+
const envVars = truthpack?.env?.vars || [];
|
|
37
|
+
|
|
38
|
+
// GitHub
|
|
39
|
+
const githubVars = envVars.filter(v => /github/i.test(v.name));
|
|
40
|
+
if (githubVars.length) {
|
|
41
|
+
contract.services.push({
|
|
42
|
+
name: "github",
|
|
43
|
+
envVars: githubVars.map(v => v.name),
|
|
44
|
+
usedIn: githubVars.flatMap(v => v.references?.map(r => r.file) || []),
|
|
45
|
+
evidence: githubVars.flatMap(v => v.references || [])
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// SendGrid
|
|
50
|
+
const sendgridVars = envVars.filter(v => /sendgrid/i.test(v.name));
|
|
51
|
+
if (sendgridVars.length) {
|
|
52
|
+
contract.services.push({
|
|
53
|
+
name: "sendgrid",
|
|
54
|
+
envVars: sendgridVars.map(v => v.name),
|
|
55
|
+
usedIn: sendgridVars.flatMap(v => v.references?.map(r => r.file) || []),
|
|
56
|
+
evidence: sendgridVars.flatMap(v => v.references || [])
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Twilio
|
|
61
|
+
const twilioVars = envVars.filter(v => /twilio/i.test(v.name));
|
|
62
|
+
if (twilioVars.length) {
|
|
63
|
+
contract.services.push({
|
|
64
|
+
name: "twilio",
|
|
65
|
+
envVars: twilioVars.map(v => v.name),
|
|
66
|
+
usedIn: twilioVars.flatMap(v => v.references?.map(r => r.file) || []),
|
|
67
|
+
evidence: twilioVars.flatMap(v => v.references || [])
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// AWS
|
|
72
|
+
const awsVars = envVars.filter(v => /^aws/i.test(v.name));
|
|
73
|
+
if (awsVars.length) {
|
|
74
|
+
contract.services.push({
|
|
75
|
+
name: "aws",
|
|
76
|
+
envVars: awsVars.map(v => v.name),
|
|
77
|
+
usedIn: awsVars.flatMap(v => v.references?.map(r => r.file) || []),
|
|
78
|
+
evidence: awsVars.flatMap(v => v.references || [])
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Supabase
|
|
83
|
+
const supabaseVars = envVars.filter(v => /supabase/i.test(v.name));
|
|
84
|
+
if (supabaseVars.length) {
|
|
85
|
+
contract.services.push({
|
|
86
|
+
name: "supabase",
|
|
87
|
+
envVars: supabaseVars.map(v => v.name).sort(),
|
|
88
|
+
usedIn: [...new Set(supabaseVars.flatMap(v => v.references?.map(r => r.file) || []))].sort(),
|
|
89
|
+
evidence: supabaseVars.flatMap(v => v.references || [])
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Deterministic output: sort services by name, and their internal arrays
|
|
94
|
+
contract.services.sort((a, b) => a.name.localeCompare(b.name));
|
|
95
|
+
for (const svc of contract.services) {
|
|
96
|
+
if (svc.envVars) svc.envVars.sort();
|
|
97
|
+
if (svc.usedIn) svc.usedIn = [...new Set(svc.usedIn)].sort();
|
|
98
|
+
if (svc.webhooks) svc.webhooks.sort((a, b) => a.path.localeCompare(b.path));
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return contract;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function extractStripeEnvVars(truthpack) {
|
|
105
|
+
const envVars = truthpack?.env?.vars || [];
|
|
106
|
+
return envVars
|
|
107
|
+
.filter(v => /stripe/i.test(v.name))
|
|
108
|
+
.map(v => v.name);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Validate external services contract
|
|
113
|
+
*/
|
|
114
|
+
function validateExternalContract(contract) {
|
|
115
|
+
const violations = [];
|
|
116
|
+
|
|
117
|
+
for (const service of contract.services) {
|
|
118
|
+
// Check Stripe webhook verification
|
|
119
|
+
if (service.name === "stripe") {
|
|
120
|
+
for (const webhook of service.webhooks || []) {
|
|
121
|
+
if (!webhook.verified) {
|
|
122
|
+
violations.push({
|
|
123
|
+
type: "unverified_webhook",
|
|
124
|
+
severity: "BLOCK",
|
|
125
|
+
service: "stripe",
|
|
126
|
+
path: webhook.path,
|
|
127
|
+
message: `Stripe webhook at ${webhook.path} missing signature verification`,
|
|
128
|
+
evidence: []
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
if (!webhook.idempotent) {
|
|
132
|
+
violations.push({
|
|
133
|
+
type: "non_idempotent_webhook",
|
|
134
|
+
severity: "WARN",
|
|
135
|
+
service: "stripe",
|
|
136
|
+
path: webhook.path,
|
|
137
|
+
message: `Stripe webhook at ${webhook.path} may not be idempotent`,
|
|
138
|
+
evidence: []
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Check for missing required env vars
|
|
145
|
+
const requiredVars = getRequiredVarsForService(service.name);
|
|
146
|
+
for (const required of requiredVars) {
|
|
147
|
+
if (!service.envVars.includes(required)) {
|
|
148
|
+
violations.push({
|
|
149
|
+
type: "missing_env",
|
|
150
|
+
severity: "WARN",
|
|
151
|
+
service: service.name,
|
|
152
|
+
envVar: required,
|
|
153
|
+
message: `Service ${service.name} typically requires ${required}`,
|
|
154
|
+
evidence: []
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return violations;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function getRequiredVarsForService(name) {
|
|
164
|
+
const requirements = {
|
|
165
|
+
stripe: ["STRIPE_SECRET_KEY", "STRIPE_WEBHOOK_SECRET"],
|
|
166
|
+
github: ["GITHUB_TOKEN"],
|
|
167
|
+
sendgrid: ["SENDGRID_API_KEY"],
|
|
168
|
+
twilio: ["TWILIO_ACCOUNT_SID", "TWILIO_AUTH_TOKEN"],
|
|
169
|
+
supabase: ["SUPABASE_URL", "SUPABASE_ANON_KEY"]
|
|
170
|
+
};
|
|
171
|
+
return requirements[name] || [];
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Diff two external contracts
|
|
176
|
+
*/
|
|
177
|
+
function diffExternalContracts(before, after) {
|
|
178
|
+
const diff = {
|
|
179
|
+
added: [],
|
|
180
|
+
removed: [],
|
|
181
|
+
changed: []
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
const beforeMap = new Map(before.services.map(s => [s.name, s]));
|
|
185
|
+
const afterMap = new Map(after.services.map(s => [s.name, s]));
|
|
186
|
+
|
|
187
|
+
for (const [name, service] of afterMap) {
|
|
188
|
+
if (!beforeMap.has(name)) {
|
|
189
|
+
diff.added.push(service);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
for (const [name, service] of beforeMap) {
|
|
194
|
+
if (!afterMap.has(name)) {
|
|
195
|
+
diff.removed.push(service);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return diff;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
module.exports = {
|
|
203
|
+
buildExternalContract,
|
|
204
|
+
validateExternalContract,
|
|
205
|
+
diffExternalContracts
|
|
206
|
+
};
|