guardvibe 3.1.4 → 3.1.5

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.
@@ -47,7 +47,11 @@ export const advancedSecurityRules = [
47
47
  severity: "high",
48
48
  owasp: "A04:2025 Insecure Design",
49
49
  description: "Code reads a value, checks a condition, then updates based on the check — without a database transaction. Two concurrent requests can both pass the check before either writes, leading to double-spending, overselling, or duplicate operations.",
50
- pattern: /(?:findUnique|findFirst|findOne|findById)\s*\([\s\S]{0,200}?\)\s*;?\s*\n[\s\S]{0,300}?if\s*\([\s\S]{0,200}?\)\s*\{[\s\S]{0,500}?(?:\.update\s*\(|\.delete\s*\(|\.decrement|\.increment)(?:(?!\$transaction|\.transaction|BEGIN|SERIALIZABLE|FOR UPDATE|NOWAIT)[\s\S]){0,300}?\}/g,
50
+ // Negative lookahead at the start of the if-body skips the 404-mapping shape
51
+ // (`if (!x) { return …; }`). Without it, the engine's backtracking matched updates
52
+ // that lived OUTSIDE the if-block (within 500-char window after the brace), making
53
+ // every `findUnique → if(!x) 404 → update` admin route a false hit.
54
+ pattern: /(?:findUnique|findFirst|findOne|findById)\s*\([\s\S]{0,200}?\)\s*;?\s*\n[\s\S]{0,300}?if\s*\([\s\S]{0,200}?\)\s*\{(?!\s*(?:return\b|throw\b|res\.\w+\(|response\.\w+\(|next\.\w+\(|NextResponse\.))[\s\S]{0,500}?(?:\.update\s*\(|\.delete\s*\(|\.decrement|\.increment)(?:(?!\$transaction|\.transaction|BEGIN|SERIALIZABLE|FOR UPDATE|NOWAIT)[\s\S]){0,300}?\}/g,
51
55
  languages: ["javascript", "typescript"],
52
56
  fix: "Wrap check-then-act sequences in a database transaction, or use atomic operations (e.g., UPDATE WHERE balance >= amount).",
53
57
  fixCode: '// BAD: race condition\nconst account = await db.account.findUnique({ where: { id } });\nif (account.balance >= 100) {\n await db.account.update({ where: { id }, data: { balance: { decrement: 100 } } });\n}\n\n// GOOD: atomic transaction\nawait db.$transaction(async (tx) => {\n const account = await tx.account.findUnique({ where: { id } });\n if (account.balance < 100) throw new Error("Insufficient");\n await tx.account.update({ where: { id }, data: { balance: { decrement: 100 } } });\n});',
@@ -96,7 +96,12 @@ export const aiToolRuntimeRules = [
96
96
  severity: "high",
97
97
  owasp: "A05:2025 Security Misconfiguration",
98
98
  description: "AI tool / MCP tool parameter schema (`z.enum(...)`, JSON Schema `enum`) is constructed at runtime from user input, fetched data, or a mutable variable. Runtime-mutable schemas defeat the safety guarantees the LLM relies on — an attacker can widen the accepted enum set or inject schema fields by poisoning the input.",
99
- pattern: /(?:z\.enum\s*\(\s*(?!\[\s*["'])(?:[a-zA-Z_$][\w$]*|\.\.\.\w+)|["']enum["']\s*:\s*(?!\[\s*(?:["']|true|false|null|\d))(?:[a-zA-Z_$][\w$]*\b|`[^`]*\$\{))/g,
99
+ // Lowercase-start identifier required: PascalCase (`FraudAlertStatus`) and SCREAMING_SNAKE
100
+ // (`STATUSES`) are TypeScript enum imports / module-level const arrays — compile-time
101
+ // static, not user-mutable. Real attack shape uses lowercase variable names
102
+ // (`allowedActions`, `userActions`, `...userInput`). Template-literal interpolation in
103
+ // the JSON-schema branch (`enum: \`...${x}...\``) stays matched — that IS a real risk.
104
+ pattern: /(?:z\.enum\s*\(\s*(?!\[\s*["'])(?:[a-z_$][\w$]*|\.\.\.[a-z_$]\w*)|["']enum["']\s*:\s*(?!\[\s*(?:["']|true|false|null|\d))(?:[a-z_$][\w$]*\b|`[^`]*\$\{))/g,
100
105
  languages: ["javascript", "typescript"],
101
106
  fix: "Define enum values as static literal arrays in source. Never compute schema enums from runtime data.",
102
107
  fixCode: '// SAFE:\nparameters: z.object({\n action: z.enum(["read", "list"]),\n})\n\n// UNSAFE — user controls allowed actions:\n// parameters: z.object({ action: z.enum(allowedActions) })',
@@ -841,6 +841,8 @@ export function analyzeCode(code, language, framework, filePath, configDir, rule
841
841
  const matched = match[0];
842
842
  if (/\bin\s*:\s*\[/i.test(matched))
843
843
  continue; // where: { x: { in: [...] } }
844
+ if (/\bin\s*:\s*[a-zA-Z_$]/i.test(matched))
845
+ continue; // where: { x: { in: someArray } } — variable-spread is also caller-bounded
844
846
  if (/\b(?:id|[a-zA-Z]+Id)\s*:\s*\{?\s*in\s*:/i.test(matched))
845
847
  continue; // where: { partnerId: { in: ids } }
846
848
  if (/\b(?:id|[a-zA-Z]+Id)\s*:\s*[a-zA-Z_$]/i.test(matched))
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "guardvibe",
3
- "version": "3.1.4",
3
+ "version": "3.1.5",
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",