guardvibe 3.1.34 → 3.1.35

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.
package/CHANGELOG.md CHANGED
@@ -5,6 +5,19 @@ All notable changes to GuardVibe are documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [3.1.35] - 2026-06-07
9
+
10
+ ### Fixed — false-positive precision on real production apps (no rule-count change, 436 / 36)
11
+ Surfaced by the precision half of the quality sweep; each narrowing was verified against the cited code and confirmed (via an old-vs-new diff over the corpus) to remove only false positives, with zero true-positive loss.
12
+ - **VG123 / VG010** no longer flag a parameterized IN-clause built from placeholder generation (`id IN (${ids.map(() => '?').join(',')})` with values passed as the params array).
13
+ - **VG137** (debug endpoint) no longer fires on build/test config files (`vite.config`, `jest-e2e`, `playwright.config`, `vitest`, etc.) where a `/test` path string sits near `process.env`.
14
+ - **VG1005** (Supabase `.or()` filter injection) now requires actual Supabase usage in the file, ending the collision with Zod's `.or()` schema combinator.
15
+ - **VG968** (cron `CRON_SECRET`) recognizes Vercel/QStash signature verification (`verifyVercelSignature`, `verifyQstashSignature`, `Receiver`) as valid cron auth.
16
+ - **VG951** (BOLA) recognizes tenant compound-where ownership (`where: { id, projectId | workspaceId | teamId }`).
17
+ - **VG601** (Stripe webhook) recognizes non-Stripe signature verification (QStash/Vercel/generic `verifySignature`).
18
+
19
+ Self-audit PASS / A / 0, gate green.
20
+
8
21
  ## [3.1.34] - 2026-06-07
9
22
 
10
23
  ### Added — recall (false-negative) improvements (433 → 436 rules, 36 tools)
package/README.md CHANGED
@@ -457,7 +457,7 @@ If your AI agent cannot connect to GuardVibe:
457
457
 
458
458
  1. **Restart your IDE/agent.** MCP servers are started by the host application. After running `npx guardvibe init`, restart Claude Code, Cursor, or Gemini CLI for the config to take effect.
459
459
  2. **Check the config path.** Run `npx guardvibe init claude` again and verify the output shows the correct config file location (`.mcp.json` in your project root for Claude Code, `.cursor/mcp.json` for Cursor).
460
- 3. **Re-run `init` to upgrade.** When upgrading GuardVibe, re-run `npx guardvibe init claude` — `.mcp.json` is pinned to a specific version (e.g. `guardvibe@3.1.34`) at init time for fast deterministic startup. As of v3.1.2 the re-run also rewrites stale pins automatically (`Upgraded GuardVibe pin (3.1.27 → 3.1.28)`); since v3.1.27 the PostToolUse hook command is pinned to the same version (was `@latest`) and re-run upgrades a stale hook too. The same applies to `npx guardvibe hook install` and `npx guardvibe ci github` (since v3.1.3) — both are version-pinned at install/generate time and re-run to upgrade.
460
+ 3. **Re-run `init` to upgrade.** When upgrading GuardVibe, re-run `npx guardvibe init claude` — `.mcp.json` is pinned to a specific version (e.g. `guardvibe@3.1.35`) at init time for fast deterministic startup. As of v3.1.2 the re-run also rewrites stale pins automatically (`Upgraded GuardVibe pin (3.1.27 → 3.1.28)`); since v3.1.27 the PostToolUse hook command is pinned to the same version (was `@latest`) and re-run upgrades a stale hook too. The same applies to `npx guardvibe hook install` and `npx guardvibe ci github` (since v3.1.3) — both are version-pinned at install/generate time and re-run to upgrade.
461
461
  4. **Pre-3.1.1 users won't see the auto-update banner.** GuardVibe started writing a once-per-day "newer version available" notice to stderr in v3.1.1. If your install predates that, you'll never see it — run `npx -y guardvibe@latest init <host>` once to bake in the latest pin and start receiving banners on subsequent sessions.
462
462
  5. **Verify Node.js version.** GuardVibe requires Node.js >= 18.0.0. Check with `node --version`.
463
463
  6. **Check npx cache.** If you upgraded GuardVibe and the old version is cached, run `npx -y guardvibe@latest` to force the latest version.
