guardvibe 3.1.11 → 3.1.13

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.
@@ -333,6 +333,13 @@ export function analyzeCode(code, language, framework, filePath, configDir, rule
333
333
  while ((lit = literalUrlAssignRe.exec(code)) !== null)
334
334
  literalUrlVars.add(lit[1]);
335
335
  }
336
+ // jsforce SOQL skip signal for VG123. jsforce's `conn.query()` is SOQL
337
+ // (Salesforce's query language), not SQL — different injection semantics, and
338
+ // jsforce does not support parameterized queries. The documented practice is
339
+ // manual escape via a `sanitize*Soql*` helper. File-level boolean: cheaper
340
+ // than re-testing both regexes per match.
341
+ const fileIsJsforceWithSoqlSanitizer = /from\s+["']@?jsforce[\w@/-]*["']/i.test(code) &&
342
+ /sanitiz\w*Soql\w*/i.test(code);
336
343
  // Config: check custom auth function names from .guardviberc
337
344
  if (!codeHasAuthGuard && config.authFunctions && config.authFunctions.length > 0) {
338
345
  const customPattern = new RegExp(`(?:${config.authFunctions.join("|")})\\s*\\(`, "i");
@@ -363,7 +370,7 @@ export function analyzeCode(code, language, framework, filePath, configDir, rule
363
370
  // agent.get('/?q=' + sqlPayload) which match the regex but aren't database calls
364
371
  // - VG042/VG678: HTTP-response/security-header rules (tests don't serve to real users)
365
372
  const isTestFile = filePath && /(?:\.(?:[\w-]+-)?(?:spec|test|e2e|stories|cy)\.(?:ts|tsx|js|jsx|mjs|cjs)$|\/__tests__\/|\/tests?\/|\/cypress\/|\/playwright\/)/i.test(filePath);
366
- if (isTestFile && ["VG001", "VG062", "VG010", "VG011", "VG013", "VG014", "VG042", "VG130", "VG678", "VG955", "VG133", "VG1021", "VG409"].includes(rule.id))
373
+ if (isTestFile && ["VG001", "VG062", "VG010", "VG011", "VG012", "VG013", "VG014", "VG042", "VG130", "VG678", "VG955", "VG133", "VG1021", "VG409"].includes(rule.id))
367
374
  continue;
368
375
  // VG955 (Missing Pagination on List Endpoint): only fire on actual request-handling
369
376
  // surfaces — API routes, App Router `route.{ts,tsx}`, pages/api, or Server Actions.
@@ -877,6 +884,45 @@ export function analyzeCode(code, language, framework, filePath, configDir, rule
877
884
  if (isServiceVerbCall && !hasSqlKeyword)
878
885
  continue;
879
886
  }
887
+ // Skip XSS-family rules (VG012/VG408/VG042/VG852) when a lint-suppression
888
+ // comment for the corresponding rule sits within a small window around the
889
+ // matched line. Universal: biome `lint/security/noDangerouslySetInnerHtml`
890
+ // ignore (most JSX cases) or eslint `react/no-danger` disable. Window is
891
+ // ±3 lines because (a) the rule may fire on the comment line itself when
892
+ // its pattern matches the `dangerouslySetInnerHtml` substring inside the
893
+ // biome ignore directive, and (b) JSX attribute rules often fire 2-3 lines
894
+ // below a `{/* biome-ignore ... */}` placed above the opening tag. The
895
+ // suppression comment is the developer's explicit acceptance of the
896
+ // exception (almost always accompanied by a sanitization rationale).
897
+ if (["VG012", "VG408", "VG042", "VG852"].includes(rule.id)) {
898
+ const window = lines
899
+ .slice(Math.max(0, lineNumber - 4), Math.min(lines.length, lineNumber + 3))
900
+ .join("\n");
901
+ if (/biome-ignore\s+lint\/security\/noDangerouslySetInnerHtml\b/i.test(window))
902
+ continue;
903
+ if (/eslint-disable(?:-next-line)?\b[^\n]{0,200}react\/no-danger\b/i.test(window))
904
+ continue;
905
+ }
906
+ // Skip VG012 when the right-hand side is a hardcoded string literal with
907
+ // no interpolation. No user input can flow in; the markup is fully
908
+ // developer-controlled.
909
+ if (rule.id === "VG012") {
910
+ const matchedLine = lines[lineNumber - 1] ?? "";
911
+ if (/\.\w+\s*=\s*"[^"\n]*"\s*;?\s*$/.test(matchedLine) && /\.innerHTML\s*=/.test(matchedLine))
912
+ continue;
913
+ if (/\.\w+\s*=\s*'[^'\n]*'\s*;?\s*$/.test(matchedLine) && /\.innerHTML\s*=/.test(matchedLine))
914
+ continue;
915
+ }
916
+ // Skip VG010/VG123 (SQL injection family) on jsforce SOQL calls. SOQL has
917
+ // different injection semantics than SQL and jsforce does not support
918
+ // parameterized queries — the documented practice is manual escape via a
919
+ // `sanitize*Soql*` helper. File must import jsforce AND use a SOQL
920
+ // sanitizer — both required, so a jsforce file that forgets to escape
921
+ // still fires. Both VG010 and VG123 are listed because the dedup logic
922
+ // (isDuplicatePair) collapses them on the same line; without skipping
923
+ // both, VG010 just takes over when VG123 is suppressed.
924
+ if ((rule.id === "VG123" || rule.id === "VG010") && fileIsJsforceWithSoqlSanitizer)
925
+ continue;
880
926
  // Skip supply chain rules for known legitimate packages
881
927
  if (["VG872", "VG873"].includes(rule.id)) {
882
928
  const pkgMatch = /"([\w@/-]+)"/.exec(match[0]);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "guardvibe",
3
- "version": "3.1.11",
3
+ "version": "3.1.13",
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",