guardvibe 3.6.0 → 3.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +22 -0
- package/README.md +9 -9
- package/build/data/rules/cve-versions.js +36 -0
- package/build/tools/auth-coverage.d.ts +6 -3
- package/build/tools/auth-coverage.js +111 -27
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,28 @@ All notable changes to GuardVibe are documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [3.8.0] - 2026-06-07
|
|
9
|
+
|
|
10
|
+
### Fixed — auth-coverage no longer crashes on (and now understands) Clerk/Next.js middleware (441 rules / 37 tools)
|
|
11
|
+
- **Crash fix:** the Next.js/Clerk catch-all `config.matcher` contains `]` inside character classes (e.g. `[^?]`), which truncated the old matcher parser and then made `matcherToRegex` throw "Unterminated character class" — so `auth_coverage` errored out on essentially every Clerk app. The matcher array is now parsed string-aware (brackets/commas/escapes inside a pattern are preserved) and matcher-to-regex never throws (it tries path-style and regex-style forms, skipping any it can't compile).
|
|
12
|
+
- **Precision:** when the middleware uses Clerk's `createRouteMatcher([...])`, those patterns are used as the precise protected-route set (a sensitive route outside the list is correctly still reported unprotected) instead of the broad `config.matcher` run-scope.
|
|
13
|
+
- **Fewer false negatives:** a recognizably non-auth middleware (next-intl / i18n / analytics) with a catch-all matcher no longer marks routes as protected. Default remains lenient for everything else, so custom auth middleware still counts.
|
|
14
|
+
- New exported `parseProtectedRouteMatchers`; verified on the corpus (5 real middleware files, 0 crashes). No rule or tool changes (441 / 37).
|
|
15
|
+
|
|
16
|
+
Gate green (build / lint / test / self-audit PASS / A / 0).
|
|
17
|
+
|
|
18
|
+
## [3.7.0] - 2026-06-07
|
|
19
|
+
|
|
20
|
+
### Added — 3 fresh CVE rules from daily intel (438 → 441 rules / 37 tools)
|
|
21
|
+
- The freshness moat in action — June 2026 advisories (past typical model training cutoffs) for mainstream stack libraries, surfaced via `npm run intel`:
|
|
22
|
+
- **VG1085** — DOMPurify XSS via `<selectedcontent>` re-clone (CVE-2026-47423). Only `dompurify` 3.4.4 is affected; 3.4.5 fixes it.
|
|
23
|
+
- **VG1086** — React Router 7 multi-CVE cluster (CVE-2026-33245 RSC `javascript:` XSS, CVE-2026-42211 turbo-stream deserialization → unauth RCE, CVE-2026-42342 + CVE-2026-34077 DoS): `react-router` 7.0.0–7.14.x (fixed 7.15.0) and `@remix-run/server-runtime` 2.10.0–2.17.4 (fixed 2.17.5).
|
|
24
|
+
- **VG1087** — Better Auth device-authorization approval bypass (CVE-2026-45337): `better-auth` 1.6.0–1.6.10 (fixed 1.6.11).
|
|
25
|
+
- **0-FP semver:** patterns only match the genuinely-vulnerable pins — a caret/tilde range that resolves to the fixed patch is NOT flagged (DOMPurify/Better Auth exact-only; React Router exact/tilde, not caret). Validated against the corpus: **0 false positives** across all `package.json` files. 22 new version-range unit tests.
|
|
26
|
+
- Counts updated everywhere (consistency guard enforces 441); CVE-rule count 67 → 70.
|
|
27
|
+
|
|
28
|
+
Gate green (build / lint / test / self-audit PASS / A / 0).
|
|
29
|
+
|
|
8
30
|
## [3.6.0] - 2026-06-07
|
|
9
31
|
|
|
10
32
|
### Fixed — VG120 SSRF false-positive narrowing (sustain 0-FP) (438 rules / 37 tools)
|
package/README.md
CHANGED
|
@@ -9,12 +9,12 @@
|
|
|
9
9
|
> **Security infrastructure your AI can't be.**
|
|
10
10
|
> No matter how good your coding agent gets, it can't know the CVE published after its training cutoff, it can't deterministically guarantee the same check every run, it can't hold your whole repo in context, and it can't objectively review its own code. GuardVibe does all four — the deterministic, post-cutoff-current, whole-repo, author-independent verification layer for AI-written code.
|
|
11
11
|
|
|
12
|
-
- **🗓️ Knows what your AI doesn't.** CVE rules refreshed **daily** from GHSA / OSV.dev / CISA KEV — GuardVibe flags vulnerable dependencies published *after* your model's training cutoff. (
|
|
12
|
+
- **🗓️ Knows what your AI doesn't.** CVE rules refreshed **daily** from GHSA / OSV.dev / CISA KEV — GuardVibe flags vulnerable dependencies published *after* your model's training cutoff. (70 CVE rules, `npm run intel` daily triage.)
|
|
13
13
|
- **🎯 Deterministic, not probabilistic.** Same code = same result, every run (content-hashed). Your AI guesses; GuardVibe doesn't.
|
|
14
14
|
- **🗺️ Sees the whole repo.** Cross-file taint + auth-coverage across every route — catches the unprotected endpoint your agent's narrow context missed.
|
|
15
15
|
- **🔍 An independent second pair of eyes.** The thing that wrote the code can't review itself. GuardVibe is the outside checker on AI-written code — in the loop *while* your AI codes (real-time edit hook), not after.
|
|
16
16
|
|
|
17
|
-
**The security MCP built for vibe coding.**
|
|
17
|
+
**The security MCP built for vibe coding.** 441 security rules, 37 tools covering the entire AI-generated code journey — from first line to production deployment.
|
|
18
18
|
|
|
19
19
|
Works with **Claude Code, Cursor, Gemini CLI, Codex, VS Code (Copilot), Windsurf**, and any MCP-compatible coding agent.
|
|
20
20
|
|
|
@@ -26,11 +26,11 @@ Works with **Claude Code, Cursor, Gemini CLI, Codex, VS Code (Copilot), Windsurf
|
|
|
26
26
|
|
|
27
27
|
Most security tools are built for enterprise security teams. GuardVibe is built for **you** — the developer using AI to build and ship web apps fast.
|
|
28
28
|
|
|
29
|
-
- **
|
|
29
|
+
- **441 security rules, 37 tools** purpose-built for the stacks AI agents generate
|
|
30
30
|
- **Zero setup friction** — `npx guardvibe` and you're scanning
|
|
31
31
|
- **No account required** — runs 100% locally, no API keys, no cloud
|
|
32
32
|
- **Understands your stack** — not generic SAST, but rules that know Next.js, Supabase, Stripe, Clerk, and the tools you actually use
|
|
33
|
-
- **CVE version intelligence** — detects
|
|
33
|
+
- **CVE version intelligence** — detects 70 known vulnerable package versions in package.json, refreshed every day from GHSA / OSV.dev / CISA KEV
|
|
34
34
|
- **AI agent & MCP security** — detects MCP server vulnerabilities, tool-description prompt injection (OWASP MCP Top 10), model-controlled sandbox-disable flags, excessive AI permissions, indirect prompt injection
|
|
35
35
|
- **Auto-fix suggestions** — `fix_code` tool returns concrete patches and structured edits the AI agent can apply mechanically. Coverage: hardcoded credentials → env-var migration; public-prefix LLM keys (`NEXT_PUBLIC_/VITE_/EXPO_PUBLIC_/REACT_APP_`) → prefix removal; CORS wildcards → env allowlist; `dangerouslyAllowBrowser` flags → drop; sandbox bypass flags (`unsafe`/`noSandbox`/`allowEval`) → drop; agent loops → add `maxSteps`; raw-HTML React props → `<ReactMarkdown>`; missing auth checks → insert auth guard; SQL injection → parameterized queries; missing rate limiters / CSRF / security headers → snippet templates.
|
|
36
36
|
- **Pre-commit hook** — block insecure code before it reaches your repo
|
|
@@ -61,10 +61,10 @@ GuardVibe is purpose-built for the AI coding workflow. Traditional tools are exc
|
|
|
61
61
|
| AI/LLM security (prompt injection, MCP, tool abuse) | 68 rules | Experimental/None | None |
|
|
62
62
|
| AI host security (CVE-2025-59536, CVE-2026-21852) | `guardvibe doctor` | Not supported | Not supported |
|
|
63
63
|
| Auto-fix suggestions for AI agents | `fix_code` tool | CLI autofix | Not supported |
|
|
64
|
-
| CVE version detection |
|
|
64
|
+
| CVE version detection | 70 packages, refreshed daily | Extensive | Extensive |
|
|
65
65
|
| Compliance mapping (SOC2, PCI-DSS, HIPAA) | Built-in | Paid tier | None |
|
|
66
66
|
| SARIF CI/CD export | Yes | Yes | Limited |
|
|
67
|
-
| Rule count |
|
|
67
|
+
| Rule count | 441 (focused, 68 AI-native) | 5000+ (broad) | N/A |
|
|
68
68
|
|
|
69
69
|
**When to use GuardVibe:** You're building with AI agents and want security scanning integrated into your coding workflow — no dashboard, no account, no CI setup.
|
|
70
70
|
|
|
@@ -188,7 +188,7 @@ React Native, Expo — AsyncStorage secrets, deep link token exposure, hardcoded
|
|
|
188
188
|
### Firebase
|
|
189
189
|
Firestore security rules, Firebase Admin SDK exposure, storage rules, custom token validation
|
|
190
190
|
|
|
191
|
-
### CVE Version Intelligence (
|
|
191
|
+
### CVE Version Intelligence (70 CVEs, refreshed daily)
|
|
192
192
|
**Frameworks:** Next.js (CVE-2024-34351, CVE-2024-46982, CVE-2025-29927, CVE-2026-23869, CVE-2026-44573 / 44574 / 44575 / 44578 / 44579 / 45109 May 2026 cluster), React + react-server-dom-* (CVE-2025-55182, CVE-2026-23870), Express, Hono pre-4.12.18 cluster, @vitejs/plugin-rsc, Strapi content-type-builder (CVE-2026-22599)
|
|
193
193
|
**Auth:** Clerk middleware bypass (GHSA-vqx2), Clerk `has()` org/billing/reverification bypass (GHSA-w24r), Clerk `clerkFrontendApiProxy` SSRF (CVE-2026-34076), NextAuth.js (2 CVEs), jsonwebtoken
|
|
194
194
|
**ORMs / SQL:** Drizzle SQL identifier injection (CVE-2026-39356) + Drizzle `sql.raw` interpolation (VG1073), MikroORM SQL injection (CVE-2026-44680), Prisma raw-query call-form, Kysely JSON-path traversal (CVE-2026-44635)
|
|
@@ -255,7 +255,7 @@ Malicious postinstall scripts, unpinned GitHub Actions, CI `npm` provenance / `-
|
|
|
255
255
|
|
|
256
256
|
All scanning tools support `format: "json"` for machine-readable output.
|
|
257
257
|
|
|
258
|
-
## Security Rules (
|
|
258
|
+
## Security Rules (441 rules across 25 modules)
|
|
259
259
|
|
|
260
260
|
| Category | Rules | Coverage |
|
|
261
261
|
|----------|-------|----------|
|
|
@@ -274,7 +274,7 @@ All scanning tools support `format: "json"` for machine-readable output.
|
|
|
274
274
|
| AI / LLM Security | 16 | Prompt injection, MCP SSRF, excessive agency, indirect injection |
|
|
275
275
|
| **AI Host Security** | **10** | **CVE-2025-59536 hook injection, CVE-2026-21852 base URL hijack, MCP config audit** |
|
|
276
276
|
| **AI Tool Runtime** | **4** | **MCP tool output sanitization, obfuscated descriptions, safety bypass** |
|
|
277
|
-
| CVE Version Intelligence |
|
|
277
|
+
| CVE Version Intelligence | 30 | Known vulnerable versions in package.json — incl. React Router 7 cluster (CVE-2026-33245/42211/42342), DOMPurify XSS (CVE-2026-47423), Better Auth bypass (CVE-2026-45337), Axios supply-chain backdoor, Clerk middleware bypass (GHSA-vqx2) |
|
|
278
278
|
| Shell / Bash | 5 | Pipe to bash, chmod 777, rm -rf, sudo password |
|
|
279
279
|
| SQL | 4 | DROP/DELETE without WHERE, stacked queries, GRANT ALL |
|
|
280
280
|
| Supply Chain | 16 | Malicious install scripts, lockfile integrity, dependency confusion, typosquat detection |
|
|
@@ -781,4 +781,40 @@ export const cveVersionRules = [
|
|
|
781
781
|
fixCode: '// package.json\n"tinymce": "^8.5.1" // or "^7.9.3" for v7\n\n// Sanitize editor output before persisting/rendering\nimport DOMPurify from "isomorphic-dompurify";\n' + 'const clean = DOMPurify.sanitize(editorHtml);',
|
|
782
782
|
compliance: ["SOC2:CC7.1", "PCI-DSS:Req6.5.7"],
|
|
783
783
|
},
|
|
784
|
+
{
|
|
785
|
+
id: "VG1085",
|
|
786
|
+
name: "DOMPurify XSS via selectedcontent re-clone (CVE-2026-47423 / GHSA-87xg-pxx2-7hvx)",
|
|
787
|
+
severity: "high",
|
|
788
|
+
owasp: "A02:2025 Injection",
|
|
789
|
+
description: "dompurify 3.4.4 can be bypassed via a `<selectedcontent>` re-clone, allowing crafted markup to survive sanitization and execute as XSS. DOMPurify is the go-to HTML sanitizer for user-generated content, so a vulnerable pin silently reopens the exact XSS hole it was added to close. Only 3.4.4 is affected; 3.4.5 fixes it (a caret/tilde range resolves to the fixed patch and is not flagged).",
|
|
790
|
+
pattern: /["']dompurify["']\s*:\s*["']=?3\.4\.4["']/g,
|
|
791
|
+
languages: ["json"],
|
|
792
|
+
fix: "Upgrade dompurify to 3.4.5+: npm install dompurify@latest. Keep sanitizing on the server side too — never trust client-only sanitization for stored content.",
|
|
793
|
+
fixCode: '// package.json\n"dompurify": "^3.4.5" // or latest',
|
|
794
|
+
compliance: ["SOC2:CC7.1", "PCI-DSS:Req6.5.7"],
|
|
795
|
+
},
|
|
796
|
+
{
|
|
797
|
+
id: "VG1086",
|
|
798
|
+
name: "React Router 7 multi-CVE cluster — XSS / deserialization RCE / DoS (CVE-2026-33245 / 42211 / 42342 / 34077)",
|
|
799
|
+
severity: "high",
|
|
800
|
+
owasp: "A08:2025 Software and Data Integrity Failures",
|
|
801
|
+
description: "react-router 7.0.0–7.14.x (and @remix-run/server-runtime 2.10.0–2.17.4) ship a cluster of server-side vulnerabilities: XSS via `javascript:` targets in RSC redirect handling, unauthenticated RCE via the vendored turbo-stream v2 TYPE_ERROR deserialization (arbitrary constructor invocation), and two denial-of-service vectors (unbounded path expansion in the `__manifest` endpoint and reflected input in single-fetch). React Router 7 powers a large share of modern React/Remix apps. Fixed across the 7.13.2–7.15.0 line (use 7.15.0+) and @remix-run/server-runtime 2.17.5+. Exact and tilde pins in the affected range are flagged; a caret range resolves to a fixed release.",
|
|
802
|
+
pattern: /(?:["']react-router["']\s*:\s*["'](?:=|~)?7\.(?:[0-9]|1[0-4])\.\d+["']|["']@remix-run\/server-runtime["']\s*:\s*["'](?:=|~)?2\.(?:1[0-6]\.\d+|17\.[0-4])["'])/g,
|
|
803
|
+
languages: ["json"],
|
|
804
|
+
fix: "Upgrade react-router to 7.15.0+ (npm install react-router@latest) and @remix-run/server-runtime to 2.17.5+. After upgrading, reject non-http(s) redirect targets and keep request size/path limits in front of the app.",
|
|
805
|
+
fixCode: '// package.json\n"react-router": "^7.15.0"\n// or, for Remix:\n"@remix-run/server-runtime": "^2.17.5"',
|
|
806
|
+
compliance: ["SOC2:CC7.1", "PCI-DSS:Req6.5.1"],
|
|
807
|
+
},
|
|
808
|
+
{
|
|
809
|
+
id: "VG1087",
|
|
810
|
+
name: "Better Auth device-authorization approval bypass (CVE-2026-45337 / GHSA-cq3f-vc6p-68fh)",
|
|
811
|
+
severity: "high",
|
|
812
|
+
owasp: "A07:2025 Identification and Authentication Failures",
|
|
813
|
+
description: "better-auth 1.6.0–1.6.10 lets the device-authorization approve/deny endpoints accept ANY authenticated session while a user code is pending, so an attacker who gets a victim signed in can have their own device-login flow approved against the victim's account. Better Auth is increasingly common in Next.js / AI-app stacks. Fixed in 1.6.11 (a caret/tilde range resolves to the fixed patch and is not flagged).",
|
|
814
|
+
pattern: /["']better-auth["']\s*:\s*["']=?1\.6\.(?:[0-9]|10)["']/g,
|
|
815
|
+
languages: ["json"],
|
|
816
|
+
fix: "Upgrade better-auth to 1.6.11+: npm install better-auth@latest. Verify device-authorization approval is bound to the session that initiated the user code.",
|
|
817
|
+
fixCode: '// package.json\n"better-auth": "^1.6.11" // or latest',
|
|
818
|
+
compliance: ["SOC2:CC6.6", "PCI-DSS:Req6.5.10"],
|
|
819
|
+
},
|
|
784
820
|
];
|
|
@@ -18,11 +18,14 @@ export interface FileEntry {
|
|
|
18
18
|
* Enumerate all routes from a set of app directory files.
|
|
19
19
|
*/
|
|
20
20
|
export declare function enumerateRoutes(files: FileEntry[]): RouteInfo[];
|
|
21
|
+
export declare function parseMiddlewareMatchers(content: string): string[];
|
|
21
22
|
/**
|
|
22
|
-
*
|
|
23
|
-
*
|
|
23
|
+
* Clerk-style protect lists: `createRouteMatcher([...])`. When present these are the
|
|
24
|
+
* precise routes the middleware enforces auth on — more accurate than config.matcher
|
|
25
|
+
* (which only says where the middleware *runs*), so a sensitive route outside the
|
|
26
|
+
* protect list is correctly still reported as unprotected.
|
|
24
27
|
*/
|
|
25
|
-
export declare function
|
|
28
|
+
export declare function parseProtectedRouteMatchers(content: string): string[];
|
|
26
29
|
/**
|
|
27
30
|
* Check if a route URL path matches any of the middleware matchers.
|
|
28
31
|
* Empty matchers = middleware covers all routes.
|
|
@@ -82,35 +82,108 @@ export function enumerateRoutes(files) {
|
|
|
82
82
|
* Parse Next.js middleware config.matcher from middleware file content.
|
|
83
83
|
* Returns array of matcher patterns.
|
|
84
84
|
*/
|
|
85
|
+
function stripComments(content) {
|
|
86
|
+
return content
|
|
87
|
+
.replace(/\\n/g, "\n").replace(/\\t/g, "\t")
|
|
88
|
+
.replace(/\/\*[\s\S]*?\*\//g, "")
|
|
89
|
+
.replace(/\/\/.*$/gm, "");
|
|
90
|
+
}
|
|
91
|
+
/** Inner text of the array starting at `[` at `openIdx`, scanning string-aware so
|
|
92
|
+
* a `]` inside a string literal (e.g. the catch-all `[^?]`) doesn't end it early. */
|
|
93
|
+
function bracketInner(s, openIdx) {
|
|
94
|
+
let depth = 0;
|
|
95
|
+
for (let i = openIdx; i < s.length; i++) {
|
|
96
|
+
const ch = s[i];
|
|
97
|
+
if (ch === '"' || ch === "'" || ch === "`") {
|
|
98
|
+
const q = ch;
|
|
99
|
+
i++;
|
|
100
|
+
while (i < s.length && s[i] !== q) {
|
|
101
|
+
if (s[i] === "\\")
|
|
102
|
+
i++;
|
|
103
|
+
i++;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
else if (ch === "[") {
|
|
107
|
+
depth++;
|
|
108
|
+
}
|
|
109
|
+
else if (ch === "]") {
|
|
110
|
+
depth--;
|
|
111
|
+
if (depth === 0)
|
|
112
|
+
return s.slice(openIdx + 1, i);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
/** A matcher written in JS source escapes regex backslashes (`\\.`); collapse one
|
|
118
|
+
* level so the extracted pattern is a usable regex (`\.`). */
|
|
119
|
+
function unescapeMatcher(s) {
|
|
120
|
+
return s.replace(/\\\\/g, "\\");
|
|
121
|
+
}
|
|
122
|
+
/** Every quoted string literal inside a region (handles `]`, `,`, escapes within). */
|
|
123
|
+
function extractStringLiterals(region) {
|
|
124
|
+
const out = [];
|
|
125
|
+
const re = /(["'`])((?:\\.|(?!\1)[\s\S])*?)\1/g;
|
|
126
|
+
let m;
|
|
127
|
+
while ((m = re.exec(region)) !== null)
|
|
128
|
+
out.push(unescapeMatcher(m[2]));
|
|
129
|
+
return out;
|
|
130
|
+
}
|
|
85
131
|
export function parseMiddlewareMatchers(content) {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
//
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
return arrayMatch[1]
|
|
99
|
-
.split(",")
|
|
100
|
-
.map(s => s.trim().replace(/^["']|["']$/g, ""))
|
|
101
|
-
.filter(Boolean);
|
|
132
|
+
const normalized = stripComments(content);
|
|
133
|
+
// Array form: matcher: [ ... ] — bound the array string-aware so a catch-all
|
|
134
|
+
// pattern containing `]`/`,` (e.g. Clerk's `[^?]`) isn't truncated.
|
|
135
|
+
const arrM = /matcher\s*:\s*\[/.exec(normalized);
|
|
136
|
+
if (arrM) {
|
|
137
|
+
const openIdx = normalized.indexOf("[", arrM.index);
|
|
138
|
+
const inner = bracketInner(normalized, openIdx);
|
|
139
|
+
if (inner !== null) {
|
|
140
|
+
const lits = extractStringLiterals(inner);
|
|
141
|
+
if (lits.length)
|
|
142
|
+
return lits;
|
|
143
|
+
}
|
|
102
144
|
}
|
|
145
|
+
// String form: matcher: "..."
|
|
146
|
+
const strM = /matcher\s*:\s*(["'`])((?:\\.|(?!\1).)*)\1/.exec(normalized);
|
|
147
|
+
if (strM)
|
|
148
|
+
return [unescapeMatcher(strM[2])];
|
|
103
149
|
return [];
|
|
104
150
|
}
|
|
105
151
|
/**
|
|
106
|
-
*
|
|
107
|
-
*
|
|
152
|
+
* Clerk-style protect lists: `createRouteMatcher([...])`. When present these are the
|
|
153
|
+
* precise routes the middleware enforces auth on — more accurate than config.matcher
|
|
154
|
+
* (which only says where the middleware *runs*), so a sensitive route outside the
|
|
155
|
+
* protect list is correctly still reported as unprotected.
|
|
156
|
+
*/
|
|
157
|
+
export function parseProtectedRouteMatchers(content) {
|
|
158
|
+
const normalized = stripComments(content);
|
|
159
|
+
const out = [];
|
|
160
|
+
const callRe = /createRouteMatcher\s*\(\s*\[/g;
|
|
161
|
+
let m;
|
|
162
|
+
while ((m = callRe.exec(normalized)) !== null) {
|
|
163
|
+
const openIdx = normalized.indexOf("[", m.index);
|
|
164
|
+
const inner = bracketInner(normalized, openIdx);
|
|
165
|
+
if (inner !== null)
|
|
166
|
+
out.push(...extractStringLiterals(inner));
|
|
167
|
+
}
|
|
168
|
+
return out;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Convert a Next.js matcher to a regex. Path-style matchers (`/x/:id`, `/y/:path*`)
|
|
172
|
+
* get token conversion; regex-style matchers (Clerk catch-all, `(.*)`, char classes)
|
|
173
|
+
* are used raw. Tries the likely form first, falls back to the other, and NEVER throws
|
|
174
|
+
* on a malformed pattern (returns null, which callers skip).
|
|
108
175
|
*/
|
|
109
176
|
function matcherToRegex(pattern) {
|
|
110
|
-
const
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
177
|
+
const pathConvert = (p) => p.replace(/\/:[\w]+\*/g, "(?:/.*)?").replace(/:[\w]+/g, "[^/]+");
|
|
178
|
+
const looksRegex = /[(|]|\.\*|\\[dwsDWS]|\[\^?/.test(pattern);
|
|
179
|
+
const candidates = looksRegex ? [pattern, pathConvert(pattern)] : [pathConvert(pattern), pattern];
|
|
180
|
+
for (const c of candidates) {
|
|
181
|
+
try {
|
|
182
|
+
return new RegExp("^" + c + "$");
|
|
183
|
+
}
|
|
184
|
+
catch { /* try next form */ }
|
|
185
|
+
}
|
|
186
|
+
return null;
|
|
114
187
|
}
|
|
115
188
|
/**
|
|
116
189
|
* Check if a route URL path matches any of the middleware matchers.
|
|
@@ -121,7 +194,7 @@ export function routeMatchesMatcher(urlPath, matchers) {
|
|
|
121
194
|
return true;
|
|
122
195
|
for (const pattern of matchers) {
|
|
123
196
|
const regex = matcherToRegex(pattern);
|
|
124
|
-
if (regex.test(urlPath))
|
|
197
|
+
if (regex && regex.test(urlPath))
|
|
125
198
|
return true;
|
|
126
199
|
}
|
|
127
200
|
return false;
|
|
@@ -154,8 +227,19 @@ function hasAuthGuard(code) {
|
|
|
154
227
|
*/
|
|
155
228
|
export function analyzeAuthCoverage(routeFiles, middlewareContent, layoutFiles, authExceptions) {
|
|
156
229
|
const routes = enumerateRoutes(routeFiles);
|
|
157
|
-
const matchers = parseMiddlewareMatchers(middlewareContent);
|
|
158
230
|
const hasMiddleware = middlewareContent.length > 0;
|
|
231
|
+
// Default-lenient: a middleware with a matcher counts as protection — EXCEPT when it
|
|
232
|
+
// is recognizably a non-auth middleware (i18n / analytics) with no auth signal, which
|
|
233
|
+
// must not mark routes protected (that would hide genuinely unprotected routes).
|
|
234
|
+
const hasAuthSignal = hasAuthGuard(middlewareContent) ||
|
|
235
|
+
/\b(?:clerkMiddleware|authMiddleware|withAuth|createRouteMatcher|NextAuth|auth0|betterAuth|supabaseMiddleware|updateSession|createServerClient|getToken)\b/.test(middlewareContent) ||
|
|
236
|
+
/auth\s*\.\s*protect\s*\(/.test(middlewareContent);
|
|
237
|
+
const isNonAuthMiddleware = /\b(?:next-intl|createI18nMiddleware|next-international|paraglide|@vercel\/analytics|posthog)\b/.test(middlewareContent)
|
|
238
|
+
|| /from\s+["']next-intl\/middleware["']/.test(middlewareContent);
|
|
239
|
+
const middlewareCountsAsAuth = hasMiddleware && (hasAuthSignal || !isNonAuthMiddleware);
|
|
240
|
+
// Prefer the precise Clerk protect list; fall back to where the (auth) middleware runs.
|
|
241
|
+
const protectMatchers = parseProtectedRouteMatchers(middlewareContent);
|
|
242
|
+
const coverageMatchers = protectMatchers.length ? protectMatchers : parseMiddlewareMatchers(middlewareContent);
|
|
159
243
|
// Map file content by path for auth detection
|
|
160
244
|
const contentByPath = new Map();
|
|
161
245
|
for (const f of routeFiles)
|
|
@@ -167,9 +251,9 @@ export function analyzeAuthCoverage(routeFiles, middlewareContent, layoutFiles,
|
|
|
167
251
|
route.hasAuthGuard = hasAuthGuard(content);
|
|
168
252
|
if (route.hasAuthGuard)
|
|
169
253
|
route.protectionSource = "auth-guard";
|
|
170
|
-
// Middleware coverage
|
|
171
|
-
if (
|
|
172
|
-
route.middlewareCovered = routeMatchesMatcher(route.urlPath,
|
|
254
|
+
// Middleware coverage (skipped for recognizably non-auth middleware)
|
|
255
|
+
if (middlewareCountsAsAuth) {
|
|
256
|
+
route.middlewareCovered = routeMatchesMatcher(route.urlPath, coverageMatchers);
|
|
173
257
|
if (route.middlewareCovered) {
|
|
174
258
|
middlewareCoveredCount++;
|
|
175
259
|
if (route.protectionSource === "none")
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "guardvibe",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.8.0",
|
|
4
4
|
"mcpName": "io.github.goklab/guardvibe",
|
|
5
|
-
"description": "Security infrastructure your AI can't be — deterministic, current past your model's training cutoff, whole-repo-aware, author-independent. Security MCP for vibe coding.
|
|
5
|
+
"description": "Security infrastructure your AI can't be — deterministic, current past your model's training cutoff, whole-repo-aware, author-independent. Security MCP for vibe coding. 441 rules, 37 tools, CLI + doctor. Host security, auth coverage mapping, LLM-powered deep scan (IDOR/business logic), taint analysis. 70 CVE rules refreshed daily from GHSA/OSV/CISA KEV — React Router 7 cluster, DOMPurify XSS, Better Auth bypass, Miasma @redhat-cloud-services compromise, Next.js May 2026 13-advisory cluster, Drizzle/MikroORM/Kysely SQL injection, Axios proxy-auth redirect leak, Hono setCookie attribute injection, Clerk SSRF, tRPC prototype pollution, @tanstack supply-chain, node-ipc protestware, OpenClaude sandbox bypass, plus the full AI-generated stack (Supabase, Stripe, Prisma, Hono, GraphQL, Convex, Turso, Uploadthing, AI SDK). 68 AI-native rules including OWASP MCP Top 10 tool-description prompt injection (VG1068), model-controlled sandbox-disable flag detection (VG1063), Session messenger exfil endpoint IOC (VG1075), and CI/CD supply-chain hardening (VG1070 npm --expect-provenance / --ignore-scripts enforcement).",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"bin": {
|
|
8
8
|
"guardvibe": "build/cli.js",
|