guardvibe 1.7.0 → 1.7.1
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/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# GuardVibe
|
|
2
2
|
|
|
3
|
-
**The security MCP built for vibe coding.**
|
|
3
|
+
**The security MCP built for vibe coding.** 277 security rules covering the entire AI-generated code journey — from first line to production deployment.
|
|
4
4
|
|
|
5
5
|
Works with **Claude Code, Cursor, Gemini CLI, Codex, Windsurf**, and any MCP-compatible coding agent.
|
|
6
6
|
|
|
@@ -8,7 +8,7 @@ Works with **Claude Code, Cursor, Gemini CLI, Codex, Windsurf**, and any MCP-com
|
|
|
8
8
|
|
|
9
9
|
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.
|
|
10
10
|
|
|
11
|
-
- **
|
|
11
|
+
- **277 security rules** purpose-built for the stacks AI agents generate
|
|
12
12
|
- **Zero setup friction** — `npx guardvibe` and you're scanning
|
|
13
13
|
- **No account required** — runs 100% locally, no API keys, no cloud
|
|
14
14
|
- **Understands your stack** — not generic SAST, but rules that know Next.js, Supabase, Stripe, Clerk, and the tools you actually use
|
|
@@ -34,7 +34,7 @@ GuardVibe is purpose-built for the AI coding workflow. Traditional tools are exc
|
|
|
34
34
|
| CVE version detection | 21 packages | Extensive | Extensive |
|
|
35
35
|
| Compliance mapping (SOC2, PCI-DSS, HIPAA) | Built-in | Paid tier | None |
|
|
36
36
|
| SARIF CI/CD export | Yes | Yes | Limited |
|
|
37
|
-
| Rule count |
|
|
37
|
+
| Rule count | 277 (focused) | 5000+ (broad) | N/A |
|
|
38
38
|
|
|
39
39
|
**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.
|
|
40
40
|
|
|
@@ -146,7 +146,7 @@ Malicious postinstall scripts, unpinned GitHub Actions, typosquat detection
|
|
|
146
146
|
|
|
147
147
|
All scanning tools support `format: "json"` for machine-readable output.
|
|
148
148
|
|
|
149
|
-
## Security Rules (
|
|
149
|
+
## Security Rules (277 rules across 23 modules)
|
|
150
150
|
|
|
151
151
|
| Category | Rules | Coverage |
|
|
152
152
|
|----------|-------|----------|
|
package/build/data/rules/core.js
CHANGED
|
@@ -346,4 +346,40 @@ export const coreRules = [
|
|
|
346
346
|
fixCode: '// BAD: user controls the regex\nconst re = new RegExp(req.query.search);\n\n// GOOD: escape regex special chars\nfunction escapeRegex(s: string) {\n return s.replace(/[.*+?^${}()|[\\]\\\\]/g, "\\\\$&");\n}\nconst re = new RegExp(escapeRegex(req.query.search));\n\n// BETTER: use string methods\nconst results = items.filter(i => i.name.includes(query));',
|
|
347
347
|
compliance: ["SOC2:CC7.1"],
|
|
348
348
|
},
|
|
349
|
+
{
|
|
350
|
+
id: "VG108",
|
|
351
|
+
name: "Vue v-html Directive with User Data",
|
|
352
|
+
severity: "high",
|
|
353
|
+
owasp: "A07:2025 Cross-Site Scripting",
|
|
354
|
+
description: "Vue's v-html directive renders raw HTML without sanitization, equivalent to innerHTML. If user-controlled data is bound via v-html, attackers can inject arbitrary scripts for stored or reflected XSS.",
|
|
355
|
+
pattern: /v-html\s*=\s*["'](?!['"])\w/gi,
|
|
356
|
+
languages: ["html", "javascript", "typescript"],
|
|
357
|
+
fix: "Avoid v-html with user data. Use text interpolation {{ }} or sanitize with DOMPurify before rendering.",
|
|
358
|
+
fixCode: '<!-- BAD: raw HTML rendering -->\n<!-- <div v-html="userComment"></div> -->\n\n<!-- GOOD: text interpolation (auto-escaped) -->\n<div>{{ userComment }}</div>\n\n<!-- If HTML needed: sanitize first -->\n<div v-html="DOMPurify.sanitize(userComment)"></div>',
|
|
359
|
+
compliance: ["SOC2:CC7.1"],
|
|
360
|
+
},
|
|
361
|
+
{
|
|
362
|
+
id: "VG109",
|
|
363
|
+
name: "Angular innerHTML Binding with User Data",
|
|
364
|
+
severity: "high",
|
|
365
|
+
owasp: "A07:2025 Cross-Site Scripting",
|
|
366
|
+
description: "Angular's [innerHTML] property binding renders HTML content. While Angular's built-in sanitizer strips scripts, it can be bypassed with bypassSecurityTrustHtml() or via CSS/SVG-based XSS vectors.",
|
|
367
|
+
pattern: /(?:\[innerHTML\]\s*=\s*["']\w|bypassSecurityTrustHtml\s*\()/gi,
|
|
368
|
+
languages: ["html", "typescript"],
|
|
369
|
+
fix: "Avoid [innerHTML] with user data. If unavoidable, never use bypassSecurityTrustHtml() on user input.",
|
|
370
|
+
fixCode: '<!-- BAD: bypass Angular sanitizer -->\n<!-- <div [innerHTML]="trustedHtml"></div> -->\n<!-- this.trustedHtml = this.sanitizer.bypassSecurityTrustHtml(userInput); -->\n\n<!-- GOOD: let Angular sanitize automatically -->\n<div [innerText]="userInput"></div>\n<!-- Or use Angular pipe with DOMPurify -->',
|
|
371
|
+
compliance: ["SOC2:CC7.1"],
|
|
372
|
+
},
|
|
373
|
+
{
|
|
374
|
+
id: "VG116",
|
|
375
|
+
name: "HTML Event Handler Injection via User Input",
|
|
376
|
+
severity: "high",
|
|
377
|
+
owasp: "A07:2025 Cross-Site Scripting",
|
|
378
|
+
description: "User input is interpolated into HTML attributes that accept JavaScript (onclick, onerror, onload, onmouseover, onfocus). Even without script tags, event handlers execute arbitrary JavaScript when the element is interacted with or loads.",
|
|
379
|
+
pattern: /(?:on(?:click|error|load|mouseover|focus|blur|submit|change|input|keyup|keydown))\s*=\s*(?:`[^`]*\$\{|["'][^"']*["']\s*\+\s*(?:user|input|query|param|req\.|data\.))/gi,
|
|
380
|
+
languages: ["javascript", "typescript", "html"],
|
|
381
|
+
fix: "Never interpolate user input into HTML event handler attributes. Use addEventListener with sanitized data instead.",
|
|
382
|
+
fixCode: '// BAD: user input in event handler\n// `<img onerror="${userInput}">`\n\n// GOOD: use addEventListener\nconst img = document.createElement("img");\nimg.addEventListener("error", () => handleError(sanitizedInput));',
|
|
383
|
+
compliance: ["SOC2:CC7.1"],
|
|
384
|
+
},
|
|
349
385
|
];
|
|
@@ -230,4 +230,16 @@ export const deploymentRules = [
|
|
|
230
230
|
fixCode: '# BAD: breaks container isolation\n# hostNetwork: true\n# hostPID: true\n\n# GOOD: use pod networking\nspec:\n hostNetwork: false\n hostPID: false\n hostIPC: false\n containers:\n - name: app\n securityContext:\n runAsNonRoot: true',
|
|
231
231
|
compliance: ["SOC2:CC6.1", "PCI-DSS:Req6.5.10"],
|
|
232
232
|
},
|
|
233
|
+
{
|
|
234
|
+
id: "VG524",
|
|
235
|
+
name: "Data URL or Blob URL in User-Controlled src/href",
|
|
236
|
+
severity: "high",
|
|
237
|
+
owasp: "A07:2025 Cross-Site Scripting",
|
|
238
|
+
description: "User-controlled input is used in src or href attributes without blocking data: and blob: URL schemes. data:text/html URLs can embed full HTML pages with scripts, and javascript: URLs execute code — bypassing same-origin restrictions.",
|
|
239
|
+
pattern: /(?:src|href|action|poster|srcDoc)\s*=\s*\{[\s\S]{0,100}?(?:user|input|param|query|data|url|link|content)[\s\S]{0,100}?(?:(?!(?:startsWith|protocol|scheme|allowlist|whitelist|URL\()[\s\S]{0,50}?))\}/gi,
|
|
240
|
+
languages: ["javascript", "typescript"],
|
|
241
|
+
fix: "Validate URL scheme against an allowlist (https: only). Block data:, blob:, and javascript: URLs from user input.",
|
|
242
|
+
fixCode: '// Validate URL scheme\nfunction isSafeUrl(url: string): boolean {\n try {\n const parsed = new URL(url);\n return ["https:", "http:"].includes(parsed.protocol);\n } catch {\n return false;\n }\n}\n\n// Usage\n<img src={isSafeUrl(userUrl) ? userUrl : "/placeholder.png"} />',
|
|
243
|
+
compliance: ["SOC2:CC7.1"],
|
|
244
|
+
},
|
|
233
245
|
];
|
|
@@ -434,4 +434,52 @@ export const modernStackRules = [
|
|
|
434
434
|
fixCode: '// Express: trust only your reverse proxy\napp.set("trust proxy", 1); // trust first proxy\n\n// Rate limiter: use req.ip (respects trust proxy)\nimport rateLimit from "express-rate-limit";\nconst limiter = rateLimit({\n keyGenerator: (req) => req.ip, // uses trusted proxy chain\n max: 100,\n windowMs: 15 * 60 * 1000,\n});',
|
|
435
435
|
compliance: ["SOC2:CC7.1"],
|
|
436
436
|
},
|
|
437
|
+
{
|
|
438
|
+
id: "VG990",
|
|
439
|
+
name: "SVG File Upload Without Content Sanitization",
|
|
440
|
+
severity: "critical",
|
|
441
|
+
owasp: "A07:2025 Cross-Site Scripting",
|
|
442
|
+
description: "File upload accepts SVG files but does not scan or sanitize SVG content. SVG files can contain embedded <script> tags, event handlers (onload, onclick), and external resource references that execute JavaScript when the SVG is rendered in a browser.",
|
|
443
|
+
pattern: /(?:(?:allowedMimeTypes|accept|mimeTypes|fileTypes|allowedTypes|contentType)\s*[:=]\s*(?:\[[\s\S]*?|['"`])[\s\S]{0,100}?(?:svg|image\/svg|\.svg))/gi,
|
|
444
|
+
languages: ["javascript", "typescript"],
|
|
445
|
+
fix: "Either reject SVG uploads entirely or sanitize SVG content by stripping script tags, event handlers, and external references. Use a library like DOMPurify with SVG profile.",
|
|
446
|
+
fixCode: '// Option 1: Reject SVGs\nconst ALLOWED_TYPES = ["image/png", "image/jpeg", "image/webp"]; // no SVG\n\n// Option 2: Sanitize SVG content\nimport DOMPurify from "dompurify";\nconst cleanSvg = DOMPurify.sanitize(svgContent, {\n USE_PROFILES: { svg: true, svgFilters: true },\n FORBID_TAGS: ["script", "foreignObject"],\n FORBID_ATTR: ["onclick", "onerror", "onload"],\n});',
|
|
447
|
+
compliance: ["SOC2:CC7.1"],
|
|
448
|
+
},
|
|
449
|
+
{
|
|
450
|
+
id: "VG991",
|
|
451
|
+
name: "Markdown Rendered as HTML Without Sanitization",
|
|
452
|
+
severity: "high",
|
|
453
|
+
owasp: "A07:2025 Cross-Site Scripting",
|
|
454
|
+
description: "Markdown library output (marked, showdown, markdown-it, remark) is rendered as HTML without sanitization. Most markdown parsers allow raw HTML by default — user-submitted markdown like `<img onerror=alert(1)>` passes through as executable HTML.",
|
|
455
|
+
pattern: /(?:marked|showdown|markdownIt|markdown-it|unified|remark|rehype)[\s\S]{0,300}?(?:innerHTML|dangerouslySetInnerHTML|v-html|\[innerHTML\]|\.html\s*\(|res\.send)/gi,
|
|
456
|
+
languages: ["javascript", "typescript"],
|
|
457
|
+
fix: "Sanitize markdown HTML output with DOMPurify before rendering, or configure the parser to disable raw HTML.",
|
|
458
|
+
fixCode: '// BAD: unsanitized markdown\n// element.innerHTML = marked.parse(userMarkdown);\n\n// GOOD: sanitize after parsing\nimport DOMPurify from "dompurify";\nimport { marked } from "marked";\nconst html = DOMPurify.sanitize(marked.parse(userMarkdown));\n\n// Or disable HTML in parser\nmarked.setOptions({ sanitize: true });\n// markdown-it: const md = markdownIt({ html: false });',
|
|
459
|
+
compliance: ["SOC2:CC7.1"],
|
|
460
|
+
},
|
|
461
|
+
{
|
|
462
|
+
id: "VG992",
|
|
463
|
+
name: "Rich Text Editor Output Without Sanitization",
|
|
464
|
+
severity: "high",
|
|
465
|
+
owasp: "A07:2025 Cross-Site Scripting",
|
|
466
|
+
description: "WYSIWYG/rich text editor content (TipTap, Draft.js, Slate, Quill, CKEditor, TinyMCE) is rendered via innerHTML or dangerouslySetInnerHTML without sanitization. Editor output is user-controlled HTML that can contain XSS payloads — especially if the editor allows source code editing or paste from external sources.",
|
|
467
|
+
pattern: /(?:editor\.getHTML|getContent|convertToHTML|stateToHTML|serialize|draftToHtml|renderToString)[\s\S]{0,300}?(?:innerHTML|dangerouslySetInnerHTML|v-html|\[innerHTML\])/gi,
|
|
468
|
+
languages: ["javascript", "typescript"],
|
|
469
|
+
fix: "Always sanitize rich text editor output with DOMPurify before rendering, even if the editor has its own sanitization.",
|
|
470
|
+
fixCode: '// BAD: direct editor output rendering\n// <div dangerouslySetInnerHTML={{ __html: editor.getHTML() }} />\n\n// GOOD: sanitize editor output\nimport DOMPurify from "dompurify";\nconst cleanHtml = DOMPurify.sanitize(editor.getHTML(), {\n ALLOWED_TAGS: ["p", "b", "i", "em", "strong", "a", "ul", "ol", "li", "br", "h1", "h2", "h3"],\n ALLOWED_ATTR: ["href", "target", "rel"],\n});\n<div dangerouslySetInnerHTML={{ __html: cleanHtml }} />',
|
|
471
|
+
compliance: ["SOC2:CC7.1"],
|
|
472
|
+
},
|
|
473
|
+
{
|
|
474
|
+
id: "VG993",
|
|
475
|
+
name: "Upload Filename Used Without Sanitization",
|
|
476
|
+
severity: "high",
|
|
477
|
+
owasp: "A01:2025 Broken Access Control",
|
|
478
|
+
description: "User-uploaded file's original filename is used directly for storage without sanitization. Attackers can use directory traversal (../../etc/passwd), null bytes (file.php%00.jpg), double extensions (file.jpg.exe), or Unicode tricks to overwrite files, bypass type checks, or achieve remote code execution.",
|
|
479
|
+
pattern: /(?:file\.name|originalname|filename|req\.file\.originalname|formData\.get\s*\(\s*['"]file['"])\s*[\s\S]{0,100}?(?:writeFile|createWriteStream|save|upload|putObject|mv\s*\(|rename|storage)/gi,
|
|
480
|
+
languages: ["javascript", "typescript"],
|
|
481
|
+
fix: "Generate a random filename (UUID/nanoid) and validate the extension against an allowlist. Never use the original filename for storage.",
|
|
482
|
+
fixCode: 'import { randomUUID } from "crypto";\nimport path from "path";\n\n// Generate safe filename\nconst ext = path.extname(file.name).toLowerCase();\nconst ALLOWED_EXT = [".jpg", ".jpeg", ".png", ".webp", ".pdf"];\nif (!ALLOWED_EXT.includes(ext)) throw new Error("Invalid file type");\nconst safeName = `${randomUUID()}${ext}`;\nawait fs.writeFile(`/uploads/${safeName}`, buffer);',
|
|
483
|
+
compliance: ["SOC2:CC7.1"],
|
|
484
|
+
},
|
|
437
485
|
];
|
|
@@ -168,4 +168,16 @@ export const nextjsRules = [
|
|
|
168
168
|
fixCode: '// next.config.ts\nexport default {\n serverActions: {\n allowedOrigins: ["myapp.com", "*.myapp.com"],\n },\n};',
|
|
169
169
|
compliance: ["SOC2:CC6.6"],
|
|
170
170
|
},
|
|
171
|
+
{
|
|
172
|
+
id: "VG414",
|
|
173
|
+
name: "Server-Side Template Injection (SSTI)",
|
|
174
|
+
severity: "critical",
|
|
175
|
+
owasp: "A02:2025 Injection",
|
|
176
|
+
description: "User input is rendered using an unescaped template directive (EJS <%- %>, Handlebars {{{ }}}, Pug != operator, Nunjucks | safe filter). These directives bypass HTML escaping, allowing attackers to inject arbitrary HTML and JavaScript that executes server-side or client-side.",
|
|
177
|
+
pattern: /(?:<%-\s*\w|(?:\{\{\{)\s*\w|!=\s*\w[\s\S]{0,50}?(?:user|input|query|body|param|req\.|data\.)|\|\s*safe\s*(?:\}\}|\%\}))/gi,
|
|
178
|
+
languages: ["javascript", "typescript", "html"],
|
|
179
|
+
fix: "Always use escaped template directives: EJS <%= %>, Handlebars {{ }}, Pug =. Only use unescaped rendering for trusted, developer-controlled content.",
|
|
180
|
+
fixCode: '<!-- EJS: use escaped output -->\n<p><%= userInput %></p> <!-- SAFE: HTML-escaped -->\n<!-- NOT: <%- userInput %> DANGEROUS: raw HTML -->\n\n<!-- Handlebars: use double braces -->\n<p>{{userInput}}</p> <!-- SAFE: escaped -->\n<!-- NOT: {{{userInput}}} DANGEROUS: raw HTML -->\n\n<!-- Pug: use = not != -->\np= userInput //- SAFE: escaped\n//- NOT: p!= userInput DANGEROUS: raw HTML',
|
|
181
|
+
compliance: ["SOC2:CC7.1", "PCI-DSS:Req6.5.1"],
|
|
182
|
+
},
|
|
171
183
|
];
|
|
@@ -173,4 +173,16 @@ export const webSecurityRules = [
|
|
|
173
173
|
fixCode: "// Reads from OPENAI_API_KEY env automatically\nconst openai = new OpenAI();",
|
|
174
174
|
compliance: ["SOC2:CC6.1"],
|
|
175
175
|
},
|
|
176
|
+
{
|
|
177
|
+
id: "VG678",
|
|
178
|
+
name: "Missing X-Content-Type-Options Header",
|
|
179
|
+
severity: "high",
|
|
180
|
+
owasp: "A05:2021 Security Misconfiguration",
|
|
181
|
+
description: "Response serving user-uploaded files does not set X-Content-Type-Options: nosniff. Browsers may MIME-sniff the content and execute uploaded files as HTML/JavaScript, enabling stored XSS via file uploads.",
|
|
182
|
+
pattern: /(?:createReadStream|sendFile|send\s*\(|pipe\s*\(|res\.download|res\.sendFile|getSignedUrl|getPublicUrl)[\s\S]{0,500}?(?:(?!X-Content-Type-Options|nosniff)[\s\S]){10,}?(?:res\.end|\.pipe|return|response)/gi,
|
|
183
|
+
languages: ["javascript", "typescript"],
|
|
184
|
+
fix: "Set X-Content-Type-Options: nosniff on all responses serving user-uploaded content.",
|
|
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
|
+
compliance: ["SOC2:CC6.1"],
|
|
187
|
+
},
|
|
176
188
|
];
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "guardvibe",
|
|
3
|
-
"version": "1.7.
|
|
4
|
-
"description": "Security MCP for vibe coding.
|
|
3
|
+
"version": "1.7.1",
|
|
4
|
+
"description": "Security MCP for vibe coding. 277 rules, 14 tools for Next.js, Supabase, Clerk, Stripe, Prisma, tRPC, Hono, GraphQL, Convex, Turso, Uploadthing, AI SDK, and the full AI-generated stack.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"guardvibe": "build/index.js",
|