guardvibe 3.1.20 → 3.1.21

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.
@@ -296,7 +296,7 @@ export const coreRules = [
296
296
  severity: "high",
297
297
  owasp: "A02:2025 Injection",
298
298
  description: "Deep merge or object assignment from user input can lead to prototype pollution.",
299
- pattern: /(?:Object\.assign|merge|deepMerge|extend)\s*\([^)]*(?:req\.|request\.|body|params)/gi,
299
+ pattern: /(?:Object\.assign|\bmerge\b|deepMerge|\bextend\b)\s*\([^)]*(?:req\.|request\.|\bbody\b|\bparams\b)/gi,
300
300
  languages: ["javascript", "typescript"],
301
301
  fix: "Use Object.create(null) for lookup objects. Validate that keys don't include __proto__, constructor, or prototype.",
302
302
  fixCode: "// Use Object.create(null) for lookups\nconst lookup = Object.create(null);\n// Validate keys\nconst forbidden = ['__proto__', 'constructor', 'prototype'];\nif (forbidden.includes(key)) throw new Error('Invalid key');",
@@ -313,7 +313,7 @@ export function analyzeCode(code, language, framework, filePath, configDir, rule
313
313
  /import\s+.*(?:verifyCron|cronAuth|validateCron|checkCron)/i.test(code);
314
314
  const codeHasRedirectValidation = /(?:sanitize|validate|verify|check|safe|allowed)(?:Redirect|RedirectUrl|CallbackUrl)\s*\(/i.test(code) ||
315
315
  /import\s+.*(?:sanitizeRedirect|validateRedirect|safeRedirect)/i.test(code);
316
- const isMigrationFile = filePath ? /(?:migrations?|supabase\/migrations|seeds?|fixtures)\//i.test(filePath) : false;
316
+ const isMigrationFile = filePath ? /(?:\/(?:migrations?|migrate|drizzle|seeds?|fixtures)\/|supabase\/migrations\/)/i.test(filePath) : false;
317
317
  const isSqlSchemaFile = filePath ? /(?:schema|migration|seed|ddl|init).*\.sql$/i.test(filePath) : false;
318
318
  const isReactNative = /(?:react-native|from\s+['"]react-native['"]|from\s+['"]expo|import\s+.*\bexpo\b)/i.test(code);
319
319
  const codeHasTimingSafeEqual = /(?:timingSafeEqual|timing.?safe|constant.?time)/i.test(code);
@@ -369,8 +369,8 @@ export function analyzeCode(code, language, framework, filePath, configDir, rule
369
369
  // - VG010/VG011/VG013/VG014: injection rules trigger on payload strings like
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
- 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", "VG100", "VG130", "VG678", "VG955", "VG133", "VG1021", "VG409"].includes(rule.id))
372
+ const isTestFile = filePath && /(?:\.(?:[\w-]+-)?(?:spec|test|e2e|stories|cy)\.(?:ts|tsx|js|jsx|mjs|cjs)$|_test\.go$|\/__tests__\/|\/tests?\/|\/cypress\/|\/playwright\/|\/dockertest\/|\/testutil\/|\/testhelpers?\/|\/testfixtures?\/)/i.test(filePath);
373
+ if (isTestFile && ["VG001", "VG003", "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.
@@ -416,7 +416,7 @@ export function analyzeCode(code, language, framework, filePath, configDir, rule
416
416
  const isAdminRoute = filePath && /\/admin\//i.test(filePath);
417
417
  // Server-side batch context: scripts, migrations, seeds. These run offline or
418
418
  // on-deploy, not against user requests, so DoS-from-unbounded-results doesn't apply.
419
- const isBatchScriptFile = filePath && /\/(?:scripts?|migrations?|seeds?|fixtures?)\//i.test(filePath);
419
+ const isBatchScriptFile = filePath && /\/(?:scripts?|migrations?|seeds?|fixtures?|benchmarks?)\//i.test(filePath);
420
420
  // Code-generator/scaffold templates. CLI tools (create-t3-app, create-next-app,
421
421
  // create-react-app, etc.) bundle "Hello World" example files under cli/template/
422
422
  // or templates/ that are intentionally minimal — no auth, no input validation,
@@ -497,6 +497,14 @@ export function analyzeCode(code, language, framework, filePath, configDir, rule
497
497
  // request handler to authorize. Rule still fires inside route handlers and Server Actions.
498
498
  if (rule.id === "VG1008" && isBatchScriptFile)
499
499
  continue;
500
+ // Skip VG124 (Insecure Random for Security Token) in benchmark/seed/script files —
501
+ // Math.random() in benchmarks (`keys[Math.floor(Math.random() * keys.length)]` to pick
502
+ // a random test key) and seeders (`storeEncryptedKeys: Math.random() > 0.7` for fixture
503
+ // distribution) is intentional fixture randomness, not a security token. Real tokens
504
+ // generated in scripts should still use crypto.randomBytes — but the rule's keyword list
505
+ // (`token|key|code|...`) over-matches non-security `key` references in test code.
506
+ if (rule.id === "VG124" && isBatchScriptFile)
507
+ continue;
500
508
  // Skip tRPC educational/scaffold rules (VG970 publicProcedure-DB, VG971 missing-input)
501
509
  // in template/scaffold files. CLI tools like create-t3-app ship intentionally simple
502
510
  // examples under cli/template/ that the user is expected to replace before deploying.
@@ -854,6 +862,23 @@ export function analyzeCode(code, language, framework, filePath, configDir, rule
854
862
  if (/\b\w*Ref\.current\b/.test(matchedLine))
855
863
  continue;
856
864
  }
865
+ // VG1021 (AI Tool Schema Enum from User Input): skip when the variable passed to
866
+ // z.enum / "enum": is declared as a static literal array (`const X = [...] as const`,
867
+ // `const X = [...]`) elsewhere in the file. The existing pattern's lowercase-identifier
868
+ // heuristic correctly catches `userActions` style variables, but Zod's idiomatic pattern
869
+ // `const commonStringOperators = ["is", "contains"] as const; z.enum(commonStringOperators)`
870
+ // also has a lowercase-start name and is fully compile-time-static.
871
+ if (rule.id === "VG1021") {
872
+ const varMatch = match[0].match(/(?:z\.enum\s*\(\s*(?:\.\.\.)?|enum["']\s*:\s*)([a-z_$][\w$]*)/);
873
+ if (varMatch) {
874
+ const varName = varMatch[1];
875
+ // Detect: `const NAME = [` (literal array) or `const NAME: SomeType = [`. The array
876
+ // can span multiple lines so just check for the `[` on the assignment.
877
+ const declRe = new RegExp(`(?:const|let|var)\\s+${varName}\\s*(?::\\s*[\\w<>[\\],\\s|.]+)?\\s*=\\s*\\[`);
878
+ if (declRe.test(code))
879
+ continue;
880
+ }
881
+ }
857
882
  // VG850 (AI Prompt Injection via User Input): only fire when at least one of the
858
883
  // interpolations in the system-prompt template literal looks like user input. The
859
884
  // pattern matches any `${...}` interpolation, but apps commonly compose system
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "guardvibe",
3
- "version": "3.1.20",
3
+ "version": "3.1.21",
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",