@vibecheckai/cli 3.1.6 → 3.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +27 -32
- package/bin/registry.js +208 -343
- package/bin/runners/context/generators/mcp.js +18 -0
- package/bin/runners/context/index.js +72 -4
- package/bin/runners/context/proof-context.js +293 -1
- package/bin/runners/context/security-scanner.js +311 -73
- package/bin/runners/lib/analyzers.js +607 -20
- package/bin/runners/lib/detectors-v2.js +172 -15
- package/bin/runners/lib/entitlements-v2.js +48 -1
- package/bin/runners/lib/evidence-pack.js +678 -0
- package/bin/runners/lib/html-proof-report.js +913 -0
- package/bin/runners/lib/missions/plan.js +231 -41
- package/bin/runners/lib/missions/templates.js +125 -0
- package/bin/runners/lib/scan-output.js +492 -253
- package/bin/runners/lib/ship-output.js +901 -641
- package/bin/runners/runCheckpoint.js +44 -3
- package/bin/runners/runContext.d.ts +4 -0
- package/bin/runners/runContext.js +2 -3
- package/bin/runners/runDoctor.js +11 -4
- package/bin/runners/runFix.js +51 -341
- package/bin/runners/runInit.js +37 -20
- package/bin/runners/runPolish.d.ts +4 -0
- package/bin/runners/runPolish.js +608 -29
- package/bin/runners/runProve.js +210 -25
- package/bin/runners/runReality.js +861 -107
- package/bin/runners/runScan.js +238 -4
- package/bin/runners/runShip.js +19 -3
- package/bin/runners/runWatch.js +25 -5
- package/bin/vibecheck.js +35 -47
- package/mcp-server/consolidated-tools.js +408 -42
- package/mcp-server/index.js +152 -15
- package/mcp-server/package.json +1 -1
- package/mcp-server/proof-tools.js +571 -0
- package/mcp-server/tier-auth.js +22 -19
- package/mcp-server/tools-v3.js +744 -0
- package/mcp-server/truth-firewall-tools.js +190 -4
- package/package.json +3 -1
- package/bin/runners/runBadge.js +0 -916
- package/bin/runners/runContracts.js +0 -105
- package/bin/runners/runCtx.js +0 -680
- package/bin/runners/runCtxDiff.js +0 -301
- package/bin/runners/runCtxGuard.js +0 -176
- package/bin/runners/runCtxSync.js +0 -116
- package/bin/runners/runExport.js +0 -93
- package/bin/runners/runGraph.js +0 -454
- package/bin/runners/runInstall.js +0 -273
- package/bin/runners/runLabs.js +0 -341
- package/bin/runners/runLaunch.js +0 -181
- package/bin/runners/runPR.js +0 -255
- package/bin/runners/runPermissions.js +0 -310
- package/bin/runners/runPreflight.js +0 -580
- package/bin/runners/runReplay.js +0 -499
- package/bin/runners/runSecurity.js +0 -92
- package/bin/runners/runShare.js +0 -212
- package/bin/runners/runStatus.js +0 -102
- package/bin/runners/runVerify.js +0 -272
|
@@ -1,310 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* vibecheck permissions - AuthZ Matrix & IDOR Detection
|
|
3
|
-
*
|
|
4
|
-
* Verifies authorization is correct, not just present:
|
|
5
|
-
* - Which roles can hit which routes
|
|
6
|
-
* - Which UI actions leak data cross-user (IDOR)
|
|
7
|
-
* - Which admin endpoints are accidentally public
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
"use strict";
|
|
11
|
-
|
|
12
|
-
const path = require("path");
|
|
13
|
-
const fs = require("fs");
|
|
14
|
-
const { buildTruthpack, loadTruthpack } = require("./lib/truth");
|
|
15
|
-
const {
|
|
16
|
-
extractAuthModel,
|
|
17
|
-
buildAuthZMatrix,
|
|
18
|
-
formatMatrix,
|
|
19
|
-
detectIDORCandidates,
|
|
20
|
-
buildIDORTestPlan,
|
|
21
|
-
formatIDORPlan
|
|
22
|
-
} = require("./lib/permissions");
|
|
23
|
-
const { parseGlobalFlags, shouldShowBanner } = require("./lib/global-flags");
|
|
24
|
-
|
|
25
|
-
// Entitlements enforcement
|
|
26
|
-
const entitlements = require("./lib/entitlements-v2");
|
|
27
|
-
|
|
28
|
-
const c = {
|
|
29
|
-
reset: '\x1b[0m',
|
|
30
|
-
bold: '\x1b[1m',
|
|
31
|
-
dim: '\x1b[2m',
|
|
32
|
-
green: '\x1b[32m',
|
|
33
|
-
yellow: '\x1b[33m',
|
|
34
|
-
cyan: '\x1b[36m',
|
|
35
|
-
red: '\x1b[31m',
|
|
36
|
-
blue: '\x1b[34m',
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
function ensureDir(p) {
|
|
40
|
-
fs.mkdirSync(p, { recursive: true });
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
async function runPermissions(args) {
|
|
44
|
-
const opts = parseArgs(args);
|
|
45
|
-
|
|
46
|
-
if (opts.help) {
|
|
47
|
-
printHelp(shouldShowBanner(opts));
|
|
48
|
-
return 0;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// TIER ENFORCEMENT: COMPLETE only
|
|
52
|
-
const access = await entitlements.enforce("permissions", {
|
|
53
|
-
projectPath: opts.path || process.cwd(),
|
|
54
|
-
silent: false,
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
if (!access.allowed) {
|
|
58
|
-
console.log(`\n${c.yellow}Tip:${c.reset} The permissions command requires COMPLETE tier for advanced AuthZ analysis.`);
|
|
59
|
-
return entitlements.EXIT_FEATURE_NOT_ALLOWED;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
const root = path.resolve(opts.path || process.cwd());
|
|
63
|
-
const outDir = path.join(root, ".vibecheck", "permissions");
|
|
64
|
-
ensureDir(outDir);
|
|
65
|
-
|
|
66
|
-
console.log(`\n${c.cyan}${c.bold}🔐 vibecheck permissions${c.reset}`);
|
|
67
|
-
|
|
68
|
-
// Load or build truthpack
|
|
69
|
-
let truthpack = loadTruthpack(root);
|
|
70
|
-
if (!truthpack) {
|
|
71
|
-
console.log(`${c.dim}Building truthpack...${c.reset}`);
|
|
72
|
-
truthpack = await buildTruthpack({ repoRoot: root, fastifyEntry: opts.fastifyEntry });
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// Extract auth model
|
|
76
|
-
console.log(`${c.dim}Extracting auth model...${c.reset}`);
|
|
77
|
-
const authModel = await extractAuthModel(root, truthpack);
|
|
78
|
-
|
|
79
|
-
// Save auth model
|
|
80
|
-
fs.writeFileSync(
|
|
81
|
-
path.join(outDir, "auth-model.json"),
|
|
82
|
-
JSON.stringify(authModel, null, 2),
|
|
83
|
-
"utf8"
|
|
84
|
-
);
|
|
85
|
-
|
|
86
|
-
if (opts.learn) {
|
|
87
|
-
// Learning mode - just extract and display
|
|
88
|
-
console.log(`\n${c.bold}Auth Model${c.reset}`);
|
|
89
|
-
console.log(` Roles: ${authModel.roles.map(r => r.name).join(", ")}`);
|
|
90
|
-
console.log(` Protected patterns: ${authModel.protectedPatterns?.length || 0}`);
|
|
91
|
-
console.log(` Routes: ${authModel.routes.length}`);
|
|
92
|
-
console.log(` RBAC patterns: ${authModel.rbacPatterns.length}`);
|
|
93
|
-
|
|
94
|
-
if (authModel.roles.length > 0) {
|
|
95
|
-
console.log(`\n${c.bold}Detected Roles${c.reset}`);
|
|
96
|
-
for (const role of authModel.roles) {
|
|
97
|
-
console.log(` - ${role.name} (${role.evidence.length} references)`);
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
console.log(`\n${c.dim}Auth model saved to: .vibecheck/permissions/auth-model.json${c.reset}`);
|
|
102
|
-
console.log(`${c.dim}Run 'vibecheck permissions --prove' to verify at runtime${c.reset}\n`);
|
|
103
|
-
return 0;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
if (opts.prove) {
|
|
107
|
-
// Runtime verification mode
|
|
108
|
-
if (!opts.url) {
|
|
109
|
-
console.log(`${c.red}Error: --prove requires --url${c.reset}`);
|
|
110
|
-
return 1;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
console.log(`${c.dim}Running permission verification against ${opts.url}...${c.reset}`);
|
|
114
|
-
|
|
115
|
-
// This would run actual browser tests
|
|
116
|
-
// For now, show what would be tested
|
|
117
|
-
const matrix = buildAuthZMatrix(authModel, null);
|
|
118
|
-
|
|
119
|
-
console.log(`\n${c.bold}AuthZ Matrix${c.reset}`);
|
|
120
|
-
console.log(` Routes to verify: ${matrix.routes.length}`);
|
|
121
|
-
console.log(` Roles to test: ${matrix.roles.join(", ")}`);
|
|
122
|
-
|
|
123
|
-
// Save matrix
|
|
124
|
-
fs.writeFileSync(
|
|
125
|
-
path.join(outDir, "authz-matrix.json"),
|
|
126
|
-
JSON.stringify(matrix, null, 2),
|
|
127
|
-
"utf8"
|
|
128
|
-
);
|
|
129
|
-
|
|
130
|
-
fs.writeFileSync(
|
|
131
|
-
path.join(outDir, "authz-matrix.md"),
|
|
132
|
-
formatMatrix(matrix),
|
|
133
|
-
"utf8"
|
|
134
|
-
);
|
|
135
|
-
|
|
136
|
-
const blocks = matrix.violations.filter(v => v.severity === "BLOCK").length;
|
|
137
|
-
const warns = matrix.violations.filter(v => v.severity === "WARN").length;
|
|
138
|
-
|
|
139
|
-
if (matrix.violations.length > 0) {
|
|
140
|
-
console.log(`\n${c.bold}Violations${c.reset}`);
|
|
141
|
-
for (const v of matrix.violations.slice(0, 10)) {
|
|
142
|
-
const icon = v.severity === "BLOCK" ? `${c.red}✗` : `${c.yellow}⚠`;
|
|
143
|
-
console.log(` ${icon} ${v.message}${c.reset}`);
|
|
144
|
-
}
|
|
145
|
-
} else {
|
|
146
|
-
console.log(`\n${c.green}✓ No violations detected${c.reset}`);
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
console.log(`\n${c.dim}Matrix saved to: .vibecheck/permissions/authz-matrix.json${c.reset}`);
|
|
150
|
-
console.log(`\n${c.bold}Verdict:${c.reset} ${blocks ? `${c.red}🛑 BLOCK${c.reset}` : warns ? `${c.yellow}⚠️ WARN${c.reset}` : `${c.green}✅ CLEAN${c.reset}`}`);
|
|
151
|
-
|
|
152
|
-
return blocks ? 2 : warns ? 1 : 0;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
if (opts.matrix) {
|
|
156
|
-
// Output matrix mode
|
|
157
|
-
const matrix = buildAuthZMatrix(authModel, null);
|
|
158
|
-
console.log("\n" + formatMatrix(matrix));
|
|
159
|
-
|
|
160
|
-
fs.writeFileSync(
|
|
161
|
-
path.join(outDir, "authz-matrix.json"),
|
|
162
|
-
JSON.stringify(matrix, null, 2),
|
|
163
|
-
"utf8"
|
|
164
|
-
);
|
|
165
|
-
|
|
166
|
-
fs.writeFileSync(
|
|
167
|
-
path.join(outDir, "authz-matrix.md"),
|
|
168
|
-
formatMatrix(matrix),
|
|
169
|
-
"utf8"
|
|
170
|
-
);
|
|
171
|
-
|
|
172
|
-
return 0;
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
if (opts.idor) {
|
|
176
|
-
// IDOR detection mode
|
|
177
|
-
console.log(`${c.dim}Detecting IDOR candidates...${c.reset}`);
|
|
178
|
-
|
|
179
|
-
const candidates = detectIDORCandidates(authModel);
|
|
180
|
-
const plan = buildIDORTestPlan(candidates, { safe: opts.safe });
|
|
181
|
-
|
|
182
|
-
console.log(`\n${c.bold}IDOR Candidates${c.reset}`);
|
|
183
|
-
console.log(` Detected: ${candidates.length}`);
|
|
184
|
-
|
|
185
|
-
if (candidates.length > 0) {
|
|
186
|
-
for (const c of candidates.slice(0, 10)) {
|
|
187
|
-
console.log(` - ${c.resourceType}: ${c.path}`);
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
// Save plan
|
|
192
|
-
fs.writeFileSync(
|
|
193
|
-
path.join(outDir, "idor-plan.json"),
|
|
194
|
-
JSON.stringify(plan, null, 2),
|
|
195
|
-
"utf8"
|
|
196
|
-
);
|
|
197
|
-
|
|
198
|
-
fs.writeFileSync(
|
|
199
|
-
path.join(outDir, "idor-plan.md"),
|
|
200
|
-
formatIDORPlan(plan),
|
|
201
|
-
"utf8"
|
|
202
|
-
);
|
|
203
|
-
|
|
204
|
-
console.log(`\n${c.dim}IDOR plan saved to: .vibecheck/permissions/idor-plan.json${c.reset}`);
|
|
205
|
-
|
|
206
|
-
if (!opts.safe) {
|
|
207
|
-
console.log(`\n${c.yellow}⚠️ IDOR tests require --safe flag for first-party testing${c.reset}`);
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
return 0;
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
// Default: show summary
|
|
214
|
-
console.log(`\n${c.bold}Summary${c.reset}`);
|
|
215
|
-
console.log(` Roles: ${authModel.roles.length}`);
|
|
216
|
-
console.log(` Routes: ${authModel.routes.length}`);
|
|
217
|
-
console.log(` Protected patterns: ${authModel.protectedPatterns?.length || 0}`);
|
|
218
|
-
|
|
219
|
-
console.log(`\n${c.bold}Run one of:${c.reset}`);
|
|
220
|
-
console.log(` ${c.cyan}vibecheck permissions --learn${c.reset} Extract auth model from code`);
|
|
221
|
-
console.log(` ${c.cyan}vibecheck permissions --prove${c.reset} Runtime verification`);
|
|
222
|
-
console.log(` ${c.cyan}vibecheck permissions --matrix${c.reset} Output AuthZ matrix`);
|
|
223
|
-
console.log(` ${c.cyan}vibecheck permissions --idor${c.reset} Detect IDOR candidates\n`);
|
|
224
|
-
|
|
225
|
-
return 0;
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
function parseArgs(args) {
|
|
229
|
-
// Parse global flags first
|
|
230
|
-
const { flags: globalFlags, cleanArgs } = parseGlobalFlags(args);
|
|
231
|
-
|
|
232
|
-
const opts = {
|
|
233
|
-
...globalFlags, // Merge global flags (json, verbose, noBanner, quiet, ci, etc.)
|
|
234
|
-
path: globalFlags.path || process.cwd(),
|
|
235
|
-
url: null,
|
|
236
|
-
learn: false,
|
|
237
|
-
prove: false,
|
|
238
|
-
matrix: false,
|
|
239
|
-
idor: false,
|
|
240
|
-
safe: false,
|
|
241
|
-
fastifyEntry: null,
|
|
242
|
-
help: false,
|
|
243
|
-
};
|
|
244
|
-
|
|
245
|
-
// Parse command-specific args from cleanArgs
|
|
246
|
-
for (let i = 0; i < cleanArgs.length; i++) {
|
|
247
|
-
const arg = cleanArgs[i];
|
|
248
|
-
if (arg === "--learn") opts.learn = true;
|
|
249
|
-
else if (arg === "--prove") opts.prove = true;
|
|
250
|
-
else if (arg === "--matrix") opts.matrix = true;
|
|
251
|
-
else if (arg === "--idor") opts.idor = true;
|
|
252
|
-
else if (arg === "--safe") opts.safe = true;
|
|
253
|
-
else if (arg === "--url") opts.url = cleanArgs[++i];
|
|
254
|
-
else if (arg === "--fastify-entry") opts.fastifyEntry = cleanArgs[++i];
|
|
255
|
-
else if (arg === "--path" || arg === "-p") opts.path = cleanArgs[++i];
|
|
256
|
-
else if (arg === "--help" || arg === "-h") opts.help = true;
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
return opts;
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
function printHelp() {
|
|
263
|
-
console.log(`
|
|
264
|
-
${c.cyan}${c.bold}🔐 vibecheck permissions${c.reset} - AuthZ Matrix & IDOR Detection
|
|
265
|
-
|
|
266
|
-
Verify authorization is correct, not just present.
|
|
267
|
-
|
|
268
|
-
${c.bold}USAGE${c.reset}
|
|
269
|
-
vibecheck permissions --learn ${c.dim}# Extract auth model from code${c.reset}
|
|
270
|
-
vibecheck permissions --prove --url ${c.dim}# Runtime verification${c.reset}
|
|
271
|
-
vibecheck permissions --matrix ${c.dim}# Output full AuthZ matrix${c.reset}
|
|
272
|
-
vibecheck permissions --idor --safe ${c.dim}# IDOR detection (first-party)${c.reset}
|
|
273
|
-
|
|
274
|
-
${c.bold}OPTIONS${c.reset}
|
|
275
|
-
--learn Extract auth model from code
|
|
276
|
-
--prove Runtime verification of permissions
|
|
277
|
-
--matrix Output AuthZ matrix
|
|
278
|
-
--idor Detect IDOR candidates
|
|
279
|
-
--safe Enable safe mode for IDOR testing (required)
|
|
280
|
-
--url <url> Target URL for runtime testing
|
|
281
|
-
--fastify-entry Fastify entry file (e.g. src/server.ts)
|
|
282
|
-
--path, -p Project path (default: current directory)
|
|
283
|
-
--help, -h Show this help
|
|
284
|
-
|
|
285
|
-
${c.bold}FINDINGS${c.reset}
|
|
286
|
-
${c.red}BLOCK${c.reset} Endpoint accessible as anon that should require auth
|
|
287
|
-
${c.red}BLOCK${c.reset} User A can access User B's resource (IDOR)
|
|
288
|
-
${c.red}BLOCK${c.reset} Role mismatch between UI + server
|
|
289
|
-
${c.yellow}WARN${c.reset} Admin endpoint missing role check (static)
|
|
290
|
-
|
|
291
|
-
${c.bold}OUTPUT${c.reset}
|
|
292
|
-
.vibecheck/permissions/
|
|
293
|
-
auth-model.json Extracted auth model
|
|
294
|
-
authz-matrix.json Full authorization matrix
|
|
295
|
-
authz-matrix.md Human-readable matrix
|
|
296
|
-
idor-plan.json IDOR test plan
|
|
297
|
-
|
|
298
|
-
${c.bold}SAFETY${c.reset}
|
|
299
|
-
IDOR tests only run with --idor --safe flag
|
|
300
|
-
Only use on local dev/staging with explicit consent
|
|
301
|
-
Configure test users in .vibecheck/config.json
|
|
302
|
-
|
|
303
|
-
${c.bold}EXAMPLES${c.reset}
|
|
304
|
-
vibecheck permissions --learn ${c.dim}# Extract model${c.reset}
|
|
305
|
-
vibecheck permissions --prove --url http://localhost:3000
|
|
306
|
-
vibecheck permissions --idor --safe ${c.dim}# IDOR detection${c.reset}
|
|
307
|
-
`);
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
module.exports = { runPermissions };
|