guardvibe 3.1.16 → 3.1.18

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.
@@ -260,7 +260,7 @@ export const coreRules = [
260
260
  severity: "medium",
261
261
  owasp: "A05:2025 Security Misconfiguration",
262
262
  description: "Cookies set without secure, httpOnly, or sameSite flags.",
263
- pattern: /(?:cookie|setCookie|set-cookie|res\.cookie)\s*\([^)]*(?!(?:.*secure|.*httpOnly|.*sameSite))/gi,
263
+ pattern: /(?:\bcookie|setCookie|set-cookie|res\.cookie)[ \t]*\([^)]*(?!(?:.*secure|.*httpOnly|.*sameSite))/gi,
264
264
  languages: ["javascript", "typescript"],
265
265
  fix: "Set all security flags: { secure: true, httpOnly: true, sameSite: 'strict' }.",
266
266
  fixCode: "res.cookie('session', token, {\n secure: true,\n httpOnly: true,\n sameSite: 'strict',\n maxAge: 3600000\n});",
@@ -153,7 +153,7 @@ export const nextjsRules = [
153
153
  severity: "high",
154
154
  owasp: "A01:2025 Broken Access Control",
155
155
  description: "Server Action returns a full database query result without field selection. Sensitive fields (passwordHash, internalNotes) get serialized to the client.",
156
- pattern: /["']use server["'][\s\S]{0,800}?(?:return\s+\w+\.)?(?:findUnique|findFirst|findMany)\s*\((?:(?!select\s*:)[\s\S])*?\)/g,
156
+ pattern: /["']use server["'][\s\S]{0,1500}?\breturn\b[^\n;]{0,80}?(?:findUnique|findFirst|findMany)\s*\((?:(?!select\s*:)[\s\S])*?\)/g,
157
157
  languages: ["javascript", "typescript"],
158
158
  fix: "Always use select to return only needed fields from Server Actions.",
159
159
  fixCode: '"use server";\nexport async function getUser(id: string) {\n return prisma.user.findUnique({\n where: { id },\n select: { id: true, name: true, email: true },\n });\n}',
@@ -370,7 +370,7 @@ export function analyzeCode(code, language, framework, filePath, configDir, rule
370
370
  // agent.get('/?q=' + sqlPayload) which match the regex but aren't database calls
371
371
  // - VG042/VG678: HTTP-response/security-header rules (tests don't serve to real users)
372
372
  const isTestFile = filePath && /(?:\.(?:[\w-]+-)?(?:spec|test|e2e|stories|cy)\.(?:ts|tsx|js|jsx|mjs|cjs)$|\/__tests__\/|\/tests?\/|\/cypress\/|\/playwright\/)/i.test(filePath);
373
- if (isTestFile && ["VG001", "VG062", "VG010", "VG011", "VG012", "VG013", "VG014", "VG042", "VG130", "VG678", "VG955", "VG133", "VG1021", "VG409"].includes(rule.id))
373
+ if (isTestFile && ["VG001", "VG062", "VG010", "VG011", "VG012", "VG013", "VG014", "VG042", "VG100", "VG130", "VG678", "VG955", "VG133", "VG1021", "VG409"].includes(rule.id))
374
374
  continue;
375
375
  // VG955 (Missing Pagination on List Endpoint): only fire on actual request-handling
376
376
  // surfaces — API routes, App Router `route.{ts,tsx}`, pages/api, or Server Actions.
@@ -490,6 +490,12 @@ export function analyzeCode(code, language, framework, filePath, configDir, rule
490
490
  // request handler to authorize. Rule still fires inside route handlers and Server Actions.
491
491
  if (rule.id === "VG1008" && isBatchScriptFile)
492
492
  continue;
493
+ // Skip VG961 (z.any/z.unknown) in batch scripts and cron routes — `data: z.any()` and
494
+ // similar opaque fields in migration/seed scripts and cron job payloads are intentional
495
+ // passthroughs (e.g. Tinybird `tb.buildPipe({ parameters: ..., data: z.any() })`),
496
+ // not "validation disabled at the entry point" misuses.
497
+ if (rule.id === "VG961" && (isBatchScriptFile || isCronRoute))
498
+ continue;
493
499
  // Skip VG132 (Missing Request Body Size Limit) on Next.js route handlers and
494
500
  // pages/api endpoints — Next.js/Vercel apply a default 4.5MB body limit at the
495
501
  // platform layer, which is what the rule is checking for.
@@ -836,6 +842,21 @@ export function analyzeCode(code, language, framework, filePath, configDir, rule
836
842
  if (/\b\w*Ref\.current\b/.test(matchedLine))
837
843
  continue;
838
844
  }
845
+ // VG152 (Object Injection via Dynamic Property Access): only fire on bracket-key
846
+ // ASSIGNMENT (`obj[key] = ...`) — that's the prototype-pollution shape. Read-only
847
+ // bracket access (`obj[key]` on RHS, in conditional, in function arg) does not
848
+ // pollute prototypes; even with attacker-controlled key, you only get a read of
849
+ // an existing or undefined property. The rule's pattern triggers on `req.X` etc.
850
+ // upstream + `\w+[key]` somewhere within 100 chars, which fires on completely
851
+ // benign read patterns like `data[key]` inside `for (const key in data)` loops or
852
+ // `DEFAULT_REDIRECTS[key]` lookups against hardcoded constants. The match string
853
+ // ends at `]` (no trailing `=`), so look slightly past match end for the assignment.
854
+ if (rule.id === "VG152") {
855
+ const window = code.slice(match.index, match.index + match[0].length + 10);
856
+ const isAssignment = /\w+\s*\[\s*(?:key|field|prop|name|column|attr|param)\s*\]\s*=(?!=)/.test(window);
857
+ if (!isAssignment)
858
+ continue;
859
+ }
839
860
  // VG126 (Dynamic RegExp from User Input): skip when the variable name signals it has
840
861
  // already been escaped/sanitized (e.g. `escapedElement`, `safeQuery`, `sanitizedInput`).
841
862
  if (rule.id === "VG126") {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "guardvibe",
3
- "version": "3.1.16",
3
+ "version": "3.1.18",
4
4
  "mcpName": "io.github.goklab/guardvibe",
5
5
  "description": "Security MCP for vibe coding. 390 rules, 36 tools, CLI + doctor. Host security, auth coverage mapping, LLM-powered deep scan (IDOR/business logic), taint analysis, +25 AI-native rules (MCP supply-chain, RAG/vector poisoning, agent loop DoS, public-prefix LLM keys, sandbox bypass). Plus Next.js, Supabase, Clerk, Stripe, Prisma, tRPC, Hono, GraphQL, Convex, Turso, Uploadthing, AI SDK, and the full AI-generated stack.",
6
6
  "type": "module",