vbguard 0.3.0 → 0.5.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/README.md CHANGED
@@ -1,26 +1,46 @@
1
- # ⚡ vibeguard
1
+ # vbguard
2
2
 
3
- **Security scanner built for AI-generated code. Catches what traditional scanners miss.**
3
+ **Security scanner for AI-generated code. Catches what Snyk, Semgrep, and GitGuardian miss.**
4
4
 
5
- [![npm version](https://img.shields.io/npm/v/vibeguard.svg)](https://www.npmjs.com/package/vibeguard)
5
+ [![npm version](https://img.shields.io/npm/v/vbguard.svg)](https://www.npmjs.com/package/vbguard)
6
+ [![npm downloads](https://img.shields.io/npm/dm/vbguard.svg)](https://www.npmjs.com/package/vbguard)
6
7
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
8
 
8
9
  ---
9
10
 
10
- Vibe coding is fast. But 45% of AI-generated code ships with known vulnerabilities. The Moltbook breach, the pickle exploits, the hardcoded Supabase keys all caused by patterns that traditional scanners weren't designed to catch.
11
+ Vibe coding is fast. But 45% of AI-generated code ships with known vulnerabilities. The Moltbook breach, the pickle exploits, the hardcoded Supabase keys -- all caused by patterns that traditional scanners weren't designed to catch.
11
12
 
12
- **vibeguard** scans your codebase for the security mistakes that AI coding tools (Cursor, Claude Code, Copilot, Lovable, Bolt, Replit) introduce most often.
13
+ **vbguard** scans your codebase for security mistakes that AI coding tools (Cursor, Claude Code, Copilot, Lovable, Bolt, Replit) introduce most often. It also does things no other scanner can -- like detecting **hallucinated packages** and **broken auth flows**.
13
14
 
14
15
  ## Quick Start
15
16
 
16
17
  ```bash
17
- npx vibeguard .
18
+ npx vbguard .
18
19
  ```
19
20
 
20
- That's it. No config, no account, no API key.
21
+ No config. No account. No API key. Runs in milliseconds.
22
+
23
+ ## Get Your Security Score
24
+
25
+ ```bash
26
+ npx vbguard . --score
27
+ ```
28
+
29
+ ```
30
+ vbguard security score:
31
+
32
+ ████████████████░░░░ 78/100 -- good
33
+
34
+ * 0 critical
35
+ * 2 high
36
+ * 3 medium
37
+ * 1 low
38
+ ```
21
39
 
22
40
  ## What It Catches
23
41
 
42
+ ### Core Scanners
43
+
24
44
  | Category | Examples | Severity |
25
45
  |----------|----------|----------|
26
46
  | **Hardcoded Secrets** | API keys, DB connection strings, JWTs, private keys inline in code | Critical |
@@ -33,143 +53,291 @@ That's it. No config, no account, no API key.
33
53
  | **Missing .gitignore** | `.env` files not gitignored, secrets about to be committed | Critical |
34
54
  | **Docker Misconfigs** | Running as root, copying `.env` into images, exposed DB ports | Medium-High |
35
55
 
36
- ## Supported Languages
56
+ ### Hallucinated Package Detector (unique to vbguard)
57
+
58
+ AI tools frequently invent package names that don't exist. If someone registers that name with malicious code, your project is compromised.
37
59
 
38
- - **JavaScript / TypeScript** Express, Fastify, Next.js, React, Vue, Svelte
39
- - **Python** Flask, FastAPI, Django
60
+ - **CRITICAL**: Package doesn't exist on npm/PyPI (hallucinated by AI)
61
+ - **HIGH**: Package created less than 30 days ago (potential typosquat)
62
+ - **HIGH**: Package name within edit distance 1-2 of a popular package (`lodas` vs `lodash`)
63
+
64
+ ```bash
65
+ # Skip online checks for offline environments
66
+ npx vbguard . --offline
67
+ ```
68
+
69
+ ### Auth Flow Analyzer (Snyk can't do this)
70
+
71
+ Snyk and Semgrep **cannot** catch broken auth because it requires understanding application semantics. vbguard detects:
72
+
73
+ | Pattern | Why It Matters |
74
+ |---------|---------------|
75
+ | JWT with no expiration | Stolen tokens grant permanent access |
76
+ | JWT with weak/hardcoded secret | Anyone can forge valid tokens |
77
+ | User enumeration | Different errors for "user not found" vs "wrong password" reveal valid emails |
78
+ | Tokens in localStorage | Vulnerable to XSS -- use httpOnly cookies |
79
+ | OAuth open redirects | Unvalidated redirect_uri lets attackers steal tokens |
80
+ | Password reset without rate limiting | Enables brute-force of reset tokens |
81
+ | Signup with no email verification | Allows fake account creation |
82
+ | Inverted auth checks | The exact Lovable/Moltbook bug: blocking authenticated users, allowing anonymous |
83
+ | API routes with no auth middleware | Publicly accessible endpoints |
84
+ | Supabase signUp with no email confirmation | Anyone can register with any email |
85
+
86
+ ### Vibe-Code Patterns (unique to AI-generated code)
87
+
88
+ Patterns that **only** appear in AI-generated code, not human code:
89
+
90
+ | Pattern | Why It Matters |
91
+ |---------|---------------|
92
+ | Security TODO comments | `TODO: add authentication` -- AI leaves these as placeholders and never comes back |
93
+ | Placeholder data in production | `test@test.com`, `John Doe`, `password123` shipped to prod |
94
+ | Sensitive data in console.log | `console.log(password)`, `console.log(token)` -- debugging leftovers |
95
+ | Commented-out security code | Auth checks, validation, rate limiting disabled during debugging |
96
+ | AI-generated markers | "Created with Cursor", "Copilot suggestion" -- indicates unreviewed code |
97
+ | Error stack trace leaks | `res.status(500).send(err.stack)` exposes internals to attackers |
98
+ | Silent error swallowing | `catch(e) {}` -- security failures silently ignored |
99
+
100
+ ### Framework-Specific Scanners
101
+
102
+ #### Next.js
103
+
104
+ | Pattern | Severity |
105
+ |---------|----------|
106
+ | API keys in `"use client"` components | Critical |
107
+ | Missing `middleware.ts` for auth | Medium |
108
+ | Server Actions with no input validation | High |
109
+ | `publicRuntimeConfig` exposing secrets | Critical |
110
+ | API routes with no auth checks | Medium |
111
+ | `NEXT_PUBLIC_` prefix on sensitive env vars | Critical |
112
+
113
+ #### Supabase
114
+
115
+ | Pattern | Severity |
116
+ |---------|----------|
117
+ | Service role key in client-side code | Critical |
118
+ | `.from('table').select('*')` with no filter | Medium |
119
+ | Anon key used with no RLS | High |
120
+ | Public storage buckets with no policies | Medium |
121
+ | `SECURITY DEFINER` functions without auth checks | High |
122
+
123
+ #### Firebase
124
+
125
+ | Pattern | Severity |
126
+ |---------|----------|
127
+ | Firestore rules with `allow read, write: if true` | Critical |
128
+ | Realtime Database rules with `.read: true` at root | Critical |
129
+ | Storage rules with no auth condition | Critical/High |
130
+ | Firebase Admin SDK in client-side code | Critical |
131
+ | Firebase config with no App Check | Medium |
132
+
133
+ ## Comparison
134
+
135
+ | Feature | vbguard | Snyk | Semgrep | GitGuardian |
136
+ |---------|---------|------|---------|-------------|
137
+ | Hardcoded secrets | Yes | Partial | Yes | Yes |
138
+ | Hallucinated package detection | **Yes** | No | No | No |
139
+ | Auth flow analysis | **Yes** | No | No | No |
140
+ | AI-code-specific patterns | **Yes** | No | No | No |
141
+ | Next.js-specific rules | **Yes** | No | Partial | No |
142
+ | Supabase security | **Yes** | No | No | No |
143
+ | Firebase security | **Yes** | Partial | Partial | No |
144
+ | Security score | **Yes** | No | No | No |
145
+ | Zero config | **Yes** | No | No | No |
146
+ | Runs offline | **Yes** | No | Yes | No |
147
+ | Free & open source | **Yes** | Partial | Yes | Partial |
148
+ | Speed | < 100ms | Minutes | Seconds | Seconds |
40
149
 
41
150
  ## Usage
42
151
 
43
152
  ```bash
44
153
  # Scan current directory
45
- vibeguard .
154
+ npx vbguard .
46
155
 
47
156
  # Scan a specific project
48
- vibeguard ./my-app
157
+ npx vbguard ./my-app
49
158
 
50
159
  # Only show high and critical issues
51
- vibeguard . --severity=high
160
+ npx vbguard . --severity=high
161
+
162
+ # Output as JSON
163
+ npx vbguard . --json
164
+
165
+ # Security score (0-100)
166
+ npx vbguard . --score
167
+
168
+ # Generate fix suggestions file
169
+ npx vbguard . --fix
52
170
 
53
- # Output as JSON (for CI/CD)
54
- vibeguard . --json
171
+ # Only scan git-changed files (fast!)
172
+ npx vbguard . --diff
173
+
174
+ # Watch mode -- re-scans on file changes
175
+ npx vbguard . --watch
176
+
177
+ # SARIF output for GitHub Code Scanning
178
+ npx vbguard . --ci
179
+
180
+ # Skip online checks (hallucinated packages)
181
+ npx vbguard . --offline
55
182
 
56
183
  # Hide fix suggestions
57
- vibeguard . --no-fix
184
+ npx vbguard . --no-fix
58
185
 
59
186
  # Ignore specific directories
60
- vibeguard . --ignore=tests,scripts
187
+ npx vbguard . --ignore=tests,scripts
61
188
  ```
62
189
 
63
190
  ## CI/CD Integration
64
191
 
65
- ### GitHub Actions
192
+ ### GitHub Actions (SARIF)
193
+
194
+ vbguard outputs SARIF format, which integrates directly with GitHub's Security tab:
66
195
 
67
196
  ```yaml
68
- name: Security Scan
197
+ name: vbguard Security Scan
69
198
  on: [push, pull_request]
70
199
 
71
200
  jobs:
72
- vibeguard:
201
+ security-scan:
73
202
  runs-on: ubuntu-latest
203
+ permissions:
204
+ security-events: write
74
205
  steps:
75
206
  - uses: actions/checkout@v4
76
207
  - uses: actions/setup-node@v4
77
208
  with:
78
209
  node-version: '20'
79
- - run: npx vibeguard . --severity=high
210
+ - name: Run vbguard
211
+ run: npx vbguard@latest . --ci --offline > results.sarif || true
212
+ - name: Upload SARIF
213
+ uses: github/codeql-action/upload-sarif@v3
214
+ with:
215
+ sarif_file: results.sarif
216
+ if: always()
80
217
  ```
81
218
 
82
- vibeguard exits with code 1 if critical or high severity issues are found, making it easy to block deploys.
219
+ ### Simple CI (fail on critical/high)
83
220
 
84
- ### Pre-commit Hook
221
+ ```yaml
222
+ - run: npx vbguard . --severity=high --offline
223
+ ```
224
+
225
+ vbguard exits with code 1 if critical or high issues are found, blocking the deploy.
226
+
227
+ ## Pre-Commit Hook
228
+
229
+ ### Option 1: Husky
230
+
231
+ ```bash
232
+ npm install --save-dev husky
233
+ npx husky init
234
+ echo "npx vbguard-precommit" > .husky/pre-commit
235
+ ```
236
+
237
+ ### Option 2: Manual git hook
85
238
 
86
239
  ```bash
87
- # .husky/pre-commit
88
- npx vibeguard . --severity=high
240
+ cat > .git/hooks/pre-commit << 'EOF'
241
+ #!/bin/sh
242
+ npx vbguard-precommit
243
+ EOF
244
+ chmod +x .git/hooks/pre-commit
89
245
  ```
90
246
 
247
+ The pre-commit hook:
248
+ - Only scans **staged files** (fast)
249
+ - Blocks commits with **critical or high** issues
250
+ - Shows exactly what was blocked and why
251
+ - Skips online checks for speed
252
+ - Use `git commit --no-verify` to bypass if needed
253
+
91
254
  ## Example Output
92
255
 
93
256
  ```
94
- vibeguard v0.1.0
257
+ vbguard v0.5.0
95
258
  Security scanner for AI-generated code
96
259
 
97
260
  Scanning: /Users/dev/my-vibe-app
98
261
 
99
- 🚨 CRITICAL (3)
262
+ CRITICAL (3)
100
263
 
101
- secret/openai-api-key
264
+ > secret/openai-api-key
102
265
  src/api/chat.ts:5
103
- Hardcoded OpenAI API Key detected. AI tools commonly inline
104
- credentials this is a top cause of breaches in vibe-coded apps.
105
- 💡 Fix: Move to environment variable OPENAI_API_KEY.
106
-
107
- ▸ frontend/stripe-secret-key-in-client
108
- src/components/Checkout.tsx:12
109
- Stripe Secret Key in Client found in client-side code. This will
110
- be visible to anyone who opens browser DevTools.
111
- 💡 Fix: Stripe secret keys must NEVER be in frontend code.
112
-
113
- dangerous/pickle-deserialization
114
- api/data.py:23
115
- pickle.load() allows arbitrary code execution when deserializing
116
- untrusted data.
117
- 💡 Fix: Use json.loads() for data exchange.
118
-
119
- 🔴 HIGH (2)
120
-
121
- defaults/no-rate-limiting
122
- src/api/server.ts
123
- No rate limiting detected on HTTP server.
124
- 💡 Fix: Add express-rate-limit.
125
-
126
- defaults/permissive-cors
127
- src/api/server.ts:8
128
- CORS is set to allow all origins (*).
129
- 💡 Fix: Set specific origin: cors({ origin: 'https://yourdomain.com' })
130
-
131
- ─────────────────────────────────────────
266
+ Hardcoded OpenAI API Key detected.
267
+ Fix: Move to environment variable OPENAI_API_KEY.
268
+
269
+ > auth/jwt-weak-secret
270
+ src/auth/login.ts:23
271
+ JWT signed with weak secret "password". Anyone can forge tokens.
272
+ Fix: Use a strong random secret from process.env.JWT_SECRET.
273
+
274
+ > hallucinated/npm-package-not-found
275
+ package.json
276
+ Package "react-auth-helper" does not exist on npm.
277
+ Fix: Remove and search for the correct package name.
278
+
279
+ HIGH (2)
280
+
281
+ > vibe/security-todo-left-behind
282
+ src/middleware.ts:12
283
+ "TODO: add authentication before deploying"
284
+ Fix: Implement the security feature now.
285
+
286
+ > auth/token-in-localstorage
287
+ src/hooks/useAuth.ts:45
288
+ Auth token stored in localStorage. Vulnerable to XSS.
289
+ Fix: Use httpOnly cookies instead.
290
+
291
+ -----------------------------------------
132
292
  5 issues found: 3 critical, 2 high
133
293
  Scanned 24 files in 12ms
134
294
 
135
- Fix critical and high severity issues before deploying!
295
+ Fix critical and high severity issues before deploying!
136
296
  ```
137
297
 
138
- ## Why Not Just Use Snyk / Semgrep / SonarQube?
139
-
140
- Those tools are great for traditional code. But they weren't designed for AI-generated code patterns:
141
-
142
- - **Snyk** focuses on dependency vulnerabilities, not hardcoded secrets or missing middleware
143
- - **Semgrep** requires writing custom rules — vibeguard ships with AI-specific patterns out of the box
144
- - **SonarQube** is enterprise-heavy and takes hours to configure
298
+ ## Supported Languages
145
299
 
146
- vibeguard is opinionated, zero-config, and runs in milliseconds. It's built specifically for the patterns that Cursor, Claude Code, Copilot, Lovable, and Bolt introduce.
300
+ - **JavaScript / TypeScript** -- Express, Fastify, Next.js, React, Vue, Svelte
301
+ - **Python** -- Flask, FastAPI, Django
147
302
 
148
303
  ## How It Works
149
304
 
150
- vibeguard uses pattern matching (regex + structural analysis) against a curated ruleset of AI-specific vulnerability patterns. No AI, no API calls, no data leaves your machine. It runs entirely locally.
305
+ vbguard uses pattern matching (regex + structural analysis) against a curated ruleset of AI-specific vulnerability patterns. No AI, no API calls (except optional package registry checks), no data leaves your machine.
151
306
 
152
- The ruleset is based on real-world breaches and academic research:
153
- - The Moltbook breach (Supabase misconfiguration)
307
+ The ruleset is based on real-world breaches and research:
308
+ - The Moltbook breach (Supabase misconfiguration + inverted auth)
154
309
  - Tenzai's 2025 study (69 vulnerabilities across 5 AI coding tools)
155
310
  - Escape.tech's scan of 5,600 vibe-coded apps
156
311
  - Georgia Tech's Vibe Security Radar (tracking AI-generated CVEs)
157
312
 
158
- ## Contributing
159
-
160
- Contributions welcome. If you've found a vulnerability pattern that AI tools commonly introduce, open a PR to add it to the scanner.
313
+ ## Project Structure
161
314
 
162
315
  ```
163
316
  src/scanners/
164
- secrets.js # Hardcoded API keys, tokens, connection strings
165
- dangerous-defaults.js # Missing auth, rate limiting, CORS, headers
166
- dangerous-functions.js # eval, pickle, SQL injection, XSS
167
- exposed-frontend.js # Server secrets in client-side code
168
- permissive-configs.js # Supabase, Firebase, Docker misconfigs
169
- dependencies.js # Compromised/deprecated packages
170
- gitignore.js # Missing .gitignore entries
317
+ secrets.js # Hardcoded API keys, tokens, connection strings
318
+ dangerous-defaults.js # Missing auth, rate limiting, CORS, headers
319
+ dangerous-functions.js # eval, pickle, SQL injection, XSS
320
+ exposed-frontend.js # Server secrets in client-side code
321
+ permissive-configs.js # Docker misconfigs
322
+ dependencies.js # Compromised/deprecated packages
323
+ gitignore.js # Missing .gitignore entries
324
+ hallucinated-packages.js # AI-hallucinated npm/PyPI packages
325
+ auth-flow.js # Broken auth patterns
326
+ vibe-patterns.js # AI-code-specific antipatterns
327
+ nextjs.js # Next.js framework rules
328
+ supabase.js # Supabase security rules
329
+ firebase.js # Firebase security rules
171
330
  ```
172
331
 
332
+ ## Contributing
333
+
334
+ Contributions welcome! If you've found a vulnerability pattern that AI tools commonly introduce, open a PR to add it.
335
+
336
+ 1. Add your pattern to the relevant scanner in `src/scanners/`
337
+ 2. Add a test case in `test/test.js`
338
+ 3. Run `npm test` to verify
339
+ 4. Open a PR with a description of the real-world scenario this catches
340
+
173
341
  ## License
174
342
 
175
343
  MIT
package/package.json CHANGED
@@ -1,10 +1,11 @@
1
1
  {
2
2
  "name": "vbguard",
3
- "version": "0.3.0",
3
+ "version": "0.5.1",
4
4
  "description": "Security scanner for AI-generated code. Catches what traditional scanners miss.",
5
5
  "main": "src/index.js",
6
6
  "bin": {
7
- "vbguard": "./src/run.cmd"
7
+ "vbguard": "src/bin.js",
8
+ "vbguard-precommit": "src/precommit.js"
8
9
  },
9
10
  "scripts": {
10
11
  "start": "node src/cli.js",
@@ -32,4 +33,4 @@
32
33
  "README.md",
33
34
  "LICENSE"
34
35
  ]
35
- }
36
+ }
package/src/bin.js ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ require('./cli.js');
package/src/cli.js CHANGED
@@ -1,6 +1,7 @@
1
+ const fs = require('fs');
1
2
  const path = require('path');
2
3
  const { scanDirectory } = require('./index');
3
- const { formatReport } = require('./reporter');
4
+ const { formatReport, formatSarif, formatScore, formatFixes } = require('./reporter');
4
5
 
5
6
  const args = process.argv.slice(2);
6
7
 
@@ -15,17 +16,33 @@ const HELP = `
15
16
  --severity=X Minimum severity (low, medium, high, critical)
16
17
  --no-fix Hide fix suggestions
17
18
  --ignore=X Comma-separated patterns to ignore
19
+ --offline Skip online checks (hallucinated package detection)
20
+ --fix Generate .vbguard-fixes.md with suggested code changes
21
+ --diff Only scan files changed since last git commit
22
+ --watch Watch for file changes and re-scan automatically
23
+ --ci Output results in SARIF format (for GitHub Code Scanning)
24
+ --score Output a security score (0-100) for the project
18
25
  -h, --help Show this help
19
26
  -v, --version Show version
20
27
  `;
21
28
 
22
29
  function parseArgs(args) {
23
- const opts = { dir: '.', json: false, severity: 'low', showFix: true, ignore: [] };
30
+ const opts = {
31
+ dir: '.', json: false, severity: 'low', showFix: true,
32
+ ignore: [], offline: false, fix: false, diff: false,
33
+ watch: false, ci: false, score: false,
34
+ };
24
35
  for (const arg of args) {
25
36
  if (arg === '-h' || arg === '--help') { console.log(HELP); process.exit(0); }
26
- if (arg === '-v' || arg === '--version') { console.log('0.1.0'); process.exit(0); }
37
+ if (arg === '-v' || arg === '--version') { console.log('0.5.1'); process.exit(0); }
27
38
  if (arg === '--json') opts.json = true;
28
39
  else if (arg === '--no-fix') opts.showFix = false;
40
+ else if (arg === '--offline') opts.offline = true;
41
+ else if (arg === '--fix') opts.fix = true;
42
+ else if (arg === '--diff') opts.diff = true;
43
+ else if (arg === '--watch') opts.watch = true;
44
+ else if (arg === '--ci') opts.ci = true;
45
+ else if (arg === '--score') opts.score = true;
29
46
  else if (arg.startsWith('--severity=')) opts.severity = arg.split('=')[1];
30
47
  else if (arg.startsWith('--ignore=')) opts.ignore = arg.split('=')[1].split(',');
31
48
  else if (!arg.startsWith('-')) opts.dir = arg;
@@ -33,24 +50,86 @@ function parseArgs(args) {
33
50
  return opts;
34
51
  }
35
52
 
53
+ async function runScan(opts) {
54
+ const targetDir = path.resolve(opts.dir);
55
+
56
+ const results = await scanDirectory(targetDir, opts);
57
+
58
+ if (opts.ci) {
59
+ console.log(JSON.stringify(formatSarif(results, targetDir), null, 2));
60
+ } else if (opts.score) {
61
+ formatScore(results);
62
+ } else if (opts.json) {
63
+ console.log(JSON.stringify(results, null, 2));
64
+ } else {
65
+ formatReport(results, opts);
66
+ }
67
+
68
+ if (opts.fix) {
69
+ const fixContent = formatFixes(results, targetDir);
70
+ const fixPath = path.join(targetDir, '.vbguard-fixes.md');
71
+ fs.writeFileSync(fixPath, fixContent);
72
+ console.log(` \x1b[32m📝 Fix suggestions written to .vbguard-fixes.md\x1b[0m\n`);
73
+ }
74
+
75
+ return results;
76
+ }
77
+
36
78
  async function main() {
37
79
  const opts = parseArgs(args);
38
80
  const targetDir = path.resolve(opts.dir);
39
81
 
82
+ if (opts.watch) {
83
+ // Watch mode
84
+ console.log('');
85
+ console.log(' \x1b[1m\x1b[35m⚡ vbguard\x1b[0m \x1b[2mv0.5.1 — watch mode\x1b[0m');
86
+ console.log(` \x1b[2mWatching:\x1b[0m ${targetDir}`);
87
+ console.log('');
88
+
89
+ let debounceTimer = null;
90
+ const doScan = async () => {
91
+ try {
92
+ process.stdout.write('\x1b[2J\x1b[H'); // Clear terminal
93
+ console.log('');
94
+ console.log(' \x1b[1m\x1b[35m⚡ vbguard\x1b[0m \x1b[2mv0.5.1 — watch mode\x1b[0m');
95
+ console.log(` \x1b[2mWatching:\x1b[0m ${targetDir}`);
96
+ console.log('');
97
+ await runScan(opts);
98
+ console.log(' \x1b[2mWatching for changes...\x1b[0m\n');
99
+ } catch (err) {
100
+ console.error(`\x1b[31m Error: ${err.message}\x1b[0m`);
101
+ }
102
+ };
103
+
104
+ await doScan();
105
+ console.log(' \x1b[2mWatching for changes...\x1b[0m\n');
106
+
107
+ const IGNORE = new Set(['node_modules', '.git', '.next', 'dist', 'build', '.cache']);
108
+ fs.watch(targetDir, { recursive: true }, (eventType, filename) => {
109
+ if (!filename) return;
110
+ const parts = filename.split(path.sep);
111
+ if (parts.some(p => IGNORE.has(p))) return;
112
+ if (debounceTimer) clearTimeout(debounceTimer);
113
+ debounceTimer = setTimeout(doScan, 500);
114
+ });
115
+
116
+ return; // Keep process alive
117
+ }
118
+
119
+ // Normal scan
40
120
  console.log('');
41
- console.log(' \x1b[1m\x1b[35m\u26a1 vbguard\x1b[0m \x1b[2mv0.1.0\x1b[0m');
121
+ console.log(' \x1b[1m\x1b[35m vbguard\x1b[0m \x1b[2mv0.5.1\x1b[0m');
42
122
  console.log(' \x1b[2mSecurity scanner for AI-generated code\x1b[0m');
43
123
  console.log('');
44
- console.log(` \x1b[2mScanning:\x1b[0m ${targetDir}`);
124
+ if (opts.diff) {
125
+ console.log(` \x1b[2mScanning changed files in:\x1b[0m ${targetDir}`);
126
+ } else {
127
+ console.log(` \x1b[2mScanning:\x1b[0m ${targetDir}`);
128
+ }
45
129
  console.log('');
46
130
 
47
131
  try {
48
- const results = await scanDirectory(targetDir, opts);
49
- if (opts.json) {
50
- console.log(JSON.stringify(results, null, 2));
51
- } else {
52
- formatReport(results, opts);
53
- }
132
+ const results = await runScan(opts);
54
133
  const hasCritical = results.findings.some(f => f.severity === 'critical' || f.severity === 'high');
55
134
  process.exit(hasCritical ? 1 : 0);
56
135
  } catch (err) {
@@ -59,4 +138,4 @@ async function main() {
59
138
  }
60
139
  }
61
140
 
62
- main();
141
+ main();
package/src/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  const fs = require('fs');
2
2
  const path = require('path');
3
+ const { execSync } = require('child_process');
3
4
 
4
5
  const { scanSecrets } = require('./scanners/secrets');
5
6
  const { scanDangerousDefaults } = require('./scanners/dangerous-defaults');
@@ -8,6 +9,12 @@ const { scanMissingGitignore } = require('./scanners/gitignore');
8
9
  const { scanDangerousFunctions } = require('./scanners/dangerous-functions');
9
10
  const { scanPermissiveConfigs } = require('./scanners/permissive-configs');
10
11
  const { scanDependencies } = require('./scanners/dependencies');
12
+ const { scanHallucinatedPackages } = require('./scanners/hallucinated-packages');
13
+ const { scanAuthFlow } = require('./scanners/auth-flow');
14
+ const { scanVibePatterns } = require('./scanners/vibe-patterns');
15
+ const { scanNextjs, scanNextjsProject } = require('./scanners/nextjs');
16
+ const { scanSupabase } = require('./scanners/supabase');
17
+ const { scanFirebase } = require('./scanners/firebase');
11
18
 
12
19
  const IGNORE_DIRS = new Set([
13
20
  'node_modules', '.git', '.next', '__pycache__', '.venv', 'venv',
@@ -68,6 +75,28 @@ function walkDir(dir, ignore = []) {
68
75
  return files;
69
76
  }
70
77
 
78
+ function getChangedFiles(dir) {
79
+ try {
80
+ // Get both staged and unstaged changed files
81
+ const staged = execSync('git diff --cached --name-only', { cwd: dir, encoding: 'utf-8' }).trim();
82
+ const unstaged = execSync('git diff --name-only', { cwd: dir, encoding: 'utf-8' }).trim();
83
+ const untracked = execSync('git ls-files --others --exclude-standard', { cwd: dir, encoding: 'utf-8' }).trim();
84
+ const all = [staged, unstaged, untracked].filter(Boolean).join('\n');
85
+ return [...new Set(all.split('\n').filter(Boolean))].map(f => path.resolve(dir, f));
86
+ } catch {
87
+ return null; // Not a git repo
88
+ }
89
+ }
90
+
91
+ function getStagedFiles(dir) {
92
+ try {
93
+ const staged = execSync('git diff --cached --name-only', { cwd: dir, encoding: 'utf-8' }).trim();
94
+ return staged ? staged.split('\n').map(f => path.resolve(dir, f)) : [];
95
+ } catch {
96
+ return [];
97
+ }
98
+ }
99
+
71
100
  async function scanDirectory(dir, opts = {}) {
72
101
  const startTime = Date.now();
73
102
 
@@ -75,7 +104,19 @@ async function scanDirectory(dir, opts = {}) {
75
104
  throw new Error(`Directory not found: ${dir}`);
76
105
  }
77
106
 
78
- const files = walkDir(dir, opts.ignore || []);
107
+ let files;
108
+ if (opts.diff) {
109
+ const changed = getChangedFiles(dir);
110
+ if (changed === null) {
111
+ throw new Error('--diff requires a git repository');
112
+ }
113
+ files = changed.filter(f => shouldScanFile(f));
114
+ } else if (opts.staged) {
115
+ const staged = getStagedFiles(dir);
116
+ files = staged.filter(f => shouldScanFile(f));
117
+ } else {
118
+ files = walkDir(dir, opts.ignore || []);
119
+ }
79
120
  const findings = [];
80
121
  let filesScanned = 0;
81
122
 
@@ -103,11 +144,22 @@ async function scanDirectory(dir, opts = {}) {
103
144
  findings.push(...scanExposedFrontend(ctx));
104
145
  findings.push(...scanDangerousFunctions(ctx));
105
146
  findings.push(...scanPermissiveConfigs(ctx));
147
+ findings.push(...scanAuthFlow(ctx));
148
+ findings.push(...scanVibePatterns(ctx));
149
+ findings.push(...scanNextjs(ctx, dir));
150
+ findings.push(...scanSupabase(ctx));
151
+ findings.push(...scanFirebase(ctx));
106
152
  }
107
153
 
108
154
  // Project-level scanners
109
155
  findings.push(...scanMissingGitignore(dir));
110
156
  findings.push(...(await scanDependencies(dir)));
157
+ findings.push(...scanNextjsProject(dir));
158
+
159
+ // Online scanners (can be skipped with --offline)
160
+ if (!opts.offline) {
161
+ findings.push(...(await scanHallucinatedPackages(dir, opts)));
162
+ }
111
163
 
112
164
  // Filter by severity
113
165
  const severityOrder = { critical: 4, high: 3, medium: 2, low: 1 };