@@ -20,7 +20,7 @@ export const apiSecurityRules = [
20
20
  severity: "critical",
21
21
  owasp: "API1:2023 Broken Object Level Authorization",
22
22
  description: "Delete or update operation uses user-supplied ID without verifying resource ownership. Any authenticated user can modify or delete other users' resources.",
23
- pattern: /(?:delete|update|destroy|remove)\s*\(\s*\{?\s*(?:where\s*:\s*\{)?\s*(?:id|_id)\s*:\s*(?:req\.(?:params|query|body)|params\.|args\.|input\.)(?:(?!userId|user_id|ownerId|owner_id|createdBy|created_by|author|authorId|author_id|email|userEmail|accountId|account_id|tenantId|tenant_id|orgId|org_id|organizationId)[\s\S]){0,200}?\}/gi,
23
+ pattern: /(?:delete|update|destroy|remove)\s*\(\s*\{?\s*(?:where\s*:\s*\{)?\s*(?:id|_id)\s*:\s*(?:req\.(?:params|query|body)|params\.|args\.|input\.)(?:(?!userId|user_id|ownerId|owner_id|createdBy|created_by|author|authorId|author_id|email|userEmail|accountId|account_id|tenantId|tenant_id|orgId|org_id|organizationId|projectId|project_id|workspaceId|workspace_id|teamId|team_id)[\s\S]){0,200}?\}/gi,
24
24
  languages: ["javascript", "typescript"],
25
25
  fix: "Include the authenticated user's ID in the where clause to prevent unauthorized modifications.",
26
26
  fixCode: '// Scope mutations to the authenticated user\nconst { userId } = await auth();\nawait prisma.post.delete({\n where: { id: params.id, userId }, // ownership!\n});',
@@ -122,7 +122,7 @@ export const modernStackRules = [
122
122
  severity: "high",
123
123
  owasp: "A01:2025 Broken Access Control",
124
124
  description: "Vercel cron job endpoint does not verify the CRON_SECRET header. Anyone can trigger the cron job by calling the endpoint directly.",
125
- pattern: /(?:\/api\/cron|cron)[\s\S]*?export\s+(?:async\s+)?function\s+GET\s*\([^)]*\)\s*\{(?:(?!CRON_SECRET|authorization|Bearer|verifySignature|x-vercel-cron)[\s\S]){10,}?(?:prisma|db|supabase|fetch|sql|resend|stripe)\.\w+/g,
125
+ pattern: /(?:\/api\/cron|cron)[\s\S]*?export\s+(?:async\s+)?function\s+GET\s*\([^)]*\)\s*\{(?:(?!CRON_SECRET|authorization|Bearer|verifySignature|x-vercel-cron|verifyVercelSignature|verifyQstash|verifyQstashSignature|verifySignatureAppRouter|Receiver|verifyCron)[\s\S]){10,}?(?:prisma|db|supabase|fetch|sql|resend|stripe)\.\w+/g,
126
126
  languages: ["javascript", "typescript"],
127
127
  fix: "Verify the CRON_SECRET header at the start of every cron endpoint.",
128
128
  fixCode: 'export async function GET(request: Request) {\n const authHeader = request.headers.get("authorization");\n if (authHeader !== `Bearer ${process.env.CRON_SECRET}`) {\n return new Response("Unauthorized", { status: 401 });\n }\n // ... cron job logic\n}',
@@ -18,7 +18,7 @@ export const paymentRules = [
18
18
  severity: "critical",
19
19
  owasp: "A01:2025 Broken Access Control",
20
20
  description: "Stripe webhook endpoint processes events without verifying the webhook signature. Anyone can send fake payment events.",
21
- pattern: /(?:\/api\/webhook|\/api\/stripe|webhook.*stripe)[\s\S]*?(?:req\.body|request\.json|JSON\.parse)[\s\S]{0,300}?(?![\s\S]{0,300}?(?:constructEvent|verifyHeader|stripe\.webhooks|svix\.verify|webhook\.verify|verify\w*Webhook|verifyWebhookSignature|wh\.verify|crypto\.timingSafeEqual))/g,
21
+ pattern: /(?:\/api\/webhook|\/api\/stripe|webhook.*stripe)[\s\S]*?(?:req\.body|request\.json|JSON\.parse)[\s\S]{0,300}?(?![\s\S]{0,300}?(?:constructEvent|verifyHeader|stripe\.webhooks|svix\.verify|webhook\.verify|verify\w*Webhook|verifyWebhookSignature|wh\.verify|crypto\.timingSafeEqual|verifyQstashSignature|verifyQstash|verifySignature|verifyVercelSignature|Receiver\b|new\s+Webhook\b))/g,
22
22
  languages: ["javascript", "typescript"],
23
23
  fix: "Always verify Stripe webhook signatures using stripe.webhooks.constructEvent().",
24
24
  fixCode: "// Verify webhook signature\nconst sig = request.headers.get('stripe-signature')!;\nconst event = stripe.webhooks.constructEvent(\n body, sig, process.env.STRIPE_WEBHOOK_SECRET!\n);",
@@ -451,7 +451,17 @@ export function analyzeCode(code, language, framework, filePath, configDir, rule
451
451
  // agent.get('/?q=' + sqlPayload) which match the regex but aren't database calls
452
452
  // - VG042/VG678: HTTP-response/security-header rules (tests don't serve to real users)
453
453
  const isTestFile = filePath && /(?:\.(?:[\w-]+-)?(?:spec|test|e2e|stories|cy)\.(?:ts|tsx|js|jsx|mjs|cjs)$|_test\.go$|\/__tests__\/|\/__mocks__\/|\/tests?\/|\/cypress\/|\/playwright\/|\/dockertest\/|\/testutil\/|\/testhelpers?\/|\/testfixtures?\/)/i.test(filePath);
454
- if (isTestFile && ["VG001", "VG003", "VG062", "VG010", "VG011", "VG012", "VG013", "VG014", "VG042", "VG100", "VG130", "VG678", "VG955", "VG133", "VG1021", "VG409", "VG148", "VG424"].includes(rule.id))
454
+ if (isTestFile && ["VG001", "VG003", "VG062", "VG010", "VG011", "VG012", "VG013", "VG014", "VG042", "VG100", "VG130", "VG678", "VG955", "VG133", "VG1021", "VG409", "VG148", "VG424", "VG137"].includes(rule.id))
455
+ continue;
456
+ // VG137 (Debug Endpoint Exposes System Information) also misfires on build/test config
457
+ // files: a `<rootDir>/test/` mapper or a `/test` path string near `process.env` in
458
+ // vite/jest/playwright/vitest/rollup/webpack/react-router config is not an exposed
459
+ // debug HTTP endpoint. Skip those config files.
460
+ if (rule.id === "VG137" && filePath && /(?:\.config\.[cm]?[jt]sx?$|(?:^|\/)(?:vite|vitest|jest|playwright|cypress|rollup|webpack|esbuild|tsup|react-router|svelte|astro|nuxt|babel|tailwind|postcss|drizzle)[.-][\w.-]*\.[cm]?[jt]sx?$)/i.test(filePath))
461
+ continue;
462
+ // VG1005 (Supabase .or() Filter Injection) collides with Zod's `.or()` schema combinator
463
+ // and any other `.or(` method. Only fire when the file actually uses Supabase.
464
+ if (rule.id === "VG1005" && !/\bsupabase\b|createClient\s*\(|from\s+["']@supabase/i.test(code))
455
465
  continue;
456
466
  // VG955 (Missing Pagination on List Endpoint): only fire on actual request-handling
457
467
  // surfaces — API routes, App Router `route.{ts,tsx}`, pages/api, or Server Actions.
@@ -1000,7 +1010,11 @@ export function analyzeCode(code, language, framework, filePath, configDir, rule
1000
1010
  const isParameterized = /\b(?:bind|replacements)\s*:/.test(callCtx) || /[=\s](?:\$\d+|:[a-zA-Z_]\w*)\b/.test(tpl);
1001
1011
  const interps = tpl.match(/\$\{[^}]*\}/g) || [];
1002
1012
  const allSafe = interps.length > 0 && interps.every(s => /\$\{\s*[\w$.]*(?:hash|sha\d*|md5|bcrypt|argon2?|hmac|digest|encode|escape|encodeURIComponent|toString|String|Number|parseInt|parseFloat)\b/i.test(s));
1003
- if (isParameterized && allSafe)
1013
+ // Placeholder generation for a parameterized IN-clause: `id IN (${ids.map(()=>'?').join(',')})`
1014
+ // — the interpolation produces only `?` positional placeholders, values pass via the
1015
+ // params array. Inherently parameterized; not injectable.
1016
+ const allPlaceholderGen = interps.length > 0 && interps.every(s => /\.map\s*\(\s*\(?[\w,\s]*\)?\s*=>\s*["']\?["']/.test(s) || /^\$\{\s*["']\?["']\s*\}$/.test(s));
1017
+ if ((isParameterized && allSafe) || allPlaceholderGen)
1004
1018
  continue;
1005
1019
  }
1006
1020
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "guardvibe",
3
- "version": "3.1.34",
3
+ "version": "3.1.35",
4
4
  "mcpName": "io.github.goklab/guardvibe",
5
5
  "description": "Security MCP for vibe coding. 436 rules, 36 tools, CLI + doctor. Host security, auth coverage mapping, LLM-powered deep scan (IDOR/business logic), taint analysis. 67 CVE rules refreshed daily from GHSA/OSV/CISA KEV — Miasma @redhat-cloud-services compromise, Next.js May 2026 13-advisory cluster, Drizzle/MikroORM/Kysely SQL injection, Axios proxy-auth redirect leak, Hono setCookie attribute injection, Clerk SSRF, tRPC prototype pollution, @tanstack supply-chain, node-ipc protestware, OpenClaude sandbox bypass, plus the full AI-generated stack (Supabase, Stripe, Prisma, Hono, GraphQL, Convex, Turso, Uploadthing, AI SDK). 68 AI-native rules including OWASP MCP Top 10 tool-description prompt injection (VG1068), model-controlled sandbox-disable flag detection (VG1063), Session messenger exfil endpoint IOC (VG1075), and CI/CD supply-chain hardening (VG1070 npm --expect-provenance / --ignore-scripts enforcement).",
6
6
  "type": "module",