guardvibe 3.1.33 → 3.1.34

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,22 @@ 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.34] - 2026-06-07
9
+
10
+ ### Added — recall (false-negative) improvements (433 → 436 rules, 36 tools)
11
+ Surfaced by a recall battery of canonical vulnerable snippets; each gap was reproduced, fixed, and given positive + negative tests. A ReDoS regression guard for all rule patterns (`tests/meta/redos.test.ts`) was added and caught a polynomial backtrack in one of these very changes before release.
12
+ - **VG010** now catches the most common concat-SQLi style where the SQL string embeds a quote to wrap the value (`"... name = '" + name + "'"`), and Sequelize `literal()` raw fragments.
13
+ - **VG014** extended to `vm.runInNewContext`/`runInContext`/`runInThisContext`/`compileFunction` and `new vm.Script`.
14
+ - **VG070** extended to `unserialize()` (node-serialize), funcster, cryo.
15
+ - **VG103** extended to user-controlled bracket assignment (`obj[req.body.key] = …`) and lodash `_.set`/`objectPath.set`.
16
+ - **VG102** extended to `res.sendFile`/`res.download` path traversal.
17
+ - **VG409** extended to open redirect via `res.setHeader("Location", userInput)`.
18
+ - **VG1080** (new) DOM XSS via `document.write()`/`writeln()` with user input.
19
+ - **VG1081** (new) insecure block cipher mode — AES/DES ECB and the deprecated `crypto.createCipher`.
20
+ - **VG1082** (new) server-side template injection — `Handlebars.compile`/`ejs.render`/`pug`/`nunjucks`/lodash `_.template` on user-controlled template source.
21
+
22
+ Self-audit PASS / A / 0, gate green, determinism preserved across the corpus.
23
+
8
24
  ## [3.1.33] - 2026-06-07
9
25
 
10
26
  ### Fixed — false-positive precision (no rule-count change, stays 433 / 36)
