@vibecheckai/cli 3.5.0 → 3.5.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.
- package/bin/registry.js +214 -237
- package/bin/runners/cli-utils.js +33 -2
- package/bin/runners/context/analyzer.js +52 -1
- package/bin/runners/context/generators/cursor.js +2 -49
- package/bin/runners/context/git-context.js +3 -1
- package/bin/runners/context/team-conventions.js +33 -7
- package/bin/runners/lib/analysis-core.js +25 -5
- package/bin/runners/lib/analyzers.js +431 -481
- package/bin/runners/lib/default-config.js +127 -0
- package/bin/runners/lib/doctor/modules/security.js +3 -1
- package/bin/runners/lib/engine/ast-cache.js +210 -0
- package/bin/runners/lib/engine/auth-extractor.js +211 -0
- package/bin/runners/lib/engine/billing-extractor.js +112 -0
- package/bin/runners/lib/engine/enforcement-extractor.js +100 -0
- package/bin/runners/lib/engine/env-extractor.js +207 -0
- package/bin/runners/lib/engine/express-extractor.js +208 -0
- package/bin/runners/lib/engine/extractors.js +849 -0
- package/bin/runners/lib/engine/index.js +207 -0
- package/bin/runners/lib/engine/repo-index.js +514 -0
- package/bin/runners/lib/engine/types.js +124 -0
- package/bin/runners/lib/engines/accessibility-engine.js +18 -218
- package/bin/runners/lib/engines/api-consistency-engine.js +30 -335
- package/bin/runners/lib/engines/cross-file-analysis-engine.js +27 -292
- package/bin/runners/lib/engines/empty-catch-engine.js +17 -127
- package/bin/runners/lib/engines/mock-data-engine.js +10 -53
- package/bin/runners/lib/engines/performance-issues-engine.js +36 -176
- package/bin/runners/lib/engines/security-vulnerabilities-engine.js +54 -382
- package/bin/runners/lib/engines/type-aware-engine.js +39 -263
- package/bin/runners/lib/engines/vibecheck-engines/index.js +13 -122
- 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 +73 -373
- 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/entitlements-v2.js +73 -97
- package/bin/runners/lib/error-handler.js +44 -3
- package/bin/runners/lib/error-messages.js +289 -0
- package/bin/runners/lib/evidence-pack.js +7 -1
- package/bin/runners/lib/finding-id.js +69 -0
- package/bin/runners/lib/finding-sorter.js +89 -0
- package/bin/runners/lib/html-proof-report.js +700 -350
- package/bin/runners/lib/missions/plan.js +6 -46
- package/bin/runners/lib/missions/templates.js +0 -232
- package/bin/runners/lib/next-action.js +560 -0
- package/bin/runners/lib/prerequisites.js +149 -0
- package/bin/runners/lib/route-detection.js +137 -68
- package/bin/runners/lib/scan-output.js +91 -76
- package/bin/runners/lib/scan-runner.js +135 -0
- package/bin/runners/lib/schemas/ajv-validator.js +464 -0
- package/bin/runners/lib/schemas/error-envelope.schema.json +105 -0
- package/bin/runners/lib/schemas/finding-v3.schema.json +151 -0
- package/bin/runners/lib/schemas/report-artifact.schema.json +120 -0
- package/bin/runners/lib/schemas/run-request.schema.json +108 -0
- package/bin/runners/lib/schemas/validator.js +27 -0
- package/bin/runners/lib/schemas/verdict.schema.json +140 -0
- package/bin/runners/lib/ship-output-enterprise.js +23 -23
- package/bin/runners/lib/ship-output.js +75 -31
- package/bin/runners/lib/terminal-ui.js +6 -113
- package/bin/runners/lib/truth.js +351 -10
- package/bin/runners/lib/unified-cli-output.js +430 -603
- package/bin/runners/lib/unified-output.js +13 -9
- package/bin/runners/runAIAgent.js +10 -5
- package/bin/runners/runAgent.js +0 -3
- package/bin/runners/runAllowlist.js +389 -0
- package/bin/runners/runApprove.js +0 -33
- package/bin/runners/runAuth.js +73 -45
- package/bin/runners/runCheckpoint.js +51 -11
- package/bin/runners/runClassify.js +85 -21
- package/bin/runners/runContext.js +0 -3
- package/bin/runners/runDoctor.js +41 -28
- package/bin/runners/runEvidencePack.js +362 -0
- package/bin/runners/runFirewall.js +0 -3
- package/bin/runners/runFirewallHook.js +0 -3
- package/bin/runners/runFix.js +66 -76
- package/bin/runners/runGuard.js +18 -411
- package/bin/runners/runInit.js +113 -30
- package/bin/runners/runLabs.js +424 -0
- package/bin/runners/runMcp.js +19 -25
- package/bin/runners/runPolish.js +64 -240
- package/bin/runners/runPromptFirewall.js +12 -5
- package/bin/runners/runProve.js +57 -22
- package/bin/runners/runQuickstart.js +531 -0
- package/bin/runners/runReality.js +59 -68
- package/bin/runners/runReport.js +38 -33
- package/bin/runners/runRuntime.js +8 -5
- package/bin/runners/runScan.js +1413 -190
- package/bin/runners/runShip.js +113 -719
- package/bin/runners/runTruth.js +0 -3
- package/bin/runners/runValidate.js +13 -9
- package/bin/runners/runWatch.js +23 -14
- package/bin/scan.js +6 -1
- package/bin/vibecheck.js +204 -185
- package/mcp-server/deprecation-middleware.js +282 -0
- package/mcp-server/handlers/index.ts +15 -0
- package/mcp-server/handlers/tool-handler.ts +554 -0
- package/mcp-server/index-v1.js +698 -0
- package/mcp-server/index.js +210 -238
- package/mcp-server/lib/cache-wrapper.cjs +383 -0
- package/mcp-server/lib/error-envelope.js +138 -0
- package/mcp-server/lib/executor.ts +499 -0
- package/mcp-server/lib/index.ts +19 -0
- package/mcp-server/lib/rate-limiter.js +166 -0
- package/mcp-server/lib/sandbox.test.ts +519 -0
- package/mcp-server/lib/sandbox.ts +395 -0
- package/mcp-server/lib/types.ts +267 -0
- package/mcp-server/package.json +12 -3
- package/mcp-server/registry/tool-registry.js +794 -0
- package/mcp-server/registry/tools.json +605 -0
- package/mcp-server/registry.test.ts +334 -0
- package/mcp-server/tests/tier-gating.test.js +297 -0
- package/mcp-server/tier-auth.js +378 -45
- package/mcp-server/tools-v3.js +353 -442
- package/mcp-server/tsconfig.json +37 -0
- package/mcp-server/vibecheck-2.0-tools.js +14 -1
- package/package.json +1 -1
- package/bin/runners/lib/agent-firewall/learning/learning-engine.js +0 -849
- package/bin/runners/lib/audit-logger.js +0 -532
- package/bin/runners/lib/authority/authorities/architecture.js +0 -364
- package/bin/runners/lib/authority/authorities/compliance.js +0 -341
- package/bin/runners/lib/authority/authorities/human.js +0 -343
- package/bin/runners/lib/authority/authorities/quality.js +0 -420
- package/bin/runners/lib/authority/authorities/security.js +0 -228
- package/bin/runners/lib/authority/index.js +0 -293
- package/bin/runners/lib/bundle/bundle-intelligence.js +0 -846
- package/bin/runners/lib/cli-charts.js +0 -368
- package/bin/runners/lib/cli-config-display.js +0 -405
- package/bin/runners/lib/cli-demo.js +0 -275
- package/bin/runners/lib/cli-errors.js +0 -438
- package/bin/runners/lib/cli-help-formatter.js +0 -439
- package/bin/runners/lib/cli-interactive-menu.js +0 -509
- package/bin/runners/lib/cli-prompts.js +0 -441
- package/bin/runners/lib/cli-scan-cards.js +0 -362
- package/bin/runners/lib/compliance-reporter.js +0 -710
- package/bin/runners/lib/conductor/index.js +0 -671
- package/bin/runners/lib/easy/README.md +0 -123
- package/bin/runners/lib/easy/index.js +0 -140
- package/bin/runners/lib/easy/interactive-wizard.js +0 -788
- package/bin/runners/lib/easy/one-click-firewall.js +0 -564
- package/bin/runners/lib/easy/zero-config-reality.js +0 -714
- package/bin/runners/lib/engines/async-patterns-engine.js +0 -444
- package/bin/runners/lib/engines/bundle-size-engine.js +0 -433
- package/bin/runners/lib/engines/confidence-scoring.js +0 -276
- package/bin/runners/lib/engines/context-detection.js +0 -264
- package/bin/runners/lib/engines/database-patterns-engine.js +0 -429
- package/bin/runners/lib/engines/duplicate-code-engine.js +0 -354
- package/bin/runners/lib/engines/env-variables-engine.js +0 -458
- package/bin/runners/lib/engines/error-handling-engine.js +0 -437
- package/bin/runners/lib/engines/false-positive-prevention.js +0 -630
- package/bin/runners/lib/engines/framework-adapters/index.js +0 -607
- package/bin/runners/lib/engines/framework-detection.js +0 -508
- package/bin/runners/lib/engines/import-order-engine.js +0 -429
- package/bin/runners/lib/engines/naming-conventions-engine.js +0 -544
- package/bin/runners/lib/engines/noise-reduction-engine.js +0 -452
- package/bin/runners/lib/engines/orchestrator.js +0 -334
- package/bin/runners/lib/engines/react-patterns-engine.js +0 -457
- package/bin/runners/lib/engines/vibecheck-engines/lib/ai-hallucination-engine.js +0 -806
- package/bin/runners/lib/engines/vibecheck-engines/lib/smart-fix-engine.js +0 -577
- package/bin/runners/lib/engines/vibecheck-engines/lib/vibe-score-engine.js +0 -543
- package/bin/runners/lib/engines/vibecheck-engines.js +0 -514
- package/bin/runners/lib/enhanced-features/index.js +0 -305
- package/bin/runners/lib/enhanced-output.js +0 -631
- package/bin/runners/lib/enterprise.js +0 -300
- package/bin/runners/lib/firewall/command-validator.js +0 -351
- package/bin/runners/lib/firewall/config.js +0 -341
- package/bin/runners/lib/firewall/content-validator.js +0 -519
- package/bin/runners/lib/firewall/index.js +0 -101
- package/bin/runners/lib/firewall/path-validator.js +0 -256
- package/bin/runners/lib/intelligence/cross-repo-intelligence.js +0 -817
- package/bin/runners/lib/mcp-utils.js +0 -425
- package/bin/runners/lib/output/index.js +0 -1022
- package/bin/runners/lib/policy-engine.js +0 -652
- package/bin/runners/lib/polish/autofix/accessibility-fixes.js +0 -333
- package/bin/runners/lib/polish/autofix/async-handlers.js +0 -273
- package/bin/runners/lib/polish/autofix/dead-code.js +0 -280
- package/bin/runners/lib/polish/autofix/imports-optimizer.js +0 -344
- package/bin/runners/lib/polish/autofix/index.js +0 -200
- package/bin/runners/lib/polish/autofix/remove-consoles.js +0 -209
- package/bin/runners/lib/polish/autofix/strengthen-types.js +0 -245
- package/bin/runners/lib/polish/backend-checks.js +0 -148
- package/bin/runners/lib/polish/documentation-checks.js +0 -111
- package/bin/runners/lib/polish/frontend-checks.js +0 -168
- package/bin/runners/lib/polish/index.js +0 -71
- package/bin/runners/lib/polish/infrastructure-checks.js +0 -131
- package/bin/runners/lib/polish/library-detection.js +0 -175
- package/bin/runners/lib/polish/performance-checks.js +0 -100
- package/bin/runners/lib/polish/security-checks.js +0 -148
- package/bin/runners/lib/polish/utils.js +0 -203
- package/bin/runners/lib/prompt-builder.js +0 -540
- package/bin/runners/lib/proof-certificate.js +0 -634
- package/bin/runners/lib/reality/accessibility-audit.js +0 -946
- package/bin/runners/lib/reality/api-contract-validator.js +0 -1012
- package/bin/runners/lib/reality/chaos-engineering.js +0 -1084
- package/bin/runners/lib/reality/performance-tracker.js +0 -1077
- package/bin/runners/lib/reality/scenario-generator.js +0 -1404
- package/bin/runners/lib/reality/visual-regression.js +0 -852
- package/bin/runners/lib/reality-profiler.js +0 -717
- package/bin/runners/lib/replay/flight-recorder-viewer.js +0 -1160
- package/bin/runners/lib/review/ai-code-review.js +0 -832
- package/bin/runners/lib/rules/custom-rule-engine.js +0 -985
- package/bin/runners/lib/sbom-generator.js +0 -641
- package/bin/runners/lib/scan-output-enhanced.js +0 -512
- package/bin/runners/lib/security/owasp-scanner.js +0 -939
- package/bin/runners/lib/validators/contract-validator.js +0 -283
- package/bin/runners/lib/validators/dead-export-detector.js +0 -279
- package/bin/runners/lib/validators/dep-audit.js +0 -245
- package/bin/runners/lib/validators/env-validator.js +0 -319
- package/bin/runners/lib/validators/index.js +0 -120
- package/bin/runners/lib/validators/license-checker.js +0 -252
- package/bin/runners/lib/validators/route-validator.js +0 -290
- package/bin/runners/runAuthority.js +0 -528
- package/bin/runners/runConductor.js +0 -772
- package/bin/runners/runContainer.js +0 -366
- package/bin/runners/runEasy.js +0 -410
- package/bin/runners/runIaC.js +0 -372
- package/bin/runners/runVibe.js +0 -791
- package/mcp-server/tools.js +0 -495
|
@@ -1,607 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Framework Adapters - Plugin Architecture for Framework-Specific Detection
|
|
3
|
-
*
|
|
4
|
-
* This module provides a plugin system for framework-specific rules and patterns.
|
|
5
|
-
* Each adapter can:
|
|
6
|
-
*
|
|
7
|
-
* 1. Define framework-specific detection patterns
|
|
8
|
-
* 2. Override or suppress findings based on framework conventions
|
|
9
|
-
* 3. Add framework-specific checks
|
|
10
|
-
* 4. Provide framework-specific fix suggestions
|
|
11
|
-
*
|
|
12
|
-
* To add a new framework:
|
|
13
|
-
* 1. Create a new adapter file in this directory (e.g., remix-adapter.js)
|
|
14
|
-
* 2. Export it from this index.js
|
|
15
|
-
* 3. The adapter will be auto-loaded based on detected framework
|
|
16
|
-
*/
|
|
17
|
-
|
|
18
|
-
"use strict";
|
|
19
|
-
|
|
20
|
-
const fs = require("fs");
|
|
21
|
-
const path = require("path");
|
|
22
|
-
|
|
23
|
-
// ═══════════════════════════════════════════════════════════════════════════════
|
|
24
|
-
// ADAPTER INTERFACE
|
|
25
|
-
// ═══════════════════════════════════════════════════════════════════════════════
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Base adapter interface - all adapters should implement these methods
|
|
29
|
-
*/
|
|
30
|
-
const AdapterInterface = {
|
|
31
|
-
// Metadata
|
|
32
|
-
name: "unknown",
|
|
33
|
-
version: "1.0.0",
|
|
34
|
-
frameworks: [], // e.g., ["nextjs", "next"]
|
|
35
|
-
|
|
36
|
-
// Detection
|
|
37
|
-
detect: (projectPath) => false,
|
|
38
|
-
getVersion: (projectPath) => null,
|
|
39
|
-
|
|
40
|
-
// Finding modification
|
|
41
|
-
shouldSuppressFinding: (finding, context) => ({ suppress: false, reason: null }),
|
|
42
|
-
modifyFinding: (finding, context) => finding,
|
|
43
|
-
|
|
44
|
-
// Additional checks
|
|
45
|
-
getAdditionalChecks: () => [],
|
|
46
|
-
|
|
47
|
-
// Fix suggestions
|
|
48
|
-
getFixSuggestion: (finding, context) => null,
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
// ═══════════════════════════════════════════════════════════════════════════════
|
|
52
|
-
// BUILT-IN ADAPTERS
|
|
53
|
-
// ═══════════════════════════════════════════════════════════════════════════════
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Next.js Adapter - Handles Next.js specific patterns
|
|
57
|
-
*/
|
|
58
|
-
const NextjsAdapter = {
|
|
59
|
-
name: "nextjs",
|
|
60
|
-
version: "1.0.0",
|
|
61
|
-
frameworks: ["nextjs", "next"],
|
|
62
|
-
|
|
63
|
-
detect(projectPath) {
|
|
64
|
-
try {
|
|
65
|
-
const pkgPath = path.join(projectPath, "package.json");
|
|
66
|
-
if (fs.existsSync(pkgPath)) {
|
|
67
|
-
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
|
|
68
|
-
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
69
|
-
return !!deps.next;
|
|
70
|
-
}
|
|
71
|
-
} catch {}
|
|
72
|
-
return false;
|
|
73
|
-
},
|
|
74
|
-
|
|
75
|
-
getVersion(projectPath) {
|
|
76
|
-
try {
|
|
77
|
-
const pkgPath = path.join(projectPath, "package.json");
|
|
78
|
-
if (fs.existsSync(pkgPath)) {
|
|
79
|
-
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
|
|
80
|
-
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
81
|
-
const version = deps.next;
|
|
82
|
-
if (version) {
|
|
83
|
-
const match = version.match(/(\d+)\.(\d+)/);
|
|
84
|
-
if (match) {
|
|
85
|
-
return { major: parseInt(match[1]), minor: parseInt(match[2]), raw: version };
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
} catch {}
|
|
90
|
-
return null;
|
|
91
|
-
},
|
|
92
|
-
|
|
93
|
-
shouldSuppressFinding(finding, context) {
|
|
94
|
-
const { filePath, version } = context;
|
|
95
|
-
|
|
96
|
-
// Next.js 13+ App Router: async components are valid
|
|
97
|
-
if (version?.major >= 13) {
|
|
98
|
-
if (finding.type === "async-without-await" && /\/app\//.test(filePath)) {
|
|
99
|
-
// Server components can be async without await
|
|
100
|
-
return { suppress: true, reason: "Async server components are valid in Next.js 13+ App Router" };
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
if (finding.type === "top-level-await" && /\/app\//.test(filePath)) {
|
|
104
|
-
return { suppress: true, reason: "Top-level await is valid in Next.js 13+ server components" };
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
// Next.js API routes: exports are expected patterns
|
|
109
|
-
if (/\/api\//.test(filePath) || /\/app\/api\//.test(filePath)) {
|
|
110
|
-
if (finding.type === "unused-export" && /^(GET|POST|PUT|DELETE|PATCH|HEAD|OPTIONS)$/.test(finding.name)) {
|
|
111
|
-
return { suppress: true, reason: "HTTP method exports are standard Next.js API route patterns" };
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
// Next.js page exports
|
|
116
|
-
if (/\/pages\//.test(filePath) || /\/app\/.*page\.(tsx?|jsx?)$/.test(filePath)) {
|
|
117
|
-
if (finding.type === "unused-export" && /^(getStaticProps|getServerSideProps|getStaticPaths|generateStaticParams|generateMetadata)$/.test(finding.name)) {
|
|
118
|
-
return { suppress: true, reason: "Next.js data fetching exports are framework conventions" };
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
// Server-only imports
|
|
123
|
-
if (finding.type === "import-server-only" && version?.major >= 13) {
|
|
124
|
-
return { suppress: true, reason: "server-only imports are valid in Next.js 13+ for server components" };
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
return { suppress: false, reason: null };
|
|
128
|
-
},
|
|
129
|
-
|
|
130
|
-
modifyFinding(finding, context) {
|
|
131
|
-
// Add Next.js specific context to findings
|
|
132
|
-
if (context.version?.major >= 13 && /\/app\//.test(context.filePath)) {
|
|
133
|
-
finding.frameworkContext = "Next.js App Router";
|
|
134
|
-
} else if (/\/pages\//.test(context.filePath)) {
|
|
135
|
-
finding.frameworkContext = "Next.js Pages Router";
|
|
136
|
-
}
|
|
137
|
-
return finding;
|
|
138
|
-
},
|
|
139
|
-
|
|
140
|
-
getAdditionalChecks() {
|
|
141
|
-
return [
|
|
142
|
-
{
|
|
143
|
-
id: "nextjs-missing-metadata",
|
|
144
|
-
name: "Missing Metadata",
|
|
145
|
-
description: "Page is missing metadata export for SEO",
|
|
146
|
-
severity: "WARN",
|
|
147
|
-
check: (code, filePath) => {
|
|
148
|
-
if (!/\/app\/.*page\.(tsx?|jsx?)$/.test(filePath)) return null;
|
|
149
|
-
if (/export\s+(const|async function)\s+generateMetadata/.test(code)) return null;
|
|
150
|
-
if (/export\s+const\s+metadata/.test(code)) return null;
|
|
151
|
-
|
|
152
|
-
return {
|
|
153
|
-
type: "nextjs-missing-metadata",
|
|
154
|
-
message: "Page is missing metadata export - important for SEO",
|
|
155
|
-
severity: "WARN",
|
|
156
|
-
confidence: 0.7,
|
|
157
|
-
fixHint: "Add `export const metadata = { title: 'Page Title' }` or `export async function generateMetadata()`",
|
|
158
|
-
};
|
|
159
|
-
},
|
|
160
|
-
},
|
|
161
|
-
{
|
|
162
|
-
id: "nextjs-client-only-in-server",
|
|
163
|
-
name: "Client-Only in Server Component",
|
|
164
|
-
description: "Using client-only features in server component",
|
|
165
|
-
severity: "BLOCK",
|
|
166
|
-
check: (code, filePath) => {
|
|
167
|
-
if (!/\/app\//.test(filePath)) return null;
|
|
168
|
-
if (/^['"]use client['"]/.test(code)) return null; // Client component
|
|
169
|
-
|
|
170
|
-
const clientOnlyPatterns = [
|
|
171
|
-
/\buseState\s*\(/,
|
|
172
|
-
/\buseEffect\s*\(/,
|
|
173
|
-
/\buseRef\s*\(/,
|
|
174
|
-
/\bwindow\b/,
|
|
175
|
-
/\bdocument\b/,
|
|
176
|
-
/\blocalStorage\b/,
|
|
177
|
-
/\bsessionStorage\b/,
|
|
178
|
-
];
|
|
179
|
-
|
|
180
|
-
for (const pattern of clientOnlyPatterns) {
|
|
181
|
-
if (pattern.test(code)) {
|
|
182
|
-
return {
|
|
183
|
-
type: "nextjs-client-only-in-server",
|
|
184
|
-
message: `Using client-only feature in server component: ${pattern.source}`,
|
|
185
|
-
severity: "BLOCK",
|
|
186
|
-
confidence: 0.85,
|
|
187
|
-
fixHint: "Add 'use client' directive at the top of the file, or move client-only code to a client component",
|
|
188
|
-
};
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
return null;
|
|
193
|
-
},
|
|
194
|
-
},
|
|
195
|
-
];
|
|
196
|
-
},
|
|
197
|
-
|
|
198
|
-
getFixSuggestion(finding, context) {
|
|
199
|
-
if (finding.type === "nextjs-missing-metadata") {
|
|
200
|
-
return {
|
|
201
|
-
description: "Add metadata export",
|
|
202
|
-
code: `export const metadata = {
|
|
203
|
-
title: 'Page Title',
|
|
204
|
-
description: 'Page description',
|
|
205
|
-
};`,
|
|
206
|
-
confidence: 0.9,
|
|
207
|
-
autoApplicable: true,
|
|
208
|
-
};
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
if (finding.type === "nextjs-client-only-in-server") {
|
|
212
|
-
return {
|
|
213
|
-
description: "Add 'use client' directive",
|
|
214
|
-
code: `'use client';\n\n`,
|
|
215
|
-
insertAt: "top",
|
|
216
|
-
confidence: 0.8,
|
|
217
|
-
autoApplicable: true,
|
|
218
|
-
};
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
return null;
|
|
222
|
-
},
|
|
223
|
-
};
|
|
224
|
-
|
|
225
|
-
/**
|
|
226
|
-
* React Adapter - Handles React-specific patterns
|
|
227
|
-
*/
|
|
228
|
-
const ReactAdapter = {
|
|
229
|
-
name: "react",
|
|
230
|
-
version: "1.0.0",
|
|
231
|
-
frameworks: ["react"],
|
|
232
|
-
|
|
233
|
-
detect(projectPath) {
|
|
234
|
-
try {
|
|
235
|
-
const pkgPath = path.join(projectPath, "package.json");
|
|
236
|
-
if (fs.existsSync(pkgPath)) {
|
|
237
|
-
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
|
|
238
|
-
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
239
|
-
return !!deps.react;
|
|
240
|
-
}
|
|
241
|
-
} catch {}
|
|
242
|
-
return false;
|
|
243
|
-
},
|
|
244
|
-
|
|
245
|
-
getVersion(projectPath) {
|
|
246
|
-
try {
|
|
247
|
-
const pkgPath = path.join(projectPath, "package.json");
|
|
248
|
-
if (fs.existsSync(pkgPath)) {
|
|
249
|
-
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
|
|
250
|
-
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
251
|
-
const version = deps.react;
|
|
252
|
-
if (version) {
|
|
253
|
-
const match = version.match(/(\d+)\.(\d+)/);
|
|
254
|
-
if (match) {
|
|
255
|
-
return { major: parseInt(match[1]), minor: parseInt(match[2]), raw: version };
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
} catch {}
|
|
260
|
-
return null;
|
|
261
|
-
},
|
|
262
|
-
|
|
263
|
-
shouldSuppressFinding(finding, context) {
|
|
264
|
-
const { version } = context;
|
|
265
|
-
|
|
266
|
-
// React 19+: use() hook is valid
|
|
267
|
-
if (version?.major >= 19) {
|
|
268
|
-
if (finding.type === "unknown-hook" && finding.message?.includes("use(")) {
|
|
269
|
-
return { suppress: true, reason: "use() hook is valid in React 19+" };
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
// React 18+: concurrent features are valid
|
|
274
|
-
if (version?.major >= 18) {
|
|
275
|
-
if (finding.type === "unknown-hook" && /use(Transition|DeferredValue|Id)/.test(finding.message)) {
|
|
276
|
-
return { suppress: true, reason: `${finding.message} is valid in React 18+` };
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
return { suppress: false, reason: null };
|
|
281
|
-
},
|
|
282
|
-
|
|
283
|
-
modifyFinding(finding, context) {
|
|
284
|
-
return finding;
|
|
285
|
-
},
|
|
286
|
-
|
|
287
|
-
getAdditionalChecks() {
|
|
288
|
-
return [];
|
|
289
|
-
},
|
|
290
|
-
|
|
291
|
-
getFixSuggestion(finding, context) {
|
|
292
|
-
return null;
|
|
293
|
-
},
|
|
294
|
-
};
|
|
295
|
-
|
|
296
|
-
/**
|
|
297
|
-
* Express Adapter - Handles Express.js patterns
|
|
298
|
-
*/
|
|
299
|
-
const ExpressAdapter = {
|
|
300
|
-
name: "express",
|
|
301
|
-
version: "1.0.0",
|
|
302
|
-
frameworks: ["express"],
|
|
303
|
-
|
|
304
|
-
detect(projectPath) {
|
|
305
|
-
try {
|
|
306
|
-
const pkgPath = path.join(projectPath, "package.json");
|
|
307
|
-
if (fs.existsSync(pkgPath)) {
|
|
308
|
-
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
|
|
309
|
-
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
310
|
-
return !!deps.express;
|
|
311
|
-
}
|
|
312
|
-
} catch {}
|
|
313
|
-
return false;
|
|
314
|
-
},
|
|
315
|
-
|
|
316
|
-
getVersion(projectPath) {
|
|
317
|
-
try {
|
|
318
|
-
const pkgPath = path.join(projectPath, "package.json");
|
|
319
|
-
if (fs.existsSync(pkgPath)) {
|
|
320
|
-
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
|
|
321
|
-
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
322
|
-
const version = deps.express;
|
|
323
|
-
if (version) {
|
|
324
|
-
const match = version.match(/(\d+)\.(\d+)/);
|
|
325
|
-
if (match) {
|
|
326
|
-
return { major: parseInt(match[1]), minor: parseInt(match[2]), raw: version };
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
} catch {}
|
|
331
|
-
return null;
|
|
332
|
-
},
|
|
333
|
-
|
|
334
|
-
shouldSuppressFinding(finding, context) {
|
|
335
|
-
const { filePath } = context;
|
|
336
|
-
|
|
337
|
-
// Express middleware pattern: (req, res, next) with unused next
|
|
338
|
-
if (finding.type === "unused-parameter" && finding.name === "next") {
|
|
339
|
-
if (/middleware|handler|route/i.test(filePath)) {
|
|
340
|
-
return { suppress: true, reason: "Express middleware signature requires next parameter even if unused" };
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
// Error handling middleware: (err, req, res, next)
|
|
345
|
-
if (finding.type === "unused-parameter" && /^(err|error)$/.test(finding.name)) {
|
|
346
|
-
return { suppress: true, reason: "Express error middleware requires error parameter even if unused" };
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
return { suppress: false, reason: null };
|
|
350
|
-
},
|
|
351
|
-
|
|
352
|
-
modifyFinding(finding, context) {
|
|
353
|
-
return finding;
|
|
354
|
-
},
|
|
355
|
-
|
|
356
|
-
getAdditionalChecks() {
|
|
357
|
-
return [
|
|
358
|
-
{
|
|
359
|
-
id: "express-missing-error-handler",
|
|
360
|
-
name: "Missing Error Handler",
|
|
361
|
-
description: "Express app has no error handling middleware",
|
|
362
|
-
severity: "WARN",
|
|
363
|
-
check: (code, filePath) => {
|
|
364
|
-
if (!/\bapp\s*=\s*express\(\)/.test(code)) return null;
|
|
365
|
-
if (/app\.use\([^,]+,\s*[^,]+,\s*[^,]+,\s*[^)]+\)/.test(code)) return null; // 4-param middleware
|
|
366
|
-
|
|
367
|
-
return {
|
|
368
|
-
type: "express-missing-error-handler",
|
|
369
|
-
message: "Express app may be missing error handling middleware",
|
|
370
|
-
severity: "WARN",
|
|
371
|
-
confidence: 0.6,
|
|
372
|
-
fixHint: "Add error handling middleware: app.use((err, req, res, next) => { ... })",
|
|
373
|
-
};
|
|
374
|
-
},
|
|
375
|
-
},
|
|
376
|
-
];
|
|
377
|
-
},
|
|
378
|
-
|
|
379
|
-
getFixSuggestion(finding, context) {
|
|
380
|
-
return null;
|
|
381
|
-
},
|
|
382
|
-
};
|
|
383
|
-
|
|
384
|
-
/**
|
|
385
|
-
* Prisma Adapter - Handles Prisma ORM patterns
|
|
386
|
-
*/
|
|
387
|
-
const PrismaAdapter = {
|
|
388
|
-
name: "prisma",
|
|
389
|
-
version: "1.0.0",
|
|
390
|
-
frameworks: ["prisma"],
|
|
391
|
-
|
|
392
|
-
detect(projectPath) {
|
|
393
|
-
try {
|
|
394
|
-
const pkgPath = path.join(projectPath, "package.json");
|
|
395
|
-
if (fs.existsSync(pkgPath)) {
|
|
396
|
-
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
|
|
397
|
-
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
398
|
-
return !!deps["@prisma/client"] || !!deps.prisma;
|
|
399
|
-
}
|
|
400
|
-
} catch {}
|
|
401
|
-
return false;
|
|
402
|
-
},
|
|
403
|
-
|
|
404
|
-
getVersion(projectPath) {
|
|
405
|
-
try {
|
|
406
|
-
const pkgPath = path.join(projectPath, "package.json");
|
|
407
|
-
if (fs.existsSync(pkgPath)) {
|
|
408
|
-
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
|
|
409
|
-
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
410
|
-
const version = deps["@prisma/client"] || deps.prisma;
|
|
411
|
-
if (version) {
|
|
412
|
-
const match = version.match(/(\d+)\.(\d+)/);
|
|
413
|
-
if (match) {
|
|
414
|
-
return { major: parseInt(match[1]), minor: parseInt(match[2]), raw: version };
|
|
415
|
-
}
|
|
416
|
-
}
|
|
417
|
-
}
|
|
418
|
-
} catch {}
|
|
419
|
-
return null;
|
|
420
|
-
},
|
|
421
|
-
|
|
422
|
-
shouldSuppressFinding(finding, context) {
|
|
423
|
-
// Prisma queries are parameterized, skip SQL injection warnings
|
|
424
|
-
if (finding.type === "sql-injection" || finding.type === "raw-sql") {
|
|
425
|
-
if (/prisma\.\$queryRaw|prisma\.\$executeRaw/.test(context.code || "")) {
|
|
426
|
-
return { suppress: true, reason: "Prisma parameterized queries are safe from SQL injection" };
|
|
427
|
-
}
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
// Prisma client is generated, skip generated code warnings
|
|
431
|
-
if (finding.filePath?.includes("@prisma/client") || finding.filePath?.includes("generated/prisma")) {
|
|
432
|
-
return { suppress: true, reason: "Prisma client is generated code" };
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
return { suppress: false, reason: null };
|
|
436
|
-
},
|
|
437
|
-
|
|
438
|
-
modifyFinding(finding, context) {
|
|
439
|
-
return finding;
|
|
440
|
-
},
|
|
441
|
-
|
|
442
|
-
getAdditionalChecks() {
|
|
443
|
-
return [];
|
|
444
|
-
},
|
|
445
|
-
|
|
446
|
-
getFixSuggestion(finding, context) {
|
|
447
|
-
return null;
|
|
448
|
-
},
|
|
449
|
-
};
|
|
450
|
-
|
|
451
|
-
// ═══════════════════════════════════════════════════════════════════════════════
|
|
452
|
-
// ADAPTER REGISTRY
|
|
453
|
-
// ═══════════════════════════════════════════════════════════════════════════════
|
|
454
|
-
|
|
455
|
-
const BUILT_IN_ADAPTERS = [
|
|
456
|
-
NextjsAdapter,
|
|
457
|
-
ReactAdapter,
|
|
458
|
-
ExpressAdapter,
|
|
459
|
-
PrismaAdapter,
|
|
460
|
-
];
|
|
461
|
-
|
|
462
|
-
// Custom adapters can be registered here
|
|
463
|
-
const customAdapters = [];
|
|
464
|
-
|
|
465
|
-
/**
|
|
466
|
-
* Register a custom adapter
|
|
467
|
-
*/
|
|
468
|
-
function registerAdapter(adapter) {
|
|
469
|
-
// Validate adapter interface
|
|
470
|
-
const required = ["name", "frameworks", "detect"];
|
|
471
|
-
for (const field of required) {
|
|
472
|
-
if (!adapter[field]) {
|
|
473
|
-
throw new Error(`Adapter missing required field: ${field}`);
|
|
474
|
-
}
|
|
475
|
-
}
|
|
476
|
-
customAdapters.push(adapter);
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
/**
|
|
480
|
-
* Get all adapters (built-in + custom)
|
|
481
|
-
*/
|
|
482
|
-
function getAllAdapters() {
|
|
483
|
-
return [...BUILT_IN_ADAPTERS, ...customAdapters];
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
/**
|
|
487
|
-
* Detect which adapters are applicable for a project
|
|
488
|
-
*/
|
|
489
|
-
function detectApplicableAdapters(projectPath) {
|
|
490
|
-
const applicable = [];
|
|
491
|
-
|
|
492
|
-
for (const adapter of getAllAdapters()) {
|
|
493
|
-
try {
|
|
494
|
-
if (adapter.detect(projectPath)) {
|
|
495
|
-
const version = adapter.getVersion?.(projectPath) || null;
|
|
496
|
-
applicable.push({ adapter, version });
|
|
497
|
-
}
|
|
498
|
-
} catch {
|
|
499
|
-
// Skip adapters that fail detection
|
|
500
|
-
}
|
|
501
|
-
}
|
|
502
|
-
|
|
503
|
-
return applicable;
|
|
504
|
-
}
|
|
505
|
-
|
|
506
|
-
/**
|
|
507
|
-
* Apply all applicable adapters to a finding
|
|
508
|
-
*/
|
|
509
|
-
function applyAdapters(finding, context = {}) {
|
|
510
|
-
const { projectPath = ".", filePath = "", code = "" } = context;
|
|
511
|
-
|
|
512
|
-
// Get applicable adapters
|
|
513
|
-
const applicable = detectApplicableAdapters(projectPath);
|
|
514
|
-
|
|
515
|
-
let modifiedFinding = { ...finding };
|
|
516
|
-
|
|
517
|
-
for (const { adapter, version } of applicable) {
|
|
518
|
-
const ctx = { ...context, version, filePath, code };
|
|
519
|
-
|
|
520
|
-
// Check if finding should be suppressed
|
|
521
|
-
const suppressResult = adapter.shouldSuppressFinding?.(modifiedFinding, ctx);
|
|
522
|
-
if (suppressResult?.suppress) {
|
|
523
|
-
return {
|
|
524
|
-
finding: null,
|
|
525
|
-
suppressed: true,
|
|
526
|
-
reason: suppressResult.reason,
|
|
527
|
-
adapter: adapter.name,
|
|
528
|
-
};
|
|
529
|
-
}
|
|
530
|
-
|
|
531
|
-
// Modify finding
|
|
532
|
-
modifiedFinding = adapter.modifyFinding?.(modifiedFinding, ctx) || modifiedFinding;
|
|
533
|
-
}
|
|
534
|
-
|
|
535
|
-
return {
|
|
536
|
-
finding: modifiedFinding,
|
|
537
|
-
suppressed: false,
|
|
538
|
-
reason: null,
|
|
539
|
-
adapter: null,
|
|
540
|
-
};
|
|
541
|
-
}
|
|
542
|
-
|
|
543
|
-
/**
|
|
544
|
-
* Get all additional checks from applicable adapters
|
|
545
|
-
*/
|
|
546
|
-
function getAdditionalChecks(projectPath) {
|
|
547
|
-
const checks = [];
|
|
548
|
-
const applicable = detectApplicableAdapters(projectPath);
|
|
549
|
-
|
|
550
|
-
for (const { adapter } of applicable) {
|
|
551
|
-
const adapterChecks = adapter.getAdditionalChecks?.() || [];
|
|
552
|
-
for (const check of adapterChecks) {
|
|
553
|
-
checks.push({
|
|
554
|
-
...check,
|
|
555
|
-
adapter: adapter.name,
|
|
556
|
-
});
|
|
557
|
-
}
|
|
558
|
-
}
|
|
559
|
-
|
|
560
|
-
return checks;
|
|
561
|
-
}
|
|
562
|
-
|
|
563
|
-
/**
|
|
564
|
-
* Get fix suggestion from applicable adapters
|
|
565
|
-
*/
|
|
566
|
-
function getFixSuggestion(finding, context = {}) {
|
|
567
|
-
const { projectPath = "." } = context;
|
|
568
|
-
const applicable = detectApplicableAdapters(projectPath);
|
|
569
|
-
|
|
570
|
-
for (const { adapter, version } of applicable) {
|
|
571
|
-
const ctx = { ...context, version };
|
|
572
|
-
const suggestion = adapter.getFixSuggestion?.(finding, ctx);
|
|
573
|
-
if (suggestion) {
|
|
574
|
-
return {
|
|
575
|
-
...suggestion,
|
|
576
|
-
adapter: adapter.name,
|
|
577
|
-
};
|
|
578
|
-
}
|
|
579
|
-
}
|
|
580
|
-
|
|
581
|
-
return null;
|
|
582
|
-
}
|
|
583
|
-
|
|
584
|
-
// ═══════════════════════════════════════════════════════════════════════════════
|
|
585
|
-
// EXPORTS
|
|
586
|
-
// ═══════════════════════════════════════════════════════════════════════════════
|
|
587
|
-
|
|
588
|
-
module.exports = {
|
|
589
|
-
// Adapter management
|
|
590
|
-
registerAdapter,
|
|
591
|
-
getAllAdapters,
|
|
592
|
-
detectApplicableAdapters,
|
|
593
|
-
|
|
594
|
-
// Finding processing
|
|
595
|
-
applyAdapters,
|
|
596
|
-
getAdditionalChecks,
|
|
597
|
-
getFixSuggestion,
|
|
598
|
-
|
|
599
|
-
// Built-in adapters (for direct use or customization)
|
|
600
|
-
NextjsAdapter,
|
|
601
|
-
ReactAdapter,
|
|
602
|
-
ExpressAdapter,
|
|
603
|
-
PrismaAdapter,
|
|
604
|
-
|
|
605
|
-
// Interface (for reference)
|
|
606
|
-
AdapterInterface,
|
|
607
|
-
};
|