guardvibe 3.1.6 → 3.1.8

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.
@@ -351,7 +351,7 @@ export function analyzeCode(code, language, framework, filePath, configDir, rule
351
351
  // agent.get('/?q=' + sqlPayload) which match the regex but aren't database calls
352
352
  // - VG042/VG678: HTTP-response/security-header rules (tests don't serve to real users)
353
353
  const isTestFile = filePath && /(?:\.(?:[\w-]+-)?(?:spec|test|e2e|stories|cy)\.(?:ts|tsx|js|jsx|mjs|cjs)$|\/__tests__\/|\/tests?\/|\/cypress\/|\/playwright\/)/i.test(filePath);
354
- if (isTestFile && ["VG001", "VG062", "VG010", "VG011", "VG013", "VG014", "VG042", "VG130", "VG678", "VG955", "VG133", "VG1021"].includes(rule.id))
354
+ if (isTestFile && ["VG001", "VG062", "VG010", "VG011", "VG013", "VG014", "VG042", "VG130", "VG678", "VG955", "VG133", "VG1021", "VG409"].includes(rule.id))
355
355
  continue;
356
356
  // VG955 (Missing Pagination on List Endpoint): only fire on actual request-handling
357
357
  // surfaces — API routes, App Router `route.{ts,tsx}`, pages/api, or Server Actions.
@@ -721,6 +721,20 @@ export function analyzeCode(code, language, framework, filePath, configDir, rule
721
721
  if (dockerStageAliases.has(target))
722
722
  continue;
723
723
  }
724
+ // VG409 (Open Redirect via User Input): the rule's pattern matches based on the
725
+ // variable name (`redirectUrl`, `returnTo`, `callbackUrl`, `next`, etc.) regardless
726
+ // of how the variable was assigned. Skip when the variable is assigned to a string
727
+ // literal in the same file with no template-literal interpolation — that's a
728
+ // hardcoded redirect target, not user input.
729
+ if (rule.id === "VG409") {
730
+ const varMatch = match[0].match(/\(\s*(\w+)/);
731
+ if (varMatch) {
732
+ const varName = varMatch[1];
733
+ const literalAssign = new RegExp(`\\b(?:const|let|var)\\s+${varName}\\s*(?::\\s*[\\w<>\\[\\],\\s]+\\s*)?=\\s*(?:"[^"]*"|'[^']*'|\`[^\`$]*\`)\\s*;?`);
734
+ if (literalAssign.test(code))
735
+ continue;
736
+ }
737
+ }
724
738
  // Skip matches on comment lines and inside string literals.
725
739
  // CVE version-pin rules (VG900-VG931) are exempt — they scan package.json
726
740
  // dependency declarations where these contexts don't apply.
@@ -17,6 +17,7 @@ import { analyzeCrossFileTaint } from "./cross-file-taint.js";
17
17
  import { analyzeAuthCoverage } from "./auth-coverage.js";
18
18
  import { getRules } from "../utils/rule-registry.js";
19
19
  import { loadConfig } from "../utils/config.js";
20
+ import { isExcludedFilename } from "../utils/constants.js";
20
21
  // --- Core Logic ---
21
22
  /**
22
23
  * Compute verdict: PASS (0 critical + 0 high), WARN (high > 0), FAIL (critical > 0)
@@ -103,6 +104,8 @@ function collectJsFiles(dir, maxFiles = 200) {
103
104
  }
104
105
  if (!/\.(ts|tsx|js|jsx|mts|cts)$/.test(entry))
105
106
  continue;
107
+ if (isExcludedFilename(entry))
108
+ continue;
106
109
  if (stat.size > 100_000)
107
110
  continue;
108
111
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "guardvibe",
3
- "version": "3.1.6",
3
+ "version": "3.1.8",
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",