package/README.md CHANGED
@@ -6,7 +6,7 @@
6
6
  [![npm provenance](https://img.shields.io/badge/provenance-verified-brightgreen)](https://www.npmjs.com/package/guardvibe)
7
7
  [![codecov](https://codecov.io/gh/goklab/guardvibe/graph/badge.svg)](https://codecov.io/gh/goklab/guardvibe)
8
8
 
9
- **The security MCP built for vibe coding.** 433 security rules, 36 tools covering the entire AI-generated code journey — from first line to production deployment.
9
+ **The security MCP built for vibe coding.** 436 security rules, 36 tools covering the entire AI-generated code journey — from first line to production deployment.
10
10
 
11
11
  Works with **Claude Code, Cursor, Gemini CLI, Codex, VS Code (Copilot), Windsurf**, and any MCP-compatible coding agent.
12
12
 
@@ -14,7 +14,7 @@ Works with **Claude Code, Cursor, Gemini CLI, Codex, VS Code (Copilot), Windsurf
14
14
 
15
15
  Most security tools are built for enterprise security teams. GuardVibe is built for **you** — the developer using AI to build and ship web apps fast.
16
16
 
17
- - **433 security rules, 36 tools** purpose-built for the stacks AI agents generate
17
+ - **436 security rules, 36 tools** purpose-built for the stacks AI agents generate
18
18
  - **Zero setup friction** — `npx guardvibe` and you're scanning
19
19
  - **No account required** — runs 100% locally, no API keys, no cloud
20
20
  - **Understands your stack** — not generic SAST, but rules that know Next.js, Supabase, Stripe, Clerk, and the tools you actually use
@@ -52,7 +52,7 @@ GuardVibe is purpose-built for the AI coding workflow. Traditional tools are exc
52
52
  | CVE version detection | 67 packages, refreshed daily | Extensive | Extensive |
53
53
  | Compliance mapping (SOC2, PCI-DSS, HIPAA) | Built-in | Paid tier | None |
54
54
  | SARIF CI/CD export | Yes | Yes | Limited |
55
- | Rule count | 433 (focused, 68 AI-native) | 5000+ (broad) | N/A |
55
+ | Rule count | 436 (focused, 68 AI-native) | 5000+ (broad) | N/A |
56
56
 
57
57
  **When to use GuardVibe:** You're building with AI agents and want security scanning integrated into your coding workflow — no dashboard, no account, no CI setup.
58
58
 
@@ -242,7 +242,7 @@ Malicious postinstall scripts, unpinned GitHub Actions, CI `npm` provenance / `-
242
242
 
243
243
  All scanning tools support `format: "json"` for machine-readable output.
244
244
 
245
- ## Security Rules (433 rules across 25 modules)
245
+ ## Security Rules (436 rules across 25 modules)
246
246
 
247
247
  | Category | Rules | Coverage |
248
248
  |----------|-------|----------|
@@ -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.33`) 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.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.
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.
@@ -55,7 +55,7 @@ export const coreRules = [
55
55
  severity: "critical",
56
56
  owasp: "A02:2025 Injection",
57
57
  description: "String concatenation, template literals, or f-strings used in SQL queries — whether inline in the DB call or assembled in a variable/return first. This allows SQL injection attacks.",
58
- pattern: /(?:query|execute|raw|sql|all|run|get|exec|prepare|QueryRow|QueryContext)\s*\(\s*(?:`[^`]*\$\{|['"][^'"]*['"]\s*\+\s*|f"[^"]*\{|f'[^']*\{|['"][^'"]*['"]\s*%\s*|['"][^'"]*['"]\s*\.format\s*\(|['"][^'"]*['"]\s*,\s*(?:req\.|request\.|params\.|body\.|args))|(?:=|return)\s*(?:`\s*(?:SELECT|INSERT|UPDATE|DELETE)\b[^`]*\b(?:FROM|INTO|SET|WHERE|VALUES)\b[^`]*\$\{|['"]\s*(?:SELECT|INSERT|UPDATE|DELETE)\b[^\n]*?\b(?:FROM|INTO|SET|WHERE|VALUES)\b[^\n]*?['"]\s*\+\s*\w)/gi,
58
+ pattern: /(?:query|execute|raw|sql|all|run|get|exec|prepare|literal|QueryRow|QueryContext)\s*\(\s*(?:`[^`]*\$\{|(?:"[^"]*"|'[^']*')\s*\+\s*|f"[^"]*\{|f'[^']*\{|['"][^'"]*['"]\s*%\s*|['"][^'"]*['"]\s*\.format\s*\(|['"][^'"]*['"]\s*,\s*(?:req\.|request\.|params\.|body\.|args))|(?:=|return)\s*(?:`\s*(?:SELECT|INSERT|UPDATE|DELETE)\b[^`]*\b(?:FROM|INTO|SET|WHERE|VALUES)\b[^`]*\$\{|['"]\s*(?:SELECT|INSERT|UPDATE|DELETE)\b[^\n]*?\b(?:FROM|INTO|SET|WHERE|VALUES)\b[^\n]*?['"]\s*\+\s*\w)/gi,
59
59
  languages: ["javascript", "typescript", "python", "go"],
60
60
  fix: "Use parameterized queries: db.query('SELECT * FROM users WHERE id = $1', [userId]). Python: cursor.execute('SELECT * FROM users WHERE id = %s', (user_id,)). Never concatenate user input into SQL strings.",
61
61
  fixCode: "// Use parameterized queries\ndb.query('SELECT * FROM users WHERE id = $1', [userId]);\n// Python: cursor.execute('SELECT * FROM users WHERE id = %s', (user_id,))",
@@ -116,7 +116,7 @@ export const coreRules = [
116
116
  severity: "critical",
117
117
  owasp: "A02:2025 Injection",
118
118
  description: "Dynamic code execution function detected. This can run arbitrary code and is a major security risk.",
119
- pattern: /(?:\beval\s*\(|new\s+Function\s*\()/gi,
119
+ pattern: /(?:\beval\s*\(|new\s+Function\s*\(|\bvm\s*\.\s*(?:runInNewContext|runInContext|runInThisContext|compileFunction)\s*\(|new\s+vm\s*\.\s*Script\s*\()/gi,
120
120
  languages: ["javascript", "typescript", "python"],
121
121
  fix: "Avoid dynamic code execution. Use JSON.parse() for JSON data. Use a sandboxed environment if absolutely required.",
122
122
  fixCode: "// Use JSON.parse for data\nconst data = JSON.parse(input);\n// Alternatives: use a proper parser for your data format\n// const fn = new " + "Function('x', 'return x * 2'); // only if absolutely needed",
@@ -224,7 +224,7 @@ export const coreRules = [
224
224
  severity: "high",
225
225
  owasp: "A08:2025 Data Integrity Failures",
226
226
  description: "Deserializing untrusted data can lead to remote code execution.",
227
- pattern: /(?:JSON\.parse\s*\(\s*(?:req\.|request\.|body)|pickle\.loads?\s*\(|yaml\.(?:load|unsafe_load)\s*\()/gi,
227
+ pattern: /(?:JSON\.parse\s*\(\s*(?:req\.|request\.|body)|pickle\.loads?\s*\(|yaml\.(?:load|unsafe_load)\s*\(|\bunserialize\s*\(|\bfuncster\s*\.|\bcryo\s*\.\s*parse\s*\()/gi,
228
228
  languages: ["javascript", "typescript", "python"],
229
229
  fix: "Validate all deserialized data with a schema (zod, joi) before processing.",
230
230
  fixCode: "// Validate with schema after parsing\nimport { z } from 'zod';\nconst schema = z.object({ name: z.string() });\nconst data = schema.parse(JSON.parse(req.body));",
@@ -284,7 +284,7 @@ export const coreRules = [
284
284
  severity: "high",
285
285
  owasp: "A01:2025 Broken Access Control",
286
286
  description: "User input used in file paths without sanitization.",
287
- pattern: /(?:readFile|readFileSync|createReadStream|open|path\.join|path\.resolve)\s*\([^)]*(?:req\.|request\.|params\.|body\.|query\.)/gi,
287
+ pattern: /(?:readFile|readFileSync|createReadStream|open|sendFile|download|path\.join|path\.resolve)\s*\([^)]*(?:req\.|request\.|params\.|body\.|query\.)/gi,
288
288
  languages: ["javascript", "typescript", "python", "go"],
289
289
  fix: "Sanitize file paths: remove ../ sequences, verify the result is within the expected directory.",
290
290
  fixCode: "import path from 'path';\nconst safePath = path.resolve('/uploads', filename);\nif (!safePath.startsWith('/uploads/')) throw new Error('Invalid path');",
@@ -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|\bmerge\b|deepMerge|\bextend\b)\s*\([^)]*(?:req\.|request\.|\bbody\b|\bparams\b)/gi,
299
+ pattern: /(?:(?:Object\.assign|\bmerge\b|deepMerge|\bextend\b|(?:_|lodash)\.set(?:With)?|objectPath\.set|dotProp\.set)\s*\([^)]*(?:req\.|request\.|\bbody\b|\bparams\b)|\[\s*(?:req|request)\.(?:body|params|query)\.[\w.]+\s*\]\s*=(?!=))/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');",
@@ -117,7 +117,7 @@ export const nextjsRules = [
117
117
  severity: "medium",
118
118
  owasp: "A01:2025 Broken Access Control",
119
119
  description: "redirect() or NextResponse.redirect() uses user-controlled input (searchParams, query) which can redirect users to malicious sites.",
120
- pattern: /(?:redirect|NextResponse\.redirect|res\.redirect|Response\.redirect)\s*\(\s*(?:searchParams|params|req\.query|request\.url|url|query|returnTo|callbackUrl|next|goto|returnUrl|redirectUrl|destination)\b/gi,
120
+ pattern: /(?:(?:redirect|NextResponse\.redirect|res\.redirect|Response\.redirect)\s*\(\s*(?:searchParams|params|req\.query|request\.url|url|query|returnTo|callbackUrl|next|goto|returnUrl|redirectUrl|destination)\b|(?:res|reply)\.setHeader\s*\(\s*['"][Ll]ocation['"]\s*,\s*(?:req\.|request\.|searchParams|params\b|url\b|query\b|returnTo|callbackUrl|returnUrl|redirectUrl|destination))/gi,
121
121
  languages: ["javascript", "typescript"],
122
122
  fix: "Validate redirect URLs against an allowlist of trusted domains.",
123
123
  fixCode: '// Validate redirect URL\nconst ALLOWED_HOSTS = ["example.com"];\nconst target = searchParams.get("next") ?? "/";\ntry {\n const url = new URL(target, request.url);\n if (!ALLOWED_HOSTS.includes(url.hostname)) redirect("/");\n redirect(url.pathname);\n} catch {\n redirect("/");\n}',
@@ -185,4 +185,40 @@ export const webSecurityRules = [
185
185
  fixCode: '// Set nosniff header for uploaded file responses\nres.setHeader("X-Content-Type-Options", "nosniff");\nres.setHeader("Content-Disposition", "attachment"); // force download for unknown types\nres.sendFile(filePath);',
186
186
  compliance: ["SOC2:CC6.1"],
187
187
  },
188
+ {
189
+ id: "VG1080",
190
+ name: "DOM XSS via document.write()",
191
+ severity: "high",
192
+ owasp: "A03:2025 Injection",
193
+ description: "document.write()/document.writeln() called with user-controlled or concatenated/interpolated content. document.write parses its argument as HTML, so attacker-influenced input (location, query params, cookies, window.name) leads to DOM-based cross-site scripting.",
194
+ pattern: /document\.write(?:ln)?\s*\(\s*(?:[^)]*?(?:location|document\.(?:URL|cookie|referrer)|searchParams|req\.|request\.|params\.|query\.|window\.name|\binput\b)|`[^`]*\$\{|["'][^"']*["']\s*\+)/gi,
195
+ languages: ["javascript", "typescript"],
196
+ fix: "Never build HTML with document.write from untrusted input. Use safe DOM APIs (textContent, createElement) or sanitize with DOMPurify before inserting.",
197
+ fixCode: "// BAD: document.write('<div>' + location.hash + '</div>')\n// GOOD:\nconst el = document.createElement('div');\nel.textContent = userValue; // auto-escaped\ncontainer.appendChild(el);",
198
+ compliance: ["SOC2:CC7.1", "PCI-DSS:Req6.5.7"],
199
+ },
200
+ {
201
+ id: "VG1081",
202
+ name: "Insecure Block Cipher Mode (ECB / deprecated createCipher)",
203
+ severity: "high",
204
+ owasp: "A02:2025 Cryptographic Failures",
205
+ description: "AES/DES used in ECB mode (createCipheriv with an *-ecb algorithm), or the deprecated crypto.createCipher() which derives a key/IV insecurely. ECB encrypts identical plaintext blocks to identical ciphertext blocks, leaking structure; createCipher is password-derived and IV-less. Both are cryptographically broken for confidentiality.",
206
+ pattern: /(?:createCipheriv\s*\(\s*["'][^"']*-ecb["']|createDecipheriv\s*\(\s*["'][^"']*-ecb["']|crypto\s*\.\s*createCipher\s*\(\s*["'])/gi,
207
+ languages: ["javascript", "typescript"],
208
+ fix: "Use an authenticated mode: aes-256-gcm with a random 12-byte IV per message (crypto.randomBytes), or aes-256-cbc with a random IV and a separate MAC. Never use ECB; replace createCipher with createCipheriv.",
209
+ fixCode: "// GOOD: AES-256-GCM with a random IV\nconst iv = crypto.randomBytes(12);\nconst cipher = crypto.createCipheriv('aes-256-gcm', key, iv);\nconst enc = Buffer.concat([cipher.update(data), cipher.final()]);\nconst tag = cipher.getAuthTag();",
210
+ compliance: ["SOC2:CC6.1", "PCI-DSS:Req3.5", "HIPAA:§164.312(a)(2)(iv)"],
211
+ },
212
+ {
213
+ id: "VG1082",
214
+ name: "Server-Side Template Injection (SSTI)",
215
+ severity: "critical",
216
+ owasp: "A03:2025 Injection",
217
+ description: "A template engine compiles/renders a user-controlled template SOURCE (not just user data bound into a fixed template). Handlebars.compile, ejs.render/compile, pug, nunjucks.renderString, or lodash _.template on attacker-influenced input allows server-side template injection — often a path to remote code execution.",
218
+ pattern: /(?:Handlebars\.compile|ejs\.(?:render|compile)|pug\.(?:compile|render)|nunjucks\.(?:renderString|compile)|_\.template|lodash\.template|dot\.template)\s*\(\s*(?:[^,)]*?(?:req\.|request\.|\bbody\b|\bparams\b|\bquery\b|userInput|\binput\b)|`[^`]*\$\{|[^,)]*\+)/gi,
219
+ languages: ["javascript", "typescript"],
220
+ fix: "Never compile a template from user input. Keep template sources static/server-owned and pass user values only as DATA to a precompiled template. If user-authored templates are required, use a sandboxed engine with no access to globals.",
221
+ fixCode: "// BAD: ejs.render(req.body.template, data)\n// GOOD: fixed template, user value as data only\nconst tpl = ejs.compile(STATIC_TEMPLATE);\nres.send(tpl({ name: req.body.name }));",
222
+ compliance: ["SOC2:CC7.1", "PCI-DSS:Req6.5.1"],
223
+ },
188
224
  ];
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "guardvibe",
3
- "version": "3.1.33",
3
+ "version": "3.1.34",
4
4
  "mcpName": "io.github.goklab/guardvibe",
5
- "description": "Security MCP for vibe coding. 433 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).",
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",
7
7
  "bin": {
8
8
  "guardvibe": "build/cli.js",