@quantracode/vibecheck 0.0.1
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/LICENSE +21 -0
- package/README.md +839 -0
- package/dist/__tests__/cli.test.d.ts +2 -0
- package/dist/__tests__/cli.test.d.ts.map +1 -0
- package/dist/__tests__/cli.test.js +243 -0
- package/dist/__tests__/fixtures/safe-app/app/api/users/route.js +36 -0
- package/dist/__tests__/fixtures/vulnerable-app/app/api/users/route.js +28 -0
- package/dist/__tests__/fixtures/vulnerable-app/lib/config.d.ts +4 -0
- package/dist/__tests__/fixtures/vulnerable-app/lib/config.d.ts.map +1 -0
- package/dist/__tests__/fixtures/vulnerable-app/lib/config.js +6 -0
- package/dist/__tests__/scanners/env-config.test.d.ts +2 -0
- package/dist/__tests__/scanners/env-config.test.d.ts.map +1 -0
- package/dist/__tests__/scanners/env-config.test.js +142 -0
- package/dist/__tests__/scanners/nextjs-middleware.test.d.ts +2 -0
- package/dist/__tests__/scanners/nextjs-middleware.test.d.ts.map +1 -0
- package/dist/__tests__/scanners/nextjs-middleware.test.js +193 -0
- package/dist/__tests__/scanners/scanner-packs.test.d.ts +2 -0
- package/dist/__tests__/scanners/scanner-packs.test.d.ts.map +1 -0
- package/dist/__tests__/scanners/scanner-packs.test.js +126 -0
- package/dist/__tests__/scanners/unused-security-imports.test.d.ts +2 -0
- package/dist/__tests__/scanners/unused-security-imports.test.d.ts.map +1 -0
- package/dist/__tests__/scanners/unused-security-imports.test.js +145 -0
- package/dist/commands/demo-artifact.d.ts +7 -0
- package/dist/commands/demo-artifact.d.ts.map +1 -0
- package/dist/commands/demo-artifact.js +322 -0
- package/dist/commands/evaluate.d.ts +30 -0
- package/dist/commands/evaluate.d.ts.map +1 -0
- package/dist/commands/evaluate.js +258 -0
- package/dist/commands/explain.d.ts +12 -0
- package/dist/commands/explain.d.ts.map +1 -0
- package/dist/commands/explain.js +214 -0
- package/dist/commands/index.d.ts +7 -0
- package/dist/commands/index.d.ts.map +1 -0
- package/dist/commands/index.js +6 -0
- package/dist/commands/intent.d.ts +21 -0
- package/dist/commands/intent.d.ts.map +1 -0
- package/dist/commands/intent.js +192 -0
- package/dist/commands/scan.d.ts +44 -0
- package/dist/commands/scan.d.ts.map +1 -0
- package/dist/commands/scan.js +497 -0
- package/dist/commands/waivers.d.ts +30 -0
- package/dist/commands/waivers.d.ts.map +1 -0
- package/dist/commands/waivers.js +249 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +17 -0
- package/dist/phase3/index.d.ts +11 -0
- package/dist/phase3/index.d.ts.map +1 -0
- package/dist/phase3/index.js +12 -0
- package/dist/phase3/intent-miner.d.ts +32 -0
- package/dist/phase3/intent-miner.d.ts.map +1 -0
- package/dist/phase3/intent-miner.js +323 -0
- package/dist/phase3/proof-trace-builder.d.ts +42 -0
- package/dist/phase3/proof-trace-builder.d.ts.map +1 -0
- package/dist/phase3/proof-trace-builder.js +441 -0
- package/dist/phase3/scanners/auth-by-ui-server-gap.d.ts +15 -0
- package/dist/phase3/scanners/auth-by-ui-server-gap.d.ts.map +1 -0
- package/dist/phase3/scanners/auth-by-ui-server-gap.js +237 -0
- package/dist/phase3/scanners/comment-claim-unproven.d.ts +14 -0
- package/dist/phase3/scanners/comment-claim-unproven.d.ts.map +1 -0
- package/dist/phase3/scanners/comment-claim-unproven.js +161 -0
- package/dist/phase3/scanners/index.d.ts +31 -0
- package/dist/phase3/scanners/index.d.ts.map +1 -0
- package/dist/phase3/scanners/index.js +40 -0
- package/dist/phase3/scanners/middleware-assumed-not-matching.d.ts +14 -0
- package/dist/phase3/scanners/middleware-assumed-not-matching.d.ts.map +1 -0
- package/dist/phase3/scanners/middleware-assumed-not-matching.js +172 -0
- package/dist/phase3/scanners/validation-claimed-missing.d.ts +15 -0
- package/dist/phase3/scanners/validation-claimed-missing.d.ts.map +1 -0
- package/dist/phase3/scanners/validation-claimed-missing.js +204 -0
- package/dist/scanners/abuse/compute-abuse.d.ts +20 -0
- package/dist/scanners/abuse/compute-abuse.d.ts.map +1 -0
- package/dist/scanners/abuse/compute-abuse.js +509 -0
- package/dist/scanners/abuse/index.d.ts +12 -0
- package/dist/scanners/abuse/index.d.ts.map +1 -0
- package/dist/scanners/abuse/index.js +15 -0
- package/dist/scanners/auth/index.d.ts +5 -0
- package/dist/scanners/auth/index.d.ts.map +1 -0
- package/dist/scanners/auth/index.js +10 -0
- package/dist/scanners/auth/middleware-gap.d.ts +22 -0
- package/dist/scanners/auth/middleware-gap.d.ts.map +1 -0
- package/dist/scanners/auth/middleware-gap.js +203 -0
- package/dist/scanners/auth/unprotected-api-route.d.ts +12 -0
- package/dist/scanners/auth/unprotected-api-route.d.ts.map +1 -0
- package/dist/scanners/auth/unprotected-api-route.js +126 -0
- package/dist/scanners/config/index.d.ts +5 -0
- package/dist/scanners/config/index.d.ts.map +1 -0
- package/dist/scanners/config/index.js +10 -0
- package/dist/scanners/config/insecure-defaults.d.ts +12 -0
- package/dist/scanners/config/insecure-defaults.d.ts.map +1 -0
- package/dist/scanners/config/insecure-defaults.js +77 -0
- package/dist/scanners/config/undocumented-env.d.ts +24 -0
- package/dist/scanners/config/undocumented-env.d.ts.map +1 -0
- package/dist/scanners/config/undocumented-env.js +159 -0
- package/dist/scanners/crypto/index.d.ts +6 -0
- package/dist/scanners/crypto/index.d.ts.map +1 -0
- package/dist/scanners/crypto/index.js +11 -0
- package/dist/scanners/crypto/jwt-decode-unverified.d.ts +14 -0
- package/dist/scanners/crypto/jwt-decode-unverified.d.ts.map +1 -0
- package/dist/scanners/crypto/jwt-decode-unverified.js +87 -0
- package/dist/scanners/crypto/math-random-tokens.d.ts +13 -0
- package/dist/scanners/crypto/math-random-tokens.d.ts.map +1 -0
- package/dist/scanners/crypto/math-random-tokens.js +80 -0
- package/dist/scanners/crypto/weak-hashing.d.ts +11 -0
- package/dist/scanners/crypto/weak-hashing.d.ts.map +1 -0
- package/dist/scanners/crypto/weak-hashing.js +95 -0
- package/dist/scanners/env-config.d.ts +24 -0
- package/dist/scanners/env-config.d.ts.map +1 -0
- package/dist/scanners/env-config.js +164 -0
- package/dist/scanners/hallucinations/index.d.ts +4 -0
- package/dist/scanners/hallucinations/index.d.ts.map +1 -0
- package/dist/scanners/hallucinations/index.js +8 -0
- package/dist/scanners/hallucinations/unused-security-imports.d.ts +36 -0
- package/dist/scanners/hallucinations/unused-security-imports.d.ts.map +1 -0
- package/dist/scanners/hallucinations/unused-security-imports.js +309 -0
- package/dist/scanners/helpers/ast-helpers.d.ts +6 -0
- package/dist/scanners/helpers/ast-helpers.d.ts.map +1 -0
- package/dist/scanners/helpers/ast-helpers.js +945 -0
- package/dist/scanners/helpers/context-builder.d.ts +17 -0
- package/dist/scanners/helpers/context-builder.d.ts.map +1 -0
- package/dist/scanners/helpers/context-builder.js +148 -0
- package/dist/scanners/helpers/index.d.ts +3 -0
- package/dist/scanners/helpers/index.d.ts.map +1 -0
- package/dist/scanners/helpers/index.js +2 -0
- package/dist/scanners/index.d.ts +30 -0
- package/dist/scanners/index.d.ts.map +1 -0
- package/dist/scanners/index.js +102 -0
- package/dist/scanners/middleware/index.d.ts +4 -0
- package/dist/scanners/middleware/index.d.ts.map +1 -0
- package/dist/scanners/middleware/index.js +7 -0
- package/dist/scanners/middleware/missing-rate-limit.d.ts +13 -0
- package/dist/scanners/middleware/missing-rate-limit.d.ts.map +1 -0
- package/dist/scanners/middleware/missing-rate-limit.js +140 -0
- package/dist/scanners/network/cors-misconfiguration.d.ts +14 -0
- package/dist/scanners/network/cors-misconfiguration.d.ts.map +1 -0
- package/dist/scanners/network/cors-misconfiguration.js +89 -0
- package/dist/scanners/network/index.d.ts +7 -0
- package/dist/scanners/network/index.d.ts.map +1 -0
- package/dist/scanners/network/index.js +18 -0
- package/dist/scanners/network/missing-timeout.d.ts +15 -0
- package/dist/scanners/network/missing-timeout.d.ts.map +1 -0
- package/dist/scanners/network/missing-timeout.js +93 -0
- package/dist/scanners/network/open-redirect.d.ts +15 -0
- package/dist/scanners/network/open-redirect.d.ts.map +1 -0
- package/dist/scanners/network/open-redirect.js +88 -0
- package/dist/scanners/network/ssrf-prone-fetch.d.ts +12 -0
- package/dist/scanners/network/ssrf-prone-fetch.d.ts.map +1 -0
- package/dist/scanners/network/ssrf-prone-fetch.js +90 -0
- package/dist/scanners/nextjs-middleware.d.ts +26 -0
- package/dist/scanners/nextjs-middleware.d.ts.map +1 -0
- package/dist/scanners/nextjs-middleware.js +246 -0
- package/dist/scanners/privacy/debug-flags.d.ts +13 -0
- package/dist/scanners/privacy/debug-flags.d.ts.map +1 -0
- package/dist/scanners/privacy/debug-flags.js +124 -0
- package/dist/scanners/privacy/index.d.ts +6 -0
- package/dist/scanners/privacy/index.d.ts.map +1 -0
- package/dist/scanners/privacy/index.js +11 -0
- package/dist/scanners/privacy/over-broad-response.d.ts +15 -0
- package/dist/scanners/privacy/over-broad-response.d.ts.map +1 -0
- package/dist/scanners/privacy/over-broad-response.js +109 -0
- package/dist/scanners/privacy/sensitive-logging.d.ts +11 -0
- package/dist/scanners/privacy/sensitive-logging.d.ts.map +1 -0
- package/dist/scanners/privacy/sensitive-logging.js +78 -0
- package/dist/scanners/types.d.ts +456 -0
- package/dist/scanners/types.d.ts.map +1 -0
- package/dist/scanners/types.js +16 -0
- package/dist/scanners/unused-security-imports.d.ts +34 -0
- package/dist/scanners/unused-security-imports.d.ts.map +1 -0
- package/dist/scanners/unused-security-imports.js +206 -0
- package/dist/scanners/uploads/index.d.ts +5 -0
- package/dist/scanners/uploads/index.d.ts.map +1 -0
- package/dist/scanners/uploads/index.js +9 -0
- package/dist/scanners/uploads/missing-constraints.d.ts +15 -0
- package/dist/scanners/uploads/missing-constraints.d.ts.map +1 -0
- package/dist/scanners/uploads/missing-constraints.js +109 -0
- package/dist/scanners/uploads/public-path.d.ts +11 -0
- package/dist/scanners/uploads/public-path.d.ts.map +1 -0
- package/dist/scanners/uploads/public-path.js +87 -0
- package/dist/scanners/validation/client-side-only.d.ts +14 -0
- package/dist/scanners/validation/client-side-only.d.ts.map +1 -0
- package/dist/scanners/validation/client-side-only.js +140 -0
- package/dist/scanners/validation/ignored-validation.d.ts +12 -0
- package/dist/scanners/validation/ignored-validation.d.ts.map +1 -0
- package/dist/scanners/validation/ignored-validation.js +119 -0
- package/dist/scanners/validation/index.d.ts +5 -0
- package/dist/scanners/validation/index.d.ts.map +1 -0
- package/dist/scanners/validation/index.js +9 -0
- package/dist/utils/exclude-patterns.d.ts +35 -0
- package/dist/utils/exclude-patterns.d.ts.map +1 -0
- package/dist/utils/exclude-patterns.js +78 -0
- package/dist/utils/file-utils.d.ts +37 -0
- package/dist/utils/file-utils.d.ts.map +1 -0
- package/dist/utils/file-utils.js +77 -0
- package/dist/utils/fingerprint.d.ts +25 -0
- package/dist/utils/fingerprint.d.ts.map +1 -0
- package/dist/utils/fingerprint.js +28 -0
- package/dist/utils/git-info.d.ts +14 -0
- package/dist/utils/git-info.d.ts.map +1 -0
- package/dist/utils/git-info.js +55 -0
- package/dist/utils/index.d.ts +4 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +3 -0
- package/dist/utils/progress.d.ts +42 -0
- package/dist/utils/progress.d.ts.map +1 -0
- package/dist/utils/progress.js +165 -0
- package/dist/utils/sarif-formatter.d.ts +92 -0
- package/dist/utils/sarif-formatter.d.ts.map +1 -0
- package/dist/utils/sarif-formatter.js +172 -0
- package/package.json +66 -0
package/README.md
ADDED
|
@@ -0,0 +1,839 @@
|
|
|
1
|
+
# vibecheck
|
|
2
|
+
|
|
3
|
+
A deterministic, local-only security scanner for modern web applications. Designed to catch common security issues in Next.js, Express, and other Node.js projects with high precision and low false positives.
|
|
4
|
+
|
|
5
|
+
## Quickstart (No Install)
|
|
6
|
+
|
|
7
|
+
Run VibeCheck instantly without installation:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
# Using npx
|
|
11
|
+
npx vibecheck scan --fail-on off --out vibecheck-scan.json
|
|
12
|
+
|
|
13
|
+
# Using pnpm dlx
|
|
14
|
+
pnpm dlx vibecheck scan --fail-on off --out vibecheck-scan.json
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
### Scan Another Folder
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npx vibecheck scan --target ../my-other-app --out scan.json
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Installation
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
npm install -g vibecheck
|
|
27
|
+
# or
|
|
28
|
+
pnpm add -g vibecheck
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Usage
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
# Scan current directory
|
|
35
|
+
vibecheck scan
|
|
36
|
+
|
|
37
|
+
# Scan specific directory with output file
|
|
38
|
+
vibecheck scan ./my-project --out ./reports/scan.json
|
|
39
|
+
|
|
40
|
+
# Use --target as alternative to positional argument
|
|
41
|
+
vibecheck scan --target ./my-project
|
|
42
|
+
|
|
43
|
+
# Output in SARIF format (for GitHub Code Scanning)
|
|
44
|
+
vibecheck scan --format sarif
|
|
45
|
+
|
|
46
|
+
# Output both JSON and SARIF
|
|
47
|
+
vibecheck scan --format both
|
|
48
|
+
|
|
49
|
+
# Fail CI if medium or higher findings
|
|
50
|
+
vibecheck scan --fail-on medium
|
|
51
|
+
|
|
52
|
+
# Disable fail threshold (always exit 0)
|
|
53
|
+
vibecheck scan --fail-on off
|
|
54
|
+
|
|
55
|
+
# Exclude specific directories
|
|
56
|
+
vibecheck scan -e "**/legacy/**" -e "**/vendor/**"
|
|
57
|
+
|
|
58
|
+
# Include test files in scan (excluded by default)
|
|
59
|
+
vibecheck scan --include-tests
|
|
60
|
+
|
|
61
|
+
# Generate intent map with coverage metrics
|
|
62
|
+
vibecheck scan --emit-intent-map
|
|
63
|
+
|
|
64
|
+
# Explain a scan report
|
|
65
|
+
vibecheck explain ./scan.json
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Command Line Options
|
|
69
|
+
|
|
70
|
+
| Option | Description | Default |
|
|
71
|
+
|--------|-------------|---------|
|
|
72
|
+
| `-t, --target <path>` | Target directory to scan | Current directory |
|
|
73
|
+
| `-o, --out <path>` | Output file or directory | `vibecheck-artifacts/vibecheck-scan.json` |
|
|
74
|
+
| `-f, --format <format>` | Output format: `json`, `sarif`, or `both` | `json` |
|
|
75
|
+
| `--repo-name <name>` | Override repository name | Auto-detected |
|
|
76
|
+
| `--fail-on <threshold>` | Exit with non-zero if findings >= threshold | `high` |
|
|
77
|
+
| `-e, --exclude <glob>` | Glob pattern to exclude (repeatable) | See below |
|
|
78
|
+
| `--include-tests` | Include test files in scan | `false` |
|
|
79
|
+
| `--emit-intent-map` | Include route map and coverage metrics | `false` |
|
|
80
|
+
| `--changed` | Only scan changed files (not implemented) | `false` |
|
|
81
|
+
|
|
82
|
+
### Default Excludes
|
|
83
|
+
|
|
84
|
+
The following patterns are excluded by default:
|
|
85
|
+
|
|
86
|
+
**Core excludes (always applied):**
|
|
87
|
+
- `node_modules`, `dist`, `.git`, `build`, `.next`, `coverage`
|
|
88
|
+
- `.turbo`, `.cache`, `out`, `.vercel`, `.netlify`
|
|
89
|
+
|
|
90
|
+
**Test excludes (skipped with `--include-tests`):**
|
|
91
|
+
- `__tests__/**`, `*.test.*`, `*.spec.*`
|
|
92
|
+
- `test/`, `tests/`, `fixtures/`, `__mocks__/`, `__fixtures__/`
|
|
93
|
+
- `cypress/`, `e2e/`, `*.stories.*`
|
|
94
|
+
|
|
95
|
+
## Scanner Packs
|
|
96
|
+
|
|
97
|
+
VibeCheck organizes security rules into modular scanner packs. Each pack focuses on a specific security domain.
|
|
98
|
+
|
|
99
|
+
### Auth Pack
|
|
100
|
+
|
|
101
|
+
Rules for authentication and authorization issues.
|
|
102
|
+
|
|
103
|
+
#### VC-AUTH-001: Unprotected State-Changing API Route
|
|
104
|
+
|
|
105
|
+
**Severity:** High / Critical
|
|
106
|
+
**Category:** auth
|
|
107
|
+
|
|
108
|
+
Detects Next.js App Router API route handlers (POST, PUT, PATCH, DELETE) that perform database operations without authentication checks.
|
|
109
|
+
|
|
110
|
+
**What it looks for:**
|
|
111
|
+
- Route handlers in `app/**/route.ts` files
|
|
112
|
+
- Handlers that use Prisma, Drizzle, or other database operations
|
|
113
|
+
- Missing calls to `getServerSession`, `auth()`, or similar auth checks
|
|
114
|
+
|
|
115
|
+
**Example (vulnerable):**
|
|
116
|
+
```typescript
|
|
117
|
+
// app/api/users/route.ts
|
|
118
|
+
export async function POST(request: Request) {
|
|
119
|
+
const body = await request.json();
|
|
120
|
+
await prisma.user.create({ data: body }); // No auth check!
|
|
121
|
+
return Response.json({ success: true });
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
**Example (safe):**
|
|
126
|
+
```typescript
|
|
127
|
+
// app/api/users/route.ts
|
|
128
|
+
export async function POST(request: Request) {
|
|
129
|
+
const session = await getServerSession();
|
|
130
|
+
if (!session) {
|
|
131
|
+
return Response.json({ error: "Unauthorized" }, { status: 401 });
|
|
132
|
+
}
|
|
133
|
+
const body = await request.json();
|
|
134
|
+
await prisma.user.create({ data: body });
|
|
135
|
+
return Response.json({ success: true });
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+
#### VC-MW-001: Middleware Matcher Gap
|
|
142
|
+
|
|
143
|
+
**Severity:** High
|
|
144
|
+
**Category:** middleware
|
|
145
|
+
|
|
146
|
+
Detects Next.js middleware that doesn't cover API routes, potentially leaving them unprotected.
|
|
147
|
+
|
|
148
|
+
**What it looks for:**
|
|
149
|
+
- `middleware.ts` files with `config.matcher` exports
|
|
150
|
+
- Matchers that exclude `/api` routes
|
|
151
|
+
- Projects using next-auth without middleware protection
|
|
152
|
+
|
|
153
|
+
**Example (vulnerable):**
|
|
154
|
+
```typescript
|
|
155
|
+
// middleware.ts
|
|
156
|
+
export const config = {
|
|
157
|
+
matcher: ['/dashboard/:path*'] // Missing /api routes!
|
|
158
|
+
};
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
**Example (safe):**
|
|
162
|
+
```typescript
|
|
163
|
+
// middleware.ts
|
|
164
|
+
export const config = {
|
|
165
|
+
matcher: ['/api/:path*', '/dashboard/:path*']
|
|
166
|
+
};
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
### Validation Pack
|
|
172
|
+
|
|
173
|
+
Rules for input validation issues.
|
|
174
|
+
|
|
175
|
+
#### VC-VAL-001: Validation Defined But Output Ignored
|
|
176
|
+
|
|
177
|
+
**Severity:** Medium
|
|
178
|
+
**Category:** validation
|
|
179
|
+
|
|
180
|
+
Detects cases where validation libraries (Zod, Yup, Joi) are called but the validated result is not used.
|
|
181
|
+
|
|
182
|
+
**What it looks for:**
|
|
183
|
+
- Calls to `.parse()`, `.validate()`, `.parseAsync()`, etc.
|
|
184
|
+
- Result not assigned to a variable
|
|
185
|
+
- Raw `request.body` or `req.body` used after validation
|
|
186
|
+
|
|
187
|
+
**Example (vulnerable):**
|
|
188
|
+
```typescript
|
|
189
|
+
const schema = z.object({ name: z.string() });
|
|
190
|
+
schema.parse(body); // Result ignored!
|
|
191
|
+
await prisma.user.create({ data: body }); // Uses unvalidated body
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
**Example (safe):**
|
|
195
|
+
```typescript
|
|
196
|
+
const schema = z.object({ name: z.string() });
|
|
197
|
+
const validated = schema.parse(body);
|
|
198
|
+
await prisma.user.create({ data: validated });
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
---
|
|
202
|
+
|
|
203
|
+
### Privacy Pack
|
|
204
|
+
|
|
205
|
+
Rules for data privacy and logging issues.
|
|
206
|
+
|
|
207
|
+
#### VC-PRIV-001: Sensitive Data Logged
|
|
208
|
+
|
|
209
|
+
**Severity:** High
|
|
210
|
+
**Category:** privacy
|
|
211
|
+
|
|
212
|
+
Detects logging statements that include sensitive variable names.
|
|
213
|
+
|
|
214
|
+
**What it looks for:**
|
|
215
|
+
- `console.log`, `console.info`, `console.debug`, `logger.info`, etc.
|
|
216
|
+
- Variables containing: password, secret, token, apiKey, creditCard, ssn, etc.
|
|
217
|
+
|
|
218
|
+
**Example (vulnerable):**
|
|
219
|
+
```typescript
|
|
220
|
+
console.log("User login:", { email, password }); // Logs password!
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
**Example (safe):**
|
|
224
|
+
```typescript
|
|
225
|
+
console.log("User login:", { email, timestamp: Date.now() });
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
---
|
|
229
|
+
|
|
230
|
+
### Config Pack
|
|
231
|
+
|
|
232
|
+
Rules for configuration and secrets management issues.
|
|
233
|
+
|
|
234
|
+
#### VC-CONFIG-001: Undocumented Environment Variable
|
|
235
|
+
|
|
236
|
+
**Severity:** Low
|
|
237
|
+
**Category:** config
|
|
238
|
+
|
|
239
|
+
Detects `process.env.VAR` references that aren't documented in `.env.example`.
|
|
240
|
+
|
|
241
|
+
---
|
|
242
|
+
|
|
243
|
+
#### VC-CONFIG-002: Insecure Default Secret Fallback
|
|
244
|
+
|
|
245
|
+
**Severity:** Critical
|
|
246
|
+
**Category:** secrets
|
|
247
|
+
|
|
248
|
+
Detects hardcoded fallback values for security-critical environment variables.
|
|
249
|
+
|
|
250
|
+
**What it looks for:**
|
|
251
|
+
- `process.env.VAR || "fallback"` patterns
|
|
252
|
+
- Variables named: SECRET, KEY, TOKEN, PASSWORD, etc.
|
|
253
|
+
- Hardcoded string fallbacks
|
|
254
|
+
|
|
255
|
+
**Example (vulnerable):**
|
|
256
|
+
```typescript
|
|
257
|
+
const jwtSecret = process.env.JWT_SECRET || "development-secret";
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
**Example (safe):**
|
|
261
|
+
```typescript
|
|
262
|
+
const jwtSecret = process.env.JWT_SECRET;
|
|
263
|
+
if (!jwtSecret) throw new Error("JWT_SECRET is required");
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
---
|
|
267
|
+
|
|
268
|
+
### Network Pack
|
|
269
|
+
|
|
270
|
+
Rules for network security issues.
|
|
271
|
+
|
|
272
|
+
#### VC-NET-001: SSRF-Prone Fetch
|
|
273
|
+
|
|
274
|
+
**Severity:** High
|
|
275
|
+
**Category:** network
|
|
276
|
+
|
|
277
|
+
Detects fetch/axios calls where the URL is derived from user input without validation.
|
|
278
|
+
|
|
279
|
+
**What it looks for:**
|
|
280
|
+
- `fetch()` or `axios.get()` calls
|
|
281
|
+
- URL constructed from request parameters, query strings, or body
|
|
282
|
+
- No URL validation or allowlist checks
|
|
283
|
+
|
|
284
|
+
**Example (vulnerable):**
|
|
285
|
+
```typescript
|
|
286
|
+
export async function GET(request: Request) {
|
|
287
|
+
const { searchParams } = new URL(request.url);
|
|
288
|
+
const url = searchParams.get("url");
|
|
289
|
+
const response = await fetch(url); // SSRF risk!
|
|
290
|
+
return Response.json(await response.json());
|
|
291
|
+
}
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
**Example (safe):**
|
|
295
|
+
```typescript
|
|
296
|
+
const ALLOWED_HOSTS = ["api.example.com", "cdn.example.com"];
|
|
297
|
+
|
|
298
|
+
export async function GET(request: Request) {
|
|
299
|
+
const { searchParams } = new URL(request.url);
|
|
300
|
+
const url = searchParams.get("url");
|
|
301
|
+
const parsed = new URL(url);
|
|
302
|
+
if (!ALLOWED_HOSTS.includes(parsed.hostname)) {
|
|
303
|
+
return Response.json({ error: "Invalid host" }, { status: 400 });
|
|
304
|
+
}
|
|
305
|
+
const response = await fetch(url);
|
|
306
|
+
return Response.json(await response.json());
|
|
307
|
+
}
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
---
|
|
311
|
+
|
|
312
|
+
### Hallucinations Pack
|
|
313
|
+
|
|
314
|
+
Rules for detecting security libraries that are imported but not properly used.
|
|
315
|
+
|
|
316
|
+
#### VC-HALL-001: Security Library Imported But Not Used
|
|
317
|
+
|
|
318
|
+
**Severity:** Medium
|
|
319
|
+
**Category:** middleware
|
|
320
|
+
|
|
321
|
+
Detects security libraries (helmet, cors, csurf, etc.) that are imported but the import is never used.
|
|
322
|
+
|
|
323
|
+
**What it looks for:**
|
|
324
|
+
- Imports from security packages: helmet, cors, csurf, express-rate-limit, hpp, etc.
|
|
325
|
+
- Import identifier not referenced after the import statement
|
|
326
|
+
|
|
327
|
+
**Example (vulnerable):**
|
|
328
|
+
```typescript
|
|
329
|
+
import helmet from "helmet"; // Imported but never used!
|
|
330
|
+
import cors from "cors";
|
|
331
|
+
|
|
332
|
+
const app = express();
|
|
333
|
+
app.use(cors());
|
|
334
|
+
// Missing: app.use(helmet());
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
---
|
|
338
|
+
|
|
339
|
+
#### VC-HALL-002: NextAuth Imported But Not Enforced
|
|
340
|
+
|
|
341
|
+
**Severity:** High
|
|
342
|
+
**Category:** auth
|
|
343
|
+
|
|
344
|
+
Detects next-auth imported but `getServerSession` never called, suggesting auth is configured but not enforced.
|
|
345
|
+
|
|
346
|
+
**What it looks for:**
|
|
347
|
+
- Imports from `next-auth` or `next-auth/next`
|
|
348
|
+
- No calls to `getServerSession` anywhere in the file
|
|
349
|
+
|
|
350
|
+
---
|
|
351
|
+
|
|
352
|
+
## Phase 2 Rules
|
|
353
|
+
|
|
354
|
+
### Network Pack (Extended)
|
|
355
|
+
|
|
356
|
+
#### VC-NET-002: Open Redirect
|
|
357
|
+
|
|
358
|
+
**Severity:** High
|
|
359
|
+
**Category:** network
|
|
360
|
+
|
|
361
|
+
Detects server-side redirects where user-controlled input determines the destination.
|
|
362
|
+
|
|
363
|
+
**Two-signal requirement:** Must identify user-controlled source AND redirect call uses that value.
|
|
364
|
+
|
|
365
|
+
**Example (vulnerable):**
|
|
366
|
+
```typescript
|
|
367
|
+
export async function GET(request: Request) {
|
|
368
|
+
const { searchParams } = new URL(request.url);
|
|
369
|
+
const next = searchParams.get("next");
|
|
370
|
+
return NextResponse.redirect(next!); // Open redirect!
|
|
371
|
+
}
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
---
|
|
375
|
+
|
|
376
|
+
#### VC-NET-003: Over-permissive CORS with Credentials
|
|
377
|
+
|
|
378
|
+
**Severity:** High
|
|
379
|
+
**Category:** network
|
|
380
|
+
|
|
381
|
+
Detects CORS configurations that combine `origin: "*"` with `credentials: true`.
|
|
382
|
+
|
|
383
|
+
**Example (vulnerable):**
|
|
384
|
+
```typescript
|
|
385
|
+
cors({ origin: "*", credentials: true }) // Dangerous combination!
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
---
|
|
389
|
+
|
|
390
|
+
#### VC-NET-004: Missing Request Timeout
|
|
391
|
+
|
|
392
|
+
**Severity:** Low
|
|
393
|
+
**Category:** network
|
|
394
|
+
|
|
395
|
+
Detects fetch/axios calls without timeout in API route handlers.
|
|
396
|
+
|
|
397
|
+
**Example (vulnerable):**
|
|
398
|
+
```typescript
|
|
399
|
+
const response = await fetch("https://external-api.com/data"); // No timeout!
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
---
|
|
403
|
+
|
|
404
|
+
### Middleware Pack
|
|
405
|
+
|
|
406
|
+
#### VC-RATE-001: Missing Rate Limiting
|
|
407
|
+
|
|
408
|
+
**Severity:** Medium
|
|
409
|
+
**Category:** middleware
|
|
410
|
+
**Confidence:** 0.65
|
|
411
|
+
|
|
412
|
+
Detects unauthenticated state-changing endpoints without rate limiting.
|
|
413
|
+
|
|
414
|
+
**What it looks for:**
|
|
415
|
+
- POST/PUT/PATCH/DELETE handlers without auth checks
|
|
416
|
+
- Handlers with database writes or sensitive operations
|
|
417
|
+
- No rate limiting signals in handler or middleware
|
|
418
|
+
|
|
419
|
+
---
|
|
420
|
+
|
|
421
|
+
### Validation Pack (Extended)
|
|
422
|
+
|
|
423
|
+
#### VC-VAL-002: Client-Side Only Validation
|
|
424
|
+
|
|
425
|
+
**Severity:** Medium
|
|
426
|
+
**Category:** validation
|
|
427
|
+
|
|
428
|
+
Detects validation in frontend components but missing in API routes.
|
|
429
|
+
|
|
430
|
+
---
|
|
431
|
+
|
|
432
|
+
### Privacy Pack (Extended)
|
|
433
|
+
|
|
434
|
+
#### VC-PRIV-002: Over-broad API Response
|
|
435
|
+
|
|
436
|
+
**Severity:** Medium/High
|
|
437
|
+
**Category:** privacy
|
|
438
|
+
|
|
439
|
+
Detects Prisma queries returning full models without `select` restrictions.
|
|
440
|
+
|
|
441
|
+
**Example (vulnerable):**
|
|
442
|
+
```typescript
|
|
443
|
+
const users = await prisma.user.findMany(); // Returns password hash!
|
|
444
|
+
return Response.json(users);
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
**Example (safe):**
|
|
448
|
+
```typescript
|
|
449
|
+
const users = await prisma.user.findMany({
|
|
450
|
+
select: { id: true, name: true, email: true }
|
|
451
|
+
});
|
|
452
|
+
return Response.json(users);
|
|
453
|
+
```
|
|
454
|
+
|
|
455
|
+
---
|
|
456
|
+
|
|
457
|
+
#### VC-PRIV-003: Debug Flags in Production
|
|
458
|
+
|
|
459
|
+
**Severity:** Medium
|
|
460
|
+
**Category:** config
|
|
461
|
+
|
|
462
|
+
Detects `debug: true` or `dev: true` in config files without NODE_ENV guards.
|
|
463
|
+
|
|
464
|
+
---
|
|
465
|
+
|
|
466
|
+
### Crypto Pack
|
|
467
|
+
|
|
468
|
+
#### VC-CRYPTO-001: Math.random for Tokens
|
|
469
|
+
|
|
470
|
+
**Severity:** High
|
|
471
|
+
**Category:** crypto
|
|
472
|
+
|
|
473
|
+
Detects Math.random used to generate tokens, keys, or session IDs.
|
|
474
|
+
|
|
475
|
+
**Example (vulnerable):**
|
|
476
|
+
```typescript
|
|
477
|
+
const token = Math.random().toString(36).substring(2); // Predictable!
|
|
478
|
+
```
|
|
479
|
+
|
|
480
|
+
**Example (safe):**
|
|
481
|
+
```typescript
|
|
482
|
+
const token = crypto.randomBytes(32).toString('hex');
|
|
483
|
+
```
|
|
484
|
+
|
|
485
|
+
---
|
|
486
|
+
|
|
487
|
+
#### VC-CRYPTO-002: JWT Decode Without Verify
|
|
488
|
+
|
|
489
|
+
**Severity:** Critical
|
|
490
|
+
**Category:** crypto
|
|
491
|
+
|
|
492
|
+
Detects jwt.decode() used without jwt.verify() in the same file.
|
|
493
|
+
|
|
494
|
+
**Example (vulnerable):**
|
|
495
|
+
```typescript
|
|
496
|
+
const payload = jwt.decode(token); // Signature not verified!
|
|
497
|
+
```
|
|
498
|
+
|
|
499
|
+
**Example (safe):**
|
|
500
|
+
```typescript
|
|
501
|
+
const payload = jwt.verify(token, secret); // Signature verified
|
|
502
|
+
```
|
|
503
|
+
|
|
504
|
+
---
|
|
505
|
+
|
|
506
|
+
#### VC-CRYPTO-003: Weak Password Hashing
|
|
507
|
+
|
|
508
|
+
**Severity:** High
|
|
509
|
+
**Category:** crypto
|
|
510
|
+
|
|
511
|
+
Detects MD5/SHA1 for passwords or bcrypt with saltRounds < 10.
|
|
512
|
+
|
|
513
|
+
**Example (vulnerable):**
|
|
514
|
+
```typescript
|
|
515
|
+
crypto.createHash('md5').update(password).digest('hex'); // Weak!
|
|
516
|
+
bcrypt.hash(password, 5); // Too few rounds!
|
|
517
|
+
```
|
|
518
|
+
|
|
519
|
+
---
|
|
520
|
+
|
|
521
|
+
### Uploads Pack
|
|
522
|
+
|
|
523
|
+
#### VC-UP-001: File Upload Without Constraints
|
|
524
|
+
|
|
525
|
+
**Severity:** High
|
|
526
|
+
**Category:** uploads
|
|
527
|
+
|
|
528
|
+
Detects file uploads without size or type validation.
|
|
529
|
+
|
|
530
|
+
**Example (vulnerable):**
|
|
531
|
+
```typescript
|
|
532
|
+
const file = formData.get('file') as File;
|
|
533
|
+
// No size or type check before processing
|
|
534
|
+
```
|
|
535
|
+
|
|
536
|
+
---
|
|
537
|
+
|
|
538
|
+
#### VC-UP-002: Upload to Public Path
|
|
539
|
+
|
|
540
|
+
**Severity:** High
|
|
541
|
+
**Category:** uploads
|
|
542
|
+
|
|
543
|
+
Detects uploaded files written to public directories.
|
|
544
|
+
|
|
545
|
+
**Example (vulnerable):**
|
|
546
|
+
```typescript
|
|
547
|
+
fs.writeFileSync(`public/uploads/${filename}`, buffer); // Publicly accessible!
|
|
548
|
+
```
|
|
549
|
+
|
|
550
|
+
---
|
|
551
|
+
|
|
552
|
+
## Phase 3: Hallucination Detection Engine
|
|
553
|
+
|
|
554
|
+
Advanced cross-file analysis for detecting security intent vs implementation gaps.
|
|
555
|
+
|
|
556
|
+
### Intent Command
|
|
557
|
+
|
|
558
|
+
Generate a security intent map baseline for your codebase:
|
|
559
|
+
|
|
560
|
+
```bash
|
|
561
|
+
# Generate intent map
|
|
562
|
+
vibecheck intent ./my-project --out intent-map.json
|
|
563
|
+
|
|
564
|
+
# Include intent map in scan output
|
|
565
|
+
vibecheck scan ./my-project --emit-intent-map
|
|
566
|
+
```
|
|
567
|
+
|
|
568
|
+
### Hallucinations Pack (Phase 3)
|
|
569
|
+
|
|
570
|
+
#### VC-HALL-010: Comment Claims Protection But Unproven
|
|
571
|
+
|
|
572
|
+
**Severity:** Medium
|
|
573
|
+
**Category:** hallucinations
|
|
574
|
+
**Confidence:** 0.75
|
|
575
|
+
|
|
576
|
+
Detects comments that claim security protection but the implementation doesn't prove it.
|
|
577
|
+
|
|
578
|
+
**Example (vulnerable):**
|
|
579
|
+
```typescript
|
|
580
|
+
// This route is protected by authentication middleware
|
|
581
|
+
export async function POST(request: Request) {
|
|
582
|
+
// No auth check here, and middleware doesn't cover /api routes
|
|
583
|
+
await prisma.user.create({ data: body });
|
|
584
|
+
}
|
|
585
|
+
```
|
|
586
|
+
|
|
587
|
+
---
|
|
588
|
+
|
|
589
|
+
#### VC-HALL-011: Middleware Assumed But Not Matching
|
|
590
|
+
|
|
591
|
+
**Severity:** High
|
|
592
|
+
**Category:** hallucinations
|
|
593
|
+
**Confidence:** 0.70
|
|
594
|
+
|
|
595
|
+
Detects routes that expect middleware protection but are not covered by matcher patterns.
|
|
596
|
+
|
|
597
|
+
**Example (vulnerable):**
|
|
598
|
+
```typescript
|
|
599
|
+
// middleware.ts
|
|
600
|
+
export const config = {
|
|
601
|
+
matcher: ['/dashboard/:path*'], // Missing /api routes!
|
|
602
|
+
};
|
|
603
|
+
|
|
604
|
+
// app/api/users/route.ts
|
|
605
|
+
// Auth handled by middleware (but middleware doesn't cover this!)
|
|
606
|
+
export async function DELETE(request: Request) {
|
|
607
|
+
await prisma.user.delete({ where: { id } });
|
|
608
|
+
}
|
|
609
|
+
```
|
|
610
|
+
|
|
611
|
+
---
|
|
612
|
+
|
|
613
|
+
#### VC-HALL-012: Validation Claimed But Missing/Ignored
|
|
614
|
+
|
|
615
|
+
**Severity:** Medium
|
|
616
|
+
**Category:** hallucinations
|
|
617
|
+
**Confidence:** 0.80
|
|
618
|
+
|
|
619
|
+
Detects validation that is claimed but not properly implemented or used.
|
|
620
|
+
|
|
621
|
+
**Example (vulnerable):**
|
|
622
|
+
```typescript
|
|
623
|
+
const schema = z.object({ name: z.string() });
|
|
624
|
+
schema.parse(body); // Result ignored!
|
|
625
|
+
await prisma.user.create({ data: body }); // Uses raw body
|
|
626
|
+
```
|
|
627
|
+
|
|
628
|
+
---
|
|
629
|
+
|
|
630
|
+
#### VC-AUTH-010: Auth-by-UI with Server Gap
|
|
631
|
+
|
|
632
|
+
**Severity:** Critical
|
|
633
|
+
**Category:** auth
|
|
634
|
+
**Confidence:** 0.85
|
|
635
|
+
|
|
636
|
+
Detects client-side auth checks without corresponding server-side protection.
|
|
637
|
+
|
|
638
|
+
**Example (vulnerable):**
|
|
639
|
+
```tsx
|
|
640
|
+
// Client component
|
|
641
|
+
const { session } = useSession();
|
|
642
|
+
if (session) {
|
|
643
|
+
// Only render if logged in
|
|
644
|
+
await fetch('/api/users', { method: 'DELETE' }); // Server has no auth!
|
|
645
|
+
}
|
|
646
|
+
```
|
|
647
|
+
|
|
648
|
+
---
|
|
649
|
+
|
|
650
|
+
### Coverage Metrics
|
|
651
|
+
|
|
652
|
+
With `--emit-intent-map`, the scan artifact includes coverage metrics:
|
|
653
|
+
|
|
654
|
+
- **authCoverage**: Percentage of state-changing routes with proven auth
|
|
655
|
+
- **validationCoverage**: Percentage of routes with request bodies that have validation
|
|
656
|
+
- **middlewareCoverage**: Percentage of routes covered by middleware matchers
|
|
657
|
+
|
|
658
|
+
### Intent Map Structure
|
|
659
|
+
|
|
660
|
+
```json
|
|
661
|
+
{
|
|
662
|
+
"routeMap": [
|
|
663
|
+
{
|
|
664
|
+
"routeId": "abc123",
|
|
665
|
+
"method": "POST",
|
|
666
|
+
"path": "/api/users",
|
|
667
|
+
"file": "app/api/users/route.ts",
|
|
668
|
+
"startLine": 10,
|
|
669
|
+
"endLine": 25
|
|
670
|
+
}
|
|
671
|
+
],
|
|
672
|
+
"middlewareMap": [
|
|
673
|
+
{
|
|
674
|
+
"file": "middleware.ts",
|
|
675
|
+
"matchers": ["/api/:path*"],
|
|
676
|
+
"protectsApi": true,
|
|
677
|
+
"startLine": 15
|
|
678
|
+
}
|
|
679
|
+
],
|
|
680
|
+
"intentMap": [
|
|
681
|
+
{
|
|
682
|
+
"intentId": "def456",
|
|
683
|
+
"type": "AUTH_ENFORCED",
|
|
684
|
+
"scope": "route",
|
|
685
|
+
"source": "comment",
|
|
686
|
+
"textEvidence": "// Protected by auth"
|
|
687
|
+
}
|
|
688
|
+
],
|
|
689
|
+
"proofTraces": {
|
|
690
|
+
"abc123": {
|
|
691
|
+
"routeId": "abc123",
|
|
692
|
+
"authProven": true,
|
|
693
|
+
"validationProven": false,
|
|
694
|
+
"middlewareCovered": true,
|
|
695
|
+
"steps": [...]
|
|
696
|
+
}
|
|
697
|
+
},
|
|
698
|
+
"coverage": {
|
|
699
|
+
"authCoverage": 0.85,
|
|
700
|
+
"validationCoverage": 0.60,
|
|
701
|
+
"middlewareCoverage": 1.0
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
```
|
|
705
|
+
|
|
706
|
+
---
|
|
707
|
+
|
|
708
|
+
## Output Formats
|
|
709
|
+
|
|
710
|
+
VibeCheck supports two output formats: JSON and SARIF.
|
|
711
|
+
|
|
712
|
+
### JSON Format
|
|
713
|
+
|
|
714
|
+
The default format, defined by `@vibecheck/schema`:
|
|
715
|
+
|
|
716
|
+
```json
|
|
717
|
+
{
|
|
718
|
+
"artifactVersion": "0.1",
|
|
719
|
+
"generatedAt": "2024-01-15T10:30:00.000Z",
|
|
720
|
+
"tool": {
|
|
721
|
+
"name": "vibecheck",
|
|
722
|
+
"version": "1.0.0"
|
|
723
|
+
},
|
|
724
|
+
"summary": {
|
|
725
|
+
"totalFindings": 2,
|
|
726
|
+
"bySeverity": { "critical": 0, "high": 1, "medium": 1, "low": 0, "info": 0 },
|
|
727
|
+
"byCategory": { "auth": 1, "validation": 1, ... }
|
|
728
|
+
},
|
|
729
|
+
"findings": [
|
|
730
|
+
{
|
|
731
|
+
"id": "f-abc123",
|
|
732
|
+
"severity": "high",
|
|
733
|
+
"confidence": 0.85,
|
|
734
|
+
"category": "auth",
|
|
735
|
+
"ruleId": "VC-AUTH-001",
|
|
736
|
+
"title": "Missing authentication on POST /api/users",
|
|
737
|
+
"description": "...",
|
|
738
|
+
"evidence": [
|
|
739
|
+
{
|
|
740
|
+
"file": "app/api/users/route.ts",
|
|
741
|
+
"startLine": 25,
|
|
742
|
+
"endLine": 30,
|
|
743
|
+
"snippet": "...",
|
|
744
|
+
"label": "Unprotected route handler"
|
|
745
|
+
}
|
|
746
|
+
],
|
|
747
|
+
"remediation": {
|
|
748
|
+
"recommendedFix": "Add authentication middleware"
|
|
749
|
+
},
|
|
750
|
+
"fingerprint": "sha256:..."
|
|
751
|
+
}
|
|
752
|
+
],
|
|
753
|
+
"metrics": {
|
|
754
|
+
"filesScanned": 50,
|
|
755
|
+
"linesOfCode": 5000,
|
|
756
|
+
"scanDurationMs": 1234
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
```
|
|
760
|
+
|
|
761
|
+
### SARIF Format
|
|
762
|
+
|
|
763
|
+
[SARIF (Static Analysis Results Interchange Format)](https://sarifweb.azurewebsites.net/) is an OASIS standard for static analysis tools. Use `--format sarif` to generate SARIF 2.1.0 output, compatible with:
|
|
764
|
+
|
|
765
|
+
- **GitHub Code Scanning** - Upload via `github/codeql-action/upload-sarif`
|
|
766
|
+
- **Azure DevOps** - Native SARIF support in security reports
|
|
767
|
+
- **VS Code** - SARIF Viewer extension
|
|
768
|
+
- **Other tools** - Any SARIF 2.1.0 compatible viewer
|
|
769
|
+
|
|
770
|
+
```bash
|
|
771
|
+
# Generate SARIF for GitHub Code Scanning
|
|
772
|
+
vibecheck scan --format sarif --out results.sarif
|
|
773
|
+
|
|
774
|
+
# Upload to GitHub (in CI workflow)
|
|
775
|
+
- uses: github/codeql-action/upload-sarif@v2
|
|
776
|
+
with:
|
|
777
|
+
sarif_file: results.sarif
|
|
778
|
+
```
|
|
779
|
+
|
|
780
|
+
## Architecture
|
|
781
|
+
|
|
782
|
+
```
|
|
783
|
+
packages/cli/src/
|
|
784
|
+
├── commands/
|
|
785
|
+
│ ├── scan.ts # Main scan orchestrator
|
|
786
|
+
│ └── explain.ts # Report viewer
|
|
787
|
+
├── scanners/
|
|
788
|
+
│ ├── types.ts # ScanContext, types
|
|
789
|
+
│ ├── helpers/
|
|
790
|
+
│ │ ├── ast-helpers.ts # ts-morph utilities
|
|
791
|
+
│ │ └── context-builder.ts # ScanContext factory
|
|
792
|
+
│ ├── auth/
|
|
793
|
+
│ │ ├── unprotected-api-route.ts # VC-AUTH-001
|
|
794
|
+
│ │ └── middleware-gap.ts # VC-MW-001
|
|
795
|
+
│ ├── validation/
|
|
796
|
+
│ │ └── ignored-validation.ts # VC-VAL-001
|
|
797
|
+
│ ├── privacy/
|
|
798
|
+
│ │ └── sensitive-logging.ts # VC-PRIV-001
|
|
799
|
+
│ ├── config/
|
|
800
|
+
│ │ ├── undocumented-env.ts # VC-CONFIG-001
|
|
801
|
+
│ │ └── insecure-defaults.ts # VC-CONFIG-002
|
|
802
|
+
│ ├── network/
|
|
803
|
+
│ │ └── ssrf-prone-fetch.ts # VC-NET-001
|
|
804
|
+
│ └── hallucinations/
|
|
805
|
+
│ └── unused-security-imports.ts # VC-HALL-001, VC-HALL-002
|
|
806
|
+
└── index.ts
|
|
807
|
+
```
|
|
808
|
+
|
|
809
|
+
## Design Principles
|
|
810
|
+
|
|
811
|
+
1. **Deterministic** - No LLM calls, results are reproducible
|
|
812
|
+
2. **Local-only** - All analysis runs on your machine
|
|
813
|
+
3. **Low false positives** - Precision over recall
|
|
814
|
+
4. **Framework-aware** - Built for Next.js, Express patterns
|
|
815
|
+
5. **Schema-compliant** - Output conforms to `@vibecheck/schema`
|
|
816
|
+
|
|
817
|
+
## Adding New Scanners
|
|
818
|
+
|
|
819
|
+
Each scanner must:
|
|
820
|
+
|
|
821
|
+
1. Accept a `ScanContext` with repo info and AST helpers
|
|
822
|
+
2. Return `Finding[]` conforming to the schema
|
|
823
|
+
3. Generate deterministic fingerprints for deduplication
|
|
824
|
+
4. Include clear evidence with file locations
|
|
825
|
+
|
|
826
|
+
```typescript
|
|
827
|
+
import { type ScanContext } from "../types.js";
|
|
828
|
+
import { type Finding } from "@vibecheck/schema";
|
|
829
|
+
|
|
830
|
+
export async function scanMyRule(ctx: ScanContext): Promise<Finding[]> {
|
|
831
|
+
const findings: Finding[] = [];
|
|
832
|
+
// ... detection logic
|
|
833
|
+
return findings;
|
|
834
|
+
}
|
|
835
|
+
```
|
|
836
|
+
|
|
837
|
+
## License
|
|
838
|
+
|
|
839
|
+
MIT
|