guardvibe 1.9.6 → 2.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.
@@ -33,7 +33,7 @@ export const apiSecurityRules = [
33
33
  severity: "high",
34
34
  owasp: "API2:2023 Broken Authentication",
35
35
  description: "Next.js Route Handler that performs data operations without any authentication check. API routes are publicly accessible by default.",
36
- pattern: /export\s+(?:async\s+)?function\s+(?:GET|POST|PUT|DELETE|PATCH)\s*\([^)]*\)\s*\{(?:(?!auth\s*\(|getServerSession|currentUser|getUser|requireAuth|requireAdmin|requireRole|isAuthenticated|verifyToken|checkAuth|clerkClient|getToken|session|protect|withAuth)[\s\S]){10,}?(?:prisma|db|supabase|query|fetch|sql)\.\w+/g,
36
+ pattern: /export\s+(?:async\s+)?function\s+(?:GET|POST|PUT|DELETE|PATCH)\s*\([^)]*\)\s*\{(?:(?!auth\s*\(|getServerSession|currentUser|getUser|requireAuth|requireAdmin|requireRole|isAuthenticated|verifyToken|checkAuth|clerkClient|getToken|session|protect|withAuth|verifyAuth|checkPermission|assertAuth|ensureAuth|guardAuth|validateAuth|authorize|checkSession|verifySession|validateSession|ensureAuthenticated)[\s\S]){10,}?(?:prisma|db|supabase|query|fetch|sql)\.\w+/g,
37
37
  languages: ["javascript", "typescript"],
38
38
  fix: "Add authentication at the start of every Route Handler that reads or writes data.",
39
39
  fixCode: 'import { auth } from "@clerk/nextjs/server";\n\nexport async function GET() {\n const { userId } = await auth();\n if (!userId) return new Response("Unauthorized", { status: 401 });\n // ... data access\n}',
@@ -96,7 +96,7 @@ export const apiSecurityRules = [
96
96
  severity: "high",
97
97
  owasp: "API5:2023 Broken Function Level Authorization",
98
98
  description: "Endpoint in /admin or /api/admin path performs operations without verifying admin role or permissions. Any authenticated user could access admin functionality.",
99
- pattern: /(?:\/api\/admin|\/admin)[\s\S]*?export\s+(?:async\s+)?function\s+(?:GET|POST|PUT|DELETE|PATCH)\s*\([^)]*\)\s*\{(?:(?!role|isAdmin|orgRole|permission|requireAdmin|checkRole|adminOnly|org:admin)[\s\S]){10,}?(?:prisma|db|supabase|sql)\.\w+/g,
99
+ pattern: /(?:\/api\/admin|\/admin)[\s\S]*?export\s+(?:async\s+)?function\s+(?:GET|POST|PUT|DELETE|PATCH)\s*\([^)]*\)\s*\{(?:(?!role|isAdmin|orgRole|permission|requireAdmin|checkRole|adminOnly|org:admin|verifyAuth|checkPermission|assertAuth|ensureAuth|authorize)[\s\S]){10,}?(?:prisma|db|supabase|sql)\.\w+/g,
100
100
  languages: ["javascript", "typescript"],
101
101
  fix: "Always verify admin role/permissions in admin endpoints.",
102
102
  fixCode: 'const { userId, orgRole } = await auth();\nif (orgRole !== "org:admin") {\n return new Response("Forbidden", { status: 403 });\n}',
@@ -6,7 +6,7 @@ export const authRules = [
6
6
  severity: "high",
7
7
  owasp: "A01:2025 Broken Access Control",
8
8
  description: "Route handler accesses database without authentication check. Anyone can call this endpoint.",
9
- pattern: /export\s+(?:async\s+)?function\s+(?:GET|POST|PUT|DELETE|PATCH)\s*\([^)]*\)\s*\{(?:(?!auth\s*\(|getServerSession|currentUser|getUser|requireAuth|requireAdmin|isAuthenticated|verifyToken|checkAuth|protect)[\s\S])*?(?:prisma|db|supabase)\.\w+/g,
9
+ pattern: /export\s+(?:async\s+)?function\s+(?:GET|POST|PUT|DELETE|PATCH)\s*\([^)]*\)\s*\{(?:(?!auth\s*\(|getServerSession|currentUser|getUser|requireAuth|requireAdmin|isAuthenticated|verifyToken|checkAuth|protect|verifyAuth|checkPermission|assertAuth|ensureAuth|guardAuth|validateAuth|authorize|checkSession|verifySession|validateSession|ensureAuthenticated|withAuth)[\s\S])*?(?:prisma|db|supabase)\.\w+/g,
10
10
  languages: ["javascript", "typescript"],
11
11
  fix: "Add authentication check at the start of every route handler that accesses data.",
12
12
  fixCode: 'import { auth } from "@clerk/nextjs/server";\n\nexport async function GET() {\n const { userId } = await auth();\n if (!userId) return new Response("Unauthorized", { status: 401 });\n const data = await db.query(...);\n}',
@@ -69,7 +69,7 @@ export const authRules = [
69
69
  severity: "high",
70
70
  owasp: "A01:2025 Broken Access Control",
71
71
  description: "Admin or dashboard route handler does not verify user role or permissions.",
72
- pattern: /(?:\/admin|\/dashboard)[\s\S]*?export\s+(?:async\s+)?function\s+(?:GET|POST|PUT|DELETE|PATCH|default)\s*\([^)]*\)\s*\{(?:(?!role|permission|isAdmin|orgRole|checkRole|requireAdmin|requireRole|adminOnly)[\s\S])*?\}/g,
72
+ pattern: /(?:\/admin|\/dashboard)[\s\S]*?export\s+(?:async\s+)?function\s+(?:GET|POST|PUT|DELETE|PATCH|default)\s*\([^)]*\)\s*\{(?:(?!role|permission|isAdmin|orgRole|checkRole|requireAdmin|requireRole|adminOnly|verifyAuth|checkPermission|assertAuth|ensureAuth|authorize)[\s\S])*?\}/g,
73
73
  languages: ["javascript", "typescript"],
74
74
  fix: "Always verify user roles and permissions in admin routes.",
75
75
  fixCode: 'import { auth } from "@clerk/nextjs/server";\n\nexport async function GET() {\n const { userId, orgRole } = await auth();\n if (orgRole !== "org:admin") {\n return new Response("Forbidden", { status: 403 });\n }\n}',
@@ -19,7 +19,7 @@ export const coreRules = [
19
19
  severity: "critical",
20
20
  owasp: "A01:2025 Broken Access Control",
21
21
  description: "Cloud provider API key or token pattern detected in source code (AWS, GitHub, OpenAI, Stripe).",
22
- pattern: /(?:AKIA[0-9A-Z]{16}|(?:ghp|gho|ghu|ghs|ghr)_[A-Za-z0-9_]{36,}|sk-[A-Za-z0-9]{20,}|sk_live_[A-Za-z0-9]{5,}|rk_live_[A-Za-z0-9]{5,})/g,
22
+ pattern: /(?:AKIA[0-9A-Z]{16}|(?:ghp|gho|ghu|ghs|ghr)_[A-Za-z0-9_]{36,}|sk-(?:proj-)?[A-Za-z0-9\-_]{20,}|sk_live_[A-Za-z0-9]{5,}|rk_live_[A-Za-z0-9]{5,})/g,
23
23
  languages: ["javascript", "typescript", "python", "go", "html", "shell"],
24
24
  fix: "Remove hardcoded keys immediately. Use environment variables or a secrets manager (AWS Secrets Manager, Vault). Rotate any compromised keys.",
25
25
  fixCode: "// Store keys in environment variables\nconst awsKey = process.env.AWS_ACCESS_KEY_ID;\nconst githubToken = process.env.GITHUB_TOKEN;",
@@ -386,4 +386,52 @@ export const coreRules = [
386
386
  fixCode: '// BAD: user input in event handler\n// `<img onerror="${userInput}">`\n\n// GOOD: use addEventListener\nconst img = document.createElement("img");\nimg.addEventListener("error", () => handleError(sanitizedInput));',
387
387
  compliance: ["SOC2:CC7.1"],
388
388
  },
389
+ {
390
+ id: "VG111",
391
+ name: "SSRF via User-Controlled URL",
392
+ severity: "high",
393
+ owasp: "A10:2025 Server-Side Request Forgery",
394
+ description: "User-controlled input is passed directly to fetch(), axios, or http.request() as the URL. Attackers can make the server request internal services (169.254.169.254 for cloud metadata, localhost admin panels, internal APIs) leading to data exfiltration or remote code execution.",
395
+ pattern: /(?:fetch|axios\.(?:get|post|put|delete|patch|request)|got(?:\.(?:get|post|put|delete|patch))?|http\.(?:get|request)|https\.(?:get|request)|urllib\.request\.urlopen)\s*\(\s*(?!["'`]https?:\/\/|["'`]\/|`\$\{process\.env)(?:[a-zA-Z_$]\w*)\s*[,)]/gi,
396
+ languages: ["javascript", "typescript", "python"],
397
+ fix: "Validate URLs against an allowlist of trusted domains. Block private/internal IP ranges (10.x, 172.16-31.x, 192.168.x, 127.x, 169.254.x, ::1). Use a URL parser to check the hostname before making the request.",
398
+ fixCode: '// Validate URL before fetching\nconst ALLOWED_HOSTS = ["api.example.com", "cdn.example.com"];\nconst parsed = new URL(userUrl);\nif (!ALLOWED_HOSTS.includes(parsed.hostname)) {\n throw new Error("Blocked: untrusted host");\n}\n// Also block private IPs\nconst ip = await dns.resolve(parsed.hostname);\nif (isPrivateIP(ip)) throw new Error("Blocked: internal host");\nawait fetch(parsed.href);',
399
+ compliance: ["SOC2:CC7.1", "PCI-DSS:Req6.5.1"],
400
+ },
401
+ {
402
+ id: "VG112",
403
+ name: "XSS via insertAdjacentHTML",
404
+ severity: "high",
405
+ owasp: "A02:2025 Injection",
406
+ description: "insertAdjacentHTML() renders raw HTML strings into the DOM without sanitization, enabling Cross-Site Scripting (XSS). Unlike textContent, this method parses and executes HTML including script tags and event handlers.",
407
+ pattern: /\.insertAdjacentHTML\s*\(\s*["'](?:beforebegin|afterbegin|beforeend|afterend)["']\s*,/gi,
408
+ languages: ["javascript", "typescript"],
409
+ fix: "Use textContent or innerText for plain text. If HTML is needed, sanitize with DOMPurify first.",
410
+ fixCode: '// BAD: XSS risk\nel.insertAdjacentHTML("beforeend", userInput);\n\n// GOOD: plain text\nel.insertAdjacentText("beforeend", userInput);\n\n// GOOD: sanitized HTML\nimport DOMPurify from "dompurify";\nel.insertAdjacentHTML("beforeend", DOMPurify.sanitize(userInput));',
411
+ compliance: ["SOC2:CC7.1", "PCI-DSS:Req6.5.7"],
412
+ },
413
+ {
414
+ id: "VG113",
415
+ name: "XSS/SSRF via XMLHttpRequest",
416
+ severity: "high",
417
+ owasp: "A02:2025 Injection",
418
+ description: "XMLHttpRequest.open() is called with a user-controlled URL. This can lead to SSRF (server-side) or data exfiltration (client-side) if the URL is not validated.",
419
+ pattern: /\.open\s*\(\s*["'](?:GET|POST|PUT|DELETE|PATCH)["']\s*,\s*(?:url|uri|href|endpoint|target|userUrl|inputUrl|src)\b/gi,
420
+ languages: ["javascript", "typescript"],
421
+ fix: "Validate the URL against an allowlist before passing to XMLHttpRequest.open(). Prefer fetch() with URL validation over raw XHR.",
422
+ fixCode: '// Validate URL before XHR\nconst allowed = ["https://api.example.com"];\nconst parsed = new URL(userUrl);\nif (!allowed.some(a => userUrl.startsWith(a))) throw new Error("Blocked");\nxhr.open("GET", parsed.href);',
423
+ compliance: ["SOC2:CC7.1"],
424
+ },
425
+ {
426
+ id: "VG114",
427
+ name: "SQL Injection via Template Literal",
428
+ severity: "critical",
429
+ owasp: "A02:2025 Injection",
430
+ description: "SQL query constructed using JavaScript template literals with interpolated variables. Template literal SQL is just as dangerous as string concatenation — any user-controlled value can break out of the SQL context and inject arbitrary queries.",
431
+ pattern: /(?:query|execute|raw|sql|prepare|QueryRow|QueryContext|db\.run|db\.all|db\.get|connection\.query)\s*\(\s*`[^`]*\$\{/gi,
432
+ languages: ["javascript", "typescript"],
433
+ fix: "Use parameterized queries. For tagged template literals (e.g. Prisma sql``, Drizzle sql``), the tagged version IS safe — only raw template literals passed to query() are dangerous.",
434
+ fixCode: '// BAD: template literal SQL\ndb.query(`SELECT * FROM users WHERE id = \'${userId}\'`);\n\n// GOOD: parameterized query\ndb.query("SELECT * FROM users WHERE id = $1", [userId]);\n\n// ALSO SAFE: tagged template literal (Prisma/Drizzle)\nimport { sql } from "drizzle-orm";\ndb.execute(sql`SELECT * FROM users WHERE id = ${userId}`);',
435
+ compliance: ["SOC2:CC7.1", "PCI-DSS:Req6.5.1"],
436
+ },
389
437
  ];
@@ -30,7 +30,7 @@ export const nextjsRules = [
30
30
  severity: "critical",
31
31
  owasp: "A01:2025 Broken Access Control",
32
32
  description: "Server Action performs data mutations without verifying user authentication. Anyone can invoke Server Actions directly via POST request.",
33
- pattern: /["']use server["'][\s\S]{0,500}?export\s+async\s+function\s+\w+\s*\([^)]*\)\s*\{(?![\s\S]{0,800}?(?:auth\s*\(|getServerSession|currentUser|getUser|requireAuth|clerkClient))/g,
33
+ pattern: /["']use server["'][\s\S]{0,500}?export\s+async\s+function\s+\w+\s*\([^)]*\)\s*\{(?![\s\S]{0,800}?(?:auth\s*\(|getServerSession|currentUser|getUser|requireAuth|requireAdmin|clerkClient|verifyAuth|checkPermission|assertAuth|ensureAuth|authorize|withAuth))/g,
34
34
  languages: ["javascript", "typescript"],
35
35
  fix: "Always verify authentication at the start of every Server Action.",
36
36
  fixCode: '"use server";\nimport { auth } from "@clerk/nextjs/server";\n\nexport async function deleteItem(id: string) {\n const { userId } = await auth();\n if (!userId) throw new Error("Unauthorized");\n}',
@@ -114,7 +114,7 @@ export const nextjsRules = [
114
114
  severity: "medium",
115
115
  owasp: "A01:2025 Broken Access Control",
116
116
  description: "redirect() or NextResponse.redirect() uses user-controlled input (searchParams, query) which can redirect users to malicious sites.",
117
- pattern: /redirect\s*\(\s*(?:searchParams|params|req\.query|request\.url|url|query)\s*[\.\[]/g,
117
+ pattern: /(?:redirect|NextResponse\.redirect|res\.redirect|Response\.redirect)\s*\(\s*(?:searchParams|params|req\.query|request\.url|url|query|returnTo|callbackUrl|next|goto|returnUrl|redirectUrl|destination)\b/gi,
118
118
  languages: ["javascript", "typescript"],
119
119
  fix: "Validate redirect URLs against an allowlist of trusted domains.",
120
120
  fixCode: '// Validate redirect URL\nconst ALLOWED_HOSTS = ["example.com"];\nconst target = searchParams.get("next") ?? "/";\ntry {\n const url = new URL(target, request.url);\n if (!ALLOWED_HOSTS.includes(url.hostname)) redirect("/");\n redirect(url.pathname);\n} catch {\n redirect("/");\n}',
@@ -126,6 +126,53 @@ function isRuleDefinitionFile(code, filePath) {
126
126
  }
127
127
  return false;
128
128
  }
129
+ /**
130
+ * Detect if code contains an auth guard pattern — regardless of function name.
131
+ * Matches patterns like:
132
+ * const { userId } = await someFunction(); if (!userId) return/throw;
133
+ * const { error } = await someFunction(); if (error) return error;
134
+ * const session = await someFunction(); if (!session) throw/return;
135
+ * await someFunction(); // + early return pattern
136
+ *
137
+ * This is naming-agnostic: works for requireAdmin, verifyAuth, checkPermission,
138
+ * ensureLoggedIn, or any custom auth wrapper.
139
+ */
140
+ function hasAuthGuardPattern(code) {
141
+ // Pattern 1: destructured result checked with early return/throw
142
+ // e.g., const { userId } = await xxx(); if (!userId) return;
143
+ // e.g., const { error } = await xxx(); if (error) return error;
144
+ if (/(?:const|let)\s+\{[^}]*\}\s*=\s*await\s+\w+\s*\([^)]*\)\s*;?\s*\n\s*if\s*\(\s*!?\w+/.test(code)) {
145
+ if (/if\s*\([^)]*\)\s*(?:return|throw)\b/.test(code))
146
+ return true;
147
+ }
148
+ // Pattern 2: result assigned then checked
149
+ // e.g., const session = await xxx(); if (!session) return;
150
+ if (/(?:const|let)\s+\w+\s*=\s*await\s+\w+\s*\([^)]*\)\s*;?\s*\n\s*if\s*\(\s*!\w+/.test(code)) {
151
+ return true;
152
+ }
153
+ // Pattern 3: function called with await that contains auth-like keywords in name
154
+ // Broad catch: any function name containing auth/session/permission/guard/verify/protect
155
+ if (/await\s+(?:\w+\.)*\w*(?:auth|Auth|session|Session|permission|Permission|guard|Guard|verify|Verify|protect|Protect|check|Check|ensure|Ensure|require|Require|assert|Assert|authorize|Authorize)\w*\s*\(/i.test(code)) {
156
+ return true;
157
+ }
158
+ return false;
159
+ }
160
+ /**
161
+ * Detect if code has a role/permission check — regardless of function name.
162
+ * Matches: role === "admin", permission check, role-based condition.
163
+ */
164
+ function hasRoleCheckPattern(code) {
165
+ // Direct role/permission comparison
166
+ if (/(?:role|permission|isAdmin|access|level)\s*(?:===|!==|==|!=)\s*["']/i.test(code))
167
+ return true;
168
+ // Function call with role/permission-like args
169
+ if (/(?:check|require|verify|ensure|assert|has|can)\w*\s*\(\s*["'](?:admin|manager|editor|owner|moderator|superadmin)/i.test(code))
170
+ return true;
171
+ // Destructured role check: const { role } = ...; if (role !== "admin")
172
+ if (/\brole\b[\s\S]{0,100}?(?:!==|===)\s*["']/i.test(code))
173
+ return true;
174
+ return false;
175
+ }
129
176
  export function analyzeCode(code, language, framework, filePath, configDir, rules) {
130
177
  // Skip files that are security rule definitions (they intentionally contain
131
178
  // vulnerable code patterns as regex matchers and fixCode examples)
@@ -136,6 +183,15 @@ export function analyzeCode(code, language, framework, filePath, configDir, rule
136
183
  const findings = [];
137
184
  const lines = code.split("\n");
138
185
  const suppressions = parseSuppressionsFromCode(lines);
186
+ // Pre-analyze: detect auth guards and role checks pattern-agnostically
187
+ let codeHasAuthGuard = hasAuthGuardPattern(code);
188
+ const codeHasRoleCheck = hasRoleCheckPattern(code);
189
+ // Config: check custom auth function names from .guardviberc
190
+ if (!codeHasAuthGuard && config.authFunctions && config.authFunctions.length > 0) {
191
+ const customPattern = new RegExp(`(?:${config.authFunctions.join("|")})\\s*\\(`, "i");
192
+ if (customPattern.test(code))
193
+ codeHasAuthGuard = true;
194
+ }
139
195
  const effectiveRules = rules ?? owaspRules;
140
196
  for (const rule of effectiveRules) {
141
197
  if (!rule.languages.includes(language))
@@ -152,25 +208,30 @@ export function analyzeCode(code, language, framework, filePath, configDir, rule
152
208
  continue;
153
209
  if (rule.id.startsWith("VG21") && !filePath && language !== "yaml")
154
210
  continue;
155
- // Context-aware: skip auth rules for webhook routes that have signature verification
211
+ // ── Context-aware rule skipping (pattern-agnostic) ──────────────
212
+ const authRuleIds = new Set(["VG420", "VG952", "VG002", "VG402"]);
213
+ const adminRoleRuleIds = new Set(["VG426", "VG957"]);
214
+ const rateLimitRuleIds = new Set(["VG956", "VG030"]);
156
215
  const isWebhookRoute = filePath && /webhook/i.test(filePath);
216
+ const isCronRoute = filePath && /(?:cron|scheduled|jobs?)\//i.test(filePath);
217
+ const isAdminRoute = filePath && /\/admin\//i.test(filePath);
218
+ // Skip auth rules when code has any auth guard pattern (naming-agnostic)
219
+ if (codeHasAuthGuard && authRuleIds.has(rule.id))
220
+ continue;
221
+ // Skip admin role rules when code has any role/permission check
222
+ if (codeHasRoleCheck && adminRoleRuleIds.has(rule.id))
223
+ continue;
224
+ // Skip auth rules for webhook routes with signature verification
157
225
  const hasSignatureVerification = isWebhookRoute && /(?:verify|signature|hmac|constructEvent|svix|webhookSecret|createHmac|X-Signature|stripe-signature)/i.test(code);
158
- const authRuleIds = new Set(["VG420", "VG952", "VG002"]);
159
226
  if (hasSignatureVerification && authRuleIds.has(rule.id))
160
227
  continue;
161
- // Context-aware: skip rate limiting rules for cron/scheduled routes
162
- const isCronRoute = filePath && /(?:cron|scheduled|jobs?)\//i.test(filePath);
163
- const rateLimitRuleIds = new Set(["VG956", "VG030"]);
228
+ // Skip rate limiting for cron and webhook routes
164
229
  if (isCronRoute && rateLimitRuleIds.has(rule.id))
165
230
  continue;
166
- // Context-aware: skip rate limiting rules for webhook routes
167
- // Webhooks are called by external services, not users — rate limiting is irrelevant
168
231
  if (isWebhookRoute && rateLimitRuleIds.has(rule.id))
169
232
  continue;
170
- // Context-aware: skip rate limiting rules for admin routes that have admin auth
171
- const isAdminRoute = filePath && /\/admin\//i.test(filePath);
172
- const hasAdminAuth = isAdminRoute && /(?:requireAdmin|adminOnly|orgRole|org:admin|isAdmin|checkRole|requireRole)/i.test(code);
173
- if (hasAdminAuth && rateLimitRuleIds.has(rule.id))
233
+ // Skip rate limiting for admin routes with auth guard
234
+ if (isAdminRoute && codeHasAuthGuard && rateLimitRuleIds.has(rule.id))
174
235
  continue;
175
236
  // Skip npm package rules (VG863/VG864/VG865): only apply to package.json files
176
237
  if ((rule.id === "VG863" || rule.id === "VG864" || rule.id === "VG865") && filePath && !filePath.endsWith("package.json"))
@@ -199,7 +260,7 @@ export function analyzeCode(code, language, framework, filePath, configDir, rule
199
260
  : rule;
200
261
  // Context-aware severity: downgrade rate limiting/pagination issues in admin routes
201
262
  // Admin routes behind requireAdmin have lower brute-force risk
202
- if (isAdminRoute && hasAdminAuth) {
263
+ if (isAdminRoute && codeHasAuthGuard) {
203
264
  const downgradeInAdmin = new Set(["VG955"]); // pagination in admin is less critical
204
265
  if (downgradeInAdmin.has(rule.id) && effectiveRule.severity === "medium") {
205
266
  effectiveRule = { ...effectiveRule, severity: "low" };
@@ -22,6 +22,10 @@ export interface GuardVibeConfig {
22
22
  };
23
23
  plugins: string[];
24
24
  compliance?: CompliancePolicy;
25
+ /** Custom auth function names that GuardVibe should recognize as auth guards.
26
+ * e.g. ["requireAdmin", "verifyUser", "ensureLoggedIn"]
27
+ * These are added ON TOP of the built-in pattern-agnostic detection. */
28
+ authFunctions?: string[];
25
29
  }
26
30
  export declare function loadConfig(dir?: string): GuardVibeConfig;
27
31
  export declare function resetConfigCache(): void;
@@ -67,6 +67,7 @@ export function loadConfig(dir) {
67
67
  exceptions: Array.isArray(parsed.compliance.exceptions) ? parsed.compliance.exceptions : [],
68
68
  requiredControls: Array.isArray(parsed.compliance.requiredControls) ? parsed.compliance.requiredControls : undefined,
69
69
  } : undefined,
70
+ authFunctions: Array.isArray(parsed.authFunctions) ? parsed.authFunctions : undefined,
70
71
  };
71
72
  }
72
73
  catch { }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "guardvibe",
3
- "version": "1.9.6",
3
+ "version": "2.0.1",
4
4
  "description": "Security MCP for vibe coding. 277 rules, 24 tools for Next.js, Supabase, Clerk, Stripe, Prisma, tRPC, Hono, GraphQL, Convex, Turso, Uploadthing, AI SDK, and the full AI-generated stack.",
5
5
  "type": "module",
6
6
  "bin": {