@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
|
@@ -48,24 +48,33 @@ function resolve(projectRoot, claim) {
|
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
// Check if env var exists in .env.example or schema files
|
|
51
|
-
|
|
52
|
-
const
|
|
51
|
+
// Check multiple possible locations for .env.example
|
|
52
|
+
const envExamplePaths = [
|
|
53
|
+
path.join(projectRoot, ".env.example"),
|
|
54
|
+
path.join(projectRoot, "apps", "web-ui", ".env.example"),
|
|
55
|
+
path.join(projectRoot, "apps", "api", ".env.example")
|
|
56
|
+
];
|
|
53
57
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
58
|
+
for (const envExamplePath of envExamplePaths) {
|
|
59
|
+
if (fs.existsSync(envExamplePath)) {
|
|
60
|
+
const envExample = fs.readFileSync(envExamplePath, "utf8");
|
|
61
|
+
if (envExample.includes(envVarName)) {
|
|
62
|
+
const relativePath = path.relative(projectRoot, envExamplePath).replace(/\\/g, "/");
|
|
63
|
+
return {
|
|
64
|
+
result: "PROVEN",
|
|
65
|
+
sources: [{
|
|
66
|
+
type: "repo.search",
|
|
67
|
+
pointer: relativePath,
|
|
68
|
+
confidence: 0.7
|
|
69
|
+
}],
|
|
70
|
+
reason: `Environment variable ${envVarName} found in ${relativePath}`
|
|
71
|
+
};
|
|
72
|
+
}
|
|
66
73
|
}
|
|
67
74
|
}
|
|
68
75
|
|
|
76
|
+
const envSchemaPath = findEnvSchemaFile(projectRoot);
|
|
77
|
+
|
|
69
78
|
if (envSchemaPath && fs.existsSync(envSchemaPath)) {
|
|
70
79
|
const envSchema = fs.readFileSync(envSchemaPath, "utf8");
|
|
71
80
|
if (envSchema.includes(envVarName)) {
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
const { getRoutes } = require("../truthpack");
|
|
11
11
|
const { canonicalizePath } = require("../../route-truth");
|
|
12
|
+
const { shouldIgnore } = require("../utils/ignore-checker");
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
15
|
* Resolve route claim evidence
|
|
@@ -17,11 +18,81 @@ const { canonicalizePath } = require("../../route-truth");
|
|
|
17
18
|
* @returns {object} Evidence result
|
|
18
19
|
*/
|
|
19
20
|
function resolve(projectRoot, claim) {
|
|
20
|
-
|
|
21
|
+
// Skip checking routes from ignored files (test files, fixtures, etc.)
|
|
22
|
+
if (claim.pointer && shouldIgnore(projectRoot, claim.pointer.split(":")[0])) {
|
|
23
|
+
return {
|
|
24
|
+
result: "PROVEN",
|
|
25
|
+
sources: [{
|
|
26
|
+
type: "ignored",
|
|
27
|
+
pointer: claim.pointer,
|
|
28
|
+
confidence: 1.0
|
|
29
|
+
}],
|
|
30
|
+
reason: `Route from ignored file (test/fixture)`
|
|
31
|
+
};
|
|
32
|
+
}
|
|
21
33
|
|
|
22
34
|
// Normalize route path from claim
|
|
23
35
|
const routePath = canonicalizePath(claim.value);
|
|
24
36
|
|
|
37
|
+
// Skip external API calls (routes that don't start with /api/ in Next.js context)
|
|
38
|
+
// Routes like /content/blog, /content/faq are external backend API calls, not Next.js routes
|
|
39
|
+
// Only validate Next.js API routes (those in apps/web-ui/src/app/api/)
|
|
40
|
+
if (!routePath.startsWith('/api/')) {
|
|
41
|
+
return {
|
|
42
|
+
result: "PROVEN",
|
|
43
|
+
sources: [{
|
|
44
|
+
type: "external_api",
|
|
45
|
+
pointer: claim.pointer,
|
|
46
|
+
confidence: 1.0
|
|
47
|
+
}],
|
|
48
|
+
reason: `Route ${routePath} is an external API call, not a Next.js route`
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Skip wildcard patterns from template literals (they're approximations)
|
|
53
|
+
// These are detected from fetch(`${url}/api/...`) and are not exact routes
|
|
54
|
+
if (routePath.includes('*') || routePath === '/api/*') {
|
|
55
|
+
return {
|
|
56
|
+
result: "PROVEN",
|
|
57
|
+
sources: [{
|
|
58
|
+
type: "pattern",
|
|
59
|
+
pointer: claim.pointer,
|
|
60
|
+
confidence: 0.8
|
|
61
|
+
}],
|
|
62
|
+
reason: `Route pattern ${routePath} is a template literal approximation, not an exact route`
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Skip backend API routes (routes from template literals using apiUrl)
|
|
67
|
+
// These are calls to the backend API server, not Next.js API routes
|
|
68
|
+
if (claim.isBackendApi || (claim.reason && claim.reason.includes('backend API'))) {
|
|
69
|
+
return {
|
|
70
|
+
result: "PROVEN",
|
|
71
|
+
sources: [{
|
|
72
|
+
type: "backend_api",
|
|
73
|
+
pointer: claim.pointer,
|
|
74
|
+
confidence: 0.9
|
|
75
|
+
}],
|
|
76
|
+
reason: `Route ${routePath} is a backend API call (using apiUrl variable), not a Next.js route`
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Skip routes that are clearly backend API routes (v1, v2, etc. are typically backend)
|
|
81
|
+
// Next.js API routes are usually simpler like /api/checkout, /api/health
|
|
82
|
+
if (routePath.match(/^\/api\/v\d+\//)) {
|
|
83
|
+
return {
|
|
84
|
+
result: "PROVEN",
|
|
85
|
+
sources: [{
|
|
86
|
+
type: "backend_api",
|
|
87
|
+
pointer: claim.pointer,
|
|
88
|
+
confidence: 0.85
|
|
89
|
+
}],
|
|
90
|
+
reason: `Route ${routePath} appears to be a backend API route (versioned), not a Next.js route`
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const routes = getRoutes(projectRoot);
|
|
95
|
+
|
|
25
96
|
// Check if route exists in truthpack
|
|
26
97
|
if (routes && routes.length > 0) {
|
|
27
98
|
// First, check for exact match
|
|
@@ -77,7 +77,7 @@ async function interceptFileWrite({
|
|
|
77
77
|
}];
|
|
78
78
|
|
|
79
79
|
// Evaluate policy
|
|
80
|
-
const verdict = evaluatePolicy({
|
|
80
|
+
const verdict = await evaluatePolicy({
|
|
81
81
|
policy,
|
|
82
82
|
claims,
|
|
83
83
|
evidence,
|
|
@@ -159,7 +159,7 @@ async function interceptMultiFileWrite({
|
|
|
159
159
|
const evidence = resolveEvidence(projectRoot, allClaims);
|
|
160
160
|
|
|
161
161
|
// Evaluate policy
|
|
162
|
-
const verdict = evaluatePolicy({
|
|
162
|
+
const verdict = await evaluatePolicy({
|
|
163
163
|
policy,
|
|
164
164
|
claims: allClaims,
|
|
165
165
|
evidence,
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Policy Engine
|
|
3
3
|
*
|
|
4
4
|
* Main policy engine that evaluates all rules deterministically.
|
|
5
|
-
*
|
|
5
|
+
* Uses AI to reduce false positives when enabled.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
"use strict";
|
|
@@ -15,6 +15,7 @@ const fakeSuccess = require("./rules/fake-success");
|
|
|
15
15
|
const scope = require("./rules/scope");
|
|
16
16
|
const unsafeSideEffect = require("./rules/unsafe-side-effect");
|
|
17
17
|
const { generateVerdict } = require("./verdict");
|
|
18
|
+
const { analyzeFalsePositive } = require("../ai/false-positive-analyzer");
|
|
18
19
|
|
|
19
20
|
/**
|
|
20
21
|
* Evaluate policy against change packet
|
|
@@ -24,9 +25,9 @@ const { generateVerdict } = require("./verdict");
|
|
|
24
25
|
* @param {array} params.evidence - Evidence resolution results
|
|
25
26
|
* @param {array} params.files - Changed files
|
|
26
27
|
* @param {string} params.intent - Agent intent message
|
|
27
|
-
* @returns {object} Verdict object
|
|
28
|
+
* @returns {Promise<object>} Verdict object
|
|
28
29
|
*/
|
|
29
|
-
function evaluatePolicy({ policy, claims, evidence, files, intent }) {
|
|
30
|
+
async function evaluatePolicy({ policy, claims, evidence, files, intent }) {
|
|
30
31
|
const violations = [];
|
|
31
32
|
|
|
32
33
|
// Evaluate all rules
|
|
@@ -44,6 +45,36 @@ function evaluatePolicy({ policy, claims, evidence, files, intent }) {
|
|
|
44
45
|
try {
|
|
45
46
|
const violation = evaluator.evaluate({ claims, evidence, policy });
|
|
46
47
|
if (violation) {
|
|
48
|
+
// Use AI to check if this is a false positive
|
|
49
|
+
const claim = claims.find(c =>
|
|
50
|
+
c.type === violation.claim?.type &&
|
|
51
|
+
c.value === violation.claim?.value
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
if (claim && policy.rules?.ai_false_positive_detection?.enabled) {
|
|
55
|
+
try {
|
|
56
|
+
const aiAnalysis = await analyzeFalsePositive({
|
|
57
|
+
violation,
|
|
58
|
+
claim,
|
|
59
|
+
filePath: claim.file || violation.claim?.file || "unknown",
|
|
60
|
+
projectRoot: process.cwd(),
|
|
61
|
+
policy
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
// If AI determines it's a false positive with high confidence, skip it
|
|
65
|
+
if (aiAnalysis.isFalsePositive && aiAnalysis.confidence >= 0.8) {
|
|
66
|
+
// Log but don't add to violations
|
|
67
|
+
if (policy.rules?.ai_false_positive_detection?.logSkipped) {
|
|
68
|
+
console.log(`[AI] Skipping false positive: ${violation.message} (confidence: ${aiAnalysis.confidence}, reason: ${aiAnalysis.reason})`);
|
|
69
|
+
}
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
} catch (aiError) {
|
|
73
|
+
// If AI analysis fails, proceed with the violation
|
|
74
|
+
console.warn(`[AI] False positive analysis failed: ${aiError.message}`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
47
78
|
violations.push(violation);
|
|
48
79
|
}
|
|
49
80
|
} catch (error) {
|
|
@@ -30,7 +30,32 @@ function evaluate({ claims, evidence, policy }) {
|
|
|
30
30
|
return null;
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
//
|
|
33
|
+
// Filter out false positives: if there's an HTTP call in the same file, it's likely a response check
|
|
34
|
+
const realSuccessClaims = successClaims.filter(claim => {
|
|
35
|
+
const claimFile = claim.pointer ? claim.pointer.split(":")[0] : "";
|
|
36
|
+
if (!claimFile) return true;
|
|
37
|
+
|
|
38
|
+
// Check if there's an HTTP call in the same file (within reasonable distance)
|
|
39
|
+
const claimLine = claim.pointer ? parseInt(claim.pointer.split(":")[1]?.split("-")[0] || "0", 10) : 0;
|
|
40
|
+
const hasNearbyHttpCall = claims.some(c => {
|
|
41
|
+
if (c.type !== CLAIM_TYPES.HTTP_CALL && c.type !== CLAIM_TYPES.ROUTE) return false;
|
|
42
|
+
const cFile = c.pointer ? c.pointer.split(":")[0] : "";
|
|
43
|
+
if (cFile !== claimFile) return false;
|
|
44
|
+
const cLine = c.pointer ? parseInt(c.pointer.split(":")[1]?.split("-")[0] || "0", 10) : 0;
|
|
45
|
+
// Within 50 lines is considered "nearby"
|
|
46
|
+
return Math.abs(cLine - claimLine) <= 50;
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
// If there's a nearby HTTP call, this is likely checking the response, not showing UI
|
|
50
|
+
// Only flag if there's NO HTTP call nearby
|
|
51
|
+
return !hasNearbyHttpCall;
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
if (realSuccessClaims.length === 0) {
|
|
55
|
+
return null; // All were false positives (likely API response checks)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Check if there's a corresponding HTTP call or side effect globally
|
|
34
59
|
const hasHttpCall = claims.some(c =>
|
|
35
60
|
c.type === CLAIM_TYPES.HTTP_CALL || c.type === CLAIM_TYPES.ROUTE
|
|
36
61
|
);
|
|
@@ -40,7 +65,7 @@ function evaluate({ claims, evidence, policy }) {
|
|
|
40
65
|
if (!hasHttpCall && !hasSideEffect) {
|
|
41
66
|
// Check if domain requires blocking
|
|
42
67
|
const blockDomains = ruleConfig.block_if_domain || [];
|
|
43
|
-
const fileDomain =
|
|
68
|
+
const fileDomain = realSuccessClaims[0].domain || "general";
|
|
44
69
|
|
|
45
70
|
const shouldBlock = blockDomains.includes(fileDomain);
|
|
46
71
|
|
|
@@ -48,8 +73,8 @@ function evaluate({ claims, evidence, policy }) {
|
|
|
48
73
|
rule: "fake_success_ui",
|
|
49
74
|
severity: shouldBlock ? "block" : (ruleConfig.severity || "warn"),
|
|
50
75
|
message: `Fake success UI: Success message shown but no HTTP call or side effect detected`,
|
|
51
|
-
claimId: `claim_${claims.indexOf(
|
|
52
|
-
claim:
|
|
76
|
+
claimId: `claim_${claims.indexOf(realSuccessClaims[0])}`,
|
|
77
|
+
claim: realSuccessClaims[0]
|
|
53
78
|
};
|
|
54
79
|
}
|
|
55
80
|
|
|
@@ -30,7 +30,19 @@ function evaluate({ claims, evidence, policy }) {
|
|
|
30
30
|
if (claim.type === CLAIM_TYPES.ROUTE || claim.type === CLAIM_TYPES.HTTP_CALL) {
|
|
31
31
|
const ev = evidence.find(e => e.claimId === `claim_${i}`);
|
|
32
32
|
|
|
33
|
+
// Skip if evidence shows it's an external API call or ignored file
|
|
34
|
+
if (ev && (ev.result === "PROVEN" && (ev.reason?.includes("external API") || ev.reason?.includes("ignored file")))) {
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
|
|
33
38
|
if (ev && ev.result === "UNPROVEN") {
|
|
39
|
+
// Double-check: skip external API calls (routes not starting with /api/)
|
|
40
|
+
const routePath = String(claim.value || "").trim();
|
|
41
|
+
if (!routePath.startsWith('/api/')) {
|
|
42
|
+
// This is an external API call, not a Next.js route - skip
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
|
|
34
46
|
return {
|
|
35
47
|
rule: "ghost_route",
|
|
36
48
|
severity: ruleConfig.severity || "block",
|
|
@@ -65,6 +65,27 @@ function loadTruthpackFile(projectRoot, filename) {
|
|
|
65
65
|
const filePath = path.join(projectRoot, TRUTHPACK_DIR, filename);
|
|
66
66
|
|
|
67
67
|
if (!fs.existsSync(filePath)) {
|
|
68
|
+
// Fallback: try loading from truthpack.json if routes.json doesn't exist
|
|
69
|
+
if (filename === "routes.json") {
|
|
70
|
+
const truthpackPath = path.join(projectRoot, ".vibecheck", "truthpack.json");
|
|
71
|
+
if (fs.existsSync(truthpackPath)) {
|
|
72
|
+
try {
|
|
73
|
+
const truthpack = JSON.parse(fs.readFileSync(truthpackPath, "utf8"));
|
|
74
|
+
// Extract server routes from truthpack.json format
|
|
75
|
+
if (truthpack.routes && truthpack.routes.server) {
|
|
76
|
+
return {
|
|
77
|
+
routes: truthpack.routes.server.map(r => ({
|
|
78
|
+
path: r.path,
|
|
79
|
+
method: r.method,
|
|
80
|
+
handler: r.handler || r.source
|
|
81
|
+
}))
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
} catch (error) {
|
|
85
|
+
// Fall through to return null
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
68
89
|
return null;
|
|
69
90
|
}
|
|
70
91
|
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ignore File Checker
|
|
3
|
+
*
|
|
4
|
+
* Checks if a file matches patterns in .vibecheckignore
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
"use strict";
|
|
8
|
+
|
|
9
|
+
const fs = require("fs");
|
|
10
|
+
const path = require("path");
|
|
11
|
+
|
|
12
|
+
// Try to use minimatch if available, otherwise use simple pattern matching
|
|
13
|
+
let minimatch;
|
|
14
|
+
try {
|
|
15
|
+
minimatch = require("minimatch").minimatch;
|
|
16
|
+
} catch {
|
|
17
|
+
minimatch = null;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
let ignorePatterns = null;
|
|
21
|
+
let ignoreFile = null;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Load ignore patterns from .vibecheckignore
|
|
25
|
+
* @param {string} projectRoot - Project root directory
|
|
26
|
+
* @returns {array} Array of ignore patterns
|
|
27
|
+
*/
|
|
28
|
+
function loadIgnorePatterns(projectRoot) {
|
|
29
|
+
if (ignorePatterns !== null && ignoreFile === projectRoot) {
|
|
30
|
+
return ignorePatterns;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const ignorePath = path.join(projectRoot, ".vibecheckignore");
|
|
34
|
+
ignorePatterns = [];
|
|
35
|
+
|
|
36
|
+
if (fs.existsSync(ignorePath)) {
|
|
37
|
+
const content = fs.readFileSync(ignorePath, "utf8");
|
|
38
|
+
ignorePatterns = content
|
|
39
|
+
.split("\n")
|
|
40
|
+
.map(line => line.trim())
|
|
41
|
+
.filter(line => line && !line.startsWith("#"));
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
ignoreFile = projectRoot;
|
|
45
|
+
return ignorePatterns;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Check if a file should be ignored
|
|
50
|
+
* @param {string} projectRoot - Project root directory
|
|
51
|
+
* @param {string} filePath - File path (relative to project root)
|
|
52
|
+
* @returns {boolean} True if file should be ignored
|
|
53
|
+
*/
|
|
54
|
+
function shouldIgnore(projectRoot, filePath) {
|
|
55
|
+
const patterns = loadIgnorePatterns(projectRoot);
|
|
56
|
+
const normalizedPath = filePath.replace(/\\/g, "/");
|
|
57
|
+
|
|
58
|
+
// Check against all patterns
|
|
59
|
+
for (const pattern of patterns) {
|
|
60
|
+
// Handle both glob patterns and simple path matches
|
|
61
|
+
if (minimatch) {
|
|
62
|
+
if (minimatch(normalizedPath, pattern, { matchBase: true }) ||
|
|
63
|
+
minimatch(normalizedPath, `**/${pattern}`, { matchBase: true })) {
|
|
64
|
+
return true;
|
|
65
|
+
}
|
|
66
|
+
} else {
|
|
67
|
+
// Fallback: simple pattern matching
|
|
68
|
+
const regexPattern = pattern
|
|
69
|
+
.replace(/\*\*/g, ".*")
|
|
70
|
+
.replace(/\*/g, "[^/]*")
|
|
71
|
+
.replace(/\//g, "\\/");
|
|
72
|
+
const regex = new RegExp(`^${regexPattern}$`);
|
|
73
|
+
if (regex.test(normalizedPath)) {
|
|
74
|
+
return true;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Also check if path contains the pattern as a substring (for directory patterns)
|
|
79
|
+
const cleanPattern = pattern.replace(/\*\*/g, "").replace(/\*/g, "");
|
|
80
|
+
if (normalizedPath.includes(cleanPattern)) {
|
|
81
|
+
return true;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Also check common test file patterns
|
|
86
|
+
const testPatterns = [
|
|
87
|
+
/\/__tests__\//,
|
|
88
|
+
/\.test\.(ts|tsx|js|jsx)$/,
|
|
89
|
+
/\.spec\.(ts|tsx|js|jsx)$/,
|
|
90
|
+
/\/tests\//,
|
|
91
|
+
/\/test\//,
|
|
92
|
+
/\/fixtures\//,
|
|
93
|
+
/\/examples\//,
|
|
94
|
+
/\/templates\//
|
|
95
|
+
];
|
|
96
|
+
|
|
97
|
+
for (const pattern of testPatterns) {
|
|
98
|
+
if (pattern.test(normalizedPath)) {
|
|
99
|
+
return true;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Clear cached ignore patterns (for testing)
|
|
108
|
+
*/
|
|
109
|
+
function clearCache() {
|
|
110
|
+
ignorePatterns = null;
|
|
111
|
+
ignoreFile = null;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
module.exports = {
|
|
115
|
+
shouldIgnore,
|
|
116
|
+
loadIgnorePatterns,
|
|
117
|
+
clearCache
|
|
118
|
+
};
|