guardvibe 3.0.39 → 3.0.41

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.
@@ -8,7 +8,7 @@ export const advancedSecurityRules = [
8
8
  severity: "high",
9
9
  owasp: "A02:2025 Injection",
10
10
  description: "User input is interpolated into HTTP response headers. Attackers can inject CRLF characters to add arbitrary headers (Set-Cookie, Location) or split the response.",
11
- pattern: /(?:setHeader|set|append|headers\.set)\s*\(\s*["'][^"']+["']\s*,\s*(?:`[^`]*\$\{|[^"']*\+\s*(?:req\.|request\.|params\.|query\.|searchParams|input|body|user))/gi,
11
+ pattern: /(?:res(?:ponse)?\.(?:setHeader|set|append)|headers\.(?:set|append)|setHeader|appendHeader)\s*\(\s*["'][^"']+["']\s*,\s*(?:`[^`]*\$\{|[^"']*\+\s*(?:req\.|request\.|params\.|query\.|searchParams|input|body|user))/gi,
12
12
  languages: ["javascript", "typescript"],
13
13
  fix: "Never interpolate user input into response headers. Sanitize by removing \\r and \\n characters.",
14
14
  fixCode: '// Sanitize header values\nconst safeValue = userInput.replace(/[\\r\\n]/g, "");\nres.setHeader("Content-Disposition", `attachment; filename="${safeValue}"`);',
@@ -321,8 +321,8 @@ export function analyzeCode(code, language, framework, filePath, configDir, rule
321
321
  // - VG010/VG011/VG013/VG014: injection rules trigger on payload strings like
322
322
  // agent.get('/?q=' + sqlPayload) which match the regex but aren't database calls
323
323
  // - VG042/VG678: HTTP-response/security-header rules (tests don't serve to real users)
324
- const isTestFile = filePath && /(?:\.(?:spec|test|e2e|stories)\.(?:ts|tsx|js|jsx|mjs|cjs)$|\/__tests__\/|\/tests?\/|\/cypress\/|\/playwright\/)/i.test(filePath);
325
- if (isTestFile && ["VG001", "VG062", "VG010", "VG011", "VG013", "VG014", "VG042", "VG678"].includes(rule.id))
324
+ const isTestFile = filePath && /(?:\.(?:[\w-]+-)?(?:spec|test|e2e|stories|cy)\.(?:ts|tsx|js|jsx|mjs|cjs)$|\/__tests__\/|\/tests?\/|\/cypress\/|\/playwright\/)/i.test(filePath);
325
+ if (isTestFile && ["VG001", "VG062", "VG010", "VG011", "VG013", "VG014", "VG042", "VG130", "VG678"].includes(rule.id))
326
326
  continue;
327
327
  // Skip Expo-specific rule (VG708) when project is not an Expo app.
328
328
  // The rule's regex incorrectly matches the literal strings "app.json"/"app.config.ts"
@@ -378,6 +378,11 @@ export function analyzeCode(code, language, framework, filePath, configDir, rule
378
378
  // for batch processing, not for serving to clients.
379
379
  if (rule.id === "VG955" && (isBatchScriptFile || isCronRoute))
380
380
  continue;
381
+ // Skip VG132 (Missing Request Body Size Limit) on Next.js route handlers and
382
+ // pages/api endpoints — Next.js/Vercel apply a default 4.5MB body limit at the
383
+ // platform layer, which is what the rule is checking for.
384
+ if (rule.id === "VG132" && filePath && /(?:\/route\.(?:ts|tsx|js|jsx)$|\/pages\/api\/)/i.test(filePath))
385
+ continue;
381
386
  // Skip VG955 in bulk-* server actions (bulk-archive, bulk-approve, bulk-ban etc.)
382
387
  // These intentionally process a caller-provided list of IDs.
383
388
  if (rule.id === "VG955" && filePath && /\/bulk-[\w-]+\.(?:ts|tsx|js|jsx)$/i.test(filePath))
@@ -620,7 +625,10 @@ export function analyzeCode(code, language, framework, filePath, configDir, rule
620
625
  // Skip VG863 for non-publishable apps. Signals that this is an application, not a library:
621
626
  // no publishing fields (bin/exports/module/types), main does not point at a build dir,
622
627
  // and start script runs a runtime/framework directly.
628
+ // Also skip when "private": true — npm refuses to publish private packages outright.
623
629
  if (rule.id === "VG863") {
630
+ if (/"private"\s*:\s*true\b/.test(code))
631
+ continue;
624
632
  const hasPublishingFields = /"(?:bin|exports|module|types|typings)"\s*:/i.test(code);
625
633
  const mainPointsToBuild = /"main"\s*:\s*"(?:dist|build|lib|out)\//i.test(code);
626
634
  const runtimeNames = "node|nodemon|tsx|ts-node|next|nest|vite|remix|astro";
@@ -632,6 +640,7 @@ export function analyzeCode(code, language, framework, filePath, configDir, rule
632
640
  // Skip VG955 (Missing Pagination) when the query is bounded by ID(s):
633
641
  // findMany({ where: { id: x } }) returns at most 1; findMany({ where: { id: { in: [...] } } })
634
642
  // is bounded by the caller-provided list. Same applies to *Id fields like partnerId, userId.
643
+ // Shorthand form { userId } / { teamId } counts as bound — these are tenant-scoped queries.
635
644
  if (rule.id === "VG955") {
636
645
  const matched = match[0];
637
646
  if (/\bin\s*:\s*\[/i.test(matched))
@@ -640,6 +649,8 @@ export function analyzeCode(code, language, framework, filePath, configDir, rule
640
649
  continue; // where: { partnerId: { in: ids } }
641
650
  if (/\b(?:id|[a-zA-Z]+Id)\s*:\s*[a-zA-Z_$]/i.test(matched))
642
651
  continue; // where: { id: someVar }
652
+ if (/\b(?:id|[a-zA-Z]+Id)\s*[,}]/i.test(matched))
653
+ continue; // where: { userId } shorthand
643
654
  }
644
655
  // Skip VG106 for non-secret variable names (TokenCount, tokenBalance, hashMap, etc.)
645
656
  // and for comparisons against literals/null/undefined that are emptiness checks,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "guardvibe",
3
- "version": "3.0.39",
3
+ "version": "3.0.41",
4
4
  "mcpName": "io.github.goklab/guardvibe",
5
5
  "description": "Security MCP for vibe coding. 365 rules, 38 tools, CLI + doctor. Host security, auth coverage mapping, LLM-powered deep scan (IDOR/business logic), taint analysis. 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",