guardvibe 3.1.19 → 3.1.21
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/build/data/rules/core.js
CHANGED
|
@@ -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|
|
|
299
|
+
pattern: /(?:Object\.assign|\bmerge\b|deepMerge|\bextend\b)\s*\([^)]*(?:req\.|request\.|\bbody\b|\bparams\b)/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');",
|
|
@@ -313,7 +313,7 @@ export function analyzeCode(code, language, framework, filePath, configDir, rule
|
|
|
313
313
|
/import\s+.*(?:verifyCron|cronAuth|validateCron|checkCron)/i.test(code);
|
|
314
314
|
const codeHasRedirectValidation = /(?:sanitize|validate|verify|check|safe|allowed)(?:Redirect|RedirectUrl|CallbackUrl)\s*\(/i.test(code) ||
|
|
315
315
|
/import\s+.*(?:sanitizeRedirect|validateRedirect|safeRedirect)/i.test(code);
|
|
316
|
-
const isMigrationFile = filePath ? /(?:migrations?|
|
|
316
|
+
const isMigrationFile = filePath ? /(?:\/(?:migrations?|migrate|drizzle|seeds?|fixtures)\/|supabase\/migrations\/)/i.test(filePath) : false;
|
|
317
317
|
const isSqlSchemaFile = filePath ? /(?:schema|migration|seed|ddl|init).*\.sql$/i.test(filePath) : false;
|
|
318
318
|
const isReactNative = /(?:react-native|from\s+['"]react-native['"]|from\s+['"]expo|import\s+.*\bexpo\b)/i.test(code);
|
|
319
319
|
const codeHasTimingSafeEqual = /(?:timingSafeEqual|timing.?safe|constant.?time)/i.test(code);
|
|
@@ -369,8 +369,8 @@ export function analyzeCode(code, language, framework, filePath, configDir, rule
|
|
|
369
369
|
// - VG010/VG011/VG013/VG014: injection rules trigger on payload strings like
|
|
370
370
|
// agent.get('/?q=' + sqlPayload) which match the regex but aren't database calls
|
|
371
371
|
// - VG042/VG678: HTTP-response/security-header rules (tests don't serve to real users)
|
|
372
|
-
const isTestFile = filePath && /(?:\.(?:[\w-]+-)?(?:spec|test|e2e|stories|cy)\.(?:ts|tsx|js|jsx|mjs|cjs)$|\/__tests__\/|\/tests?\/|\/cypress\/|\/playwright
|
|
373
|
-
if (isTestFile && ["VG001", "VG062", "VG010", "VG011", "VG012", "VG013", "VG014", "VG042", "VG100", "VG130", "VG678", "VG955", "VG133", "VG1021", "VG409"].includes(rule.id))
|
|
372
|
+
const isTestFile = filePath && /(?:\.(?:[\w-]+-)?(?:spec|test|e2e|stories|cy)\.(?:ts|tsx|js|jsx|mjs|cjs)$|_test\.go$|\/__tests__\/|\/tests?\/|\/cypress\/|\/playwright\/|\/dockertest\/|\/testutil\/|\/testhelpers?\/|\/testfixtures?\/)/i.test(filePath);
|
|
373
|
+
if (isTestFile && ["VG001", "VG003", "VG062", "VG010", "VG011", "VG012", "VG013", "VG014", "VG042", "VG100", "VG130", "VG678", "VG955", "VG133", "VG1021", "VG409"].includes(rule.id))
|
|
374
374
|
continue;
|
|
375
375
|
// VG955 (Missing Pagination on List Endpoint): only fire on actual request-handling
|
|
376
376
|
// surfaces — API routes, App Router `route.{ts,tsx}`, pages/api, or Server Actions.
|
|
@@ -416,7 +416,14 @@ export function analyzeCode(code, language, framework, filePath, configDir, rule
|
|
|
416
416
|
const isAdminRoute = filePath && /\/admin\//i.test(filePath);
|
|
417
417
|
// Server-side batch context: scripts, migrations, seeds. These run offline or
|
|
418
418
|
// on-deploy, not against user requests, so DoS-from-unbounded-results doesn't apply.
|
|
419
|
-
const isBatchScriptFile = filePath && /\/(?:scripts?|migrations?|seeds?|fixtures?)\//i.test(filePath);
|
|
419
|
+
const isBatchScriptFile = filePath && /\/(?:scripts?|migrations?|seeds?|fixtures?|benchmarks?)\//i.test(filePath);
|
|
420
|
+
// Code-generator/scaffold templates. CLI tools (create-t3-app, create-next-app,
|
|
421
|
+
// create-react-app, etc.) bundle "Hello World" example files under cli/template/
|
|
422
|
+
// or templates/ that are intentionally minimal — no auth, no input validation,
|
|
423
|
+
// no rate limiting. These get copied into user projects where the user is
|
|
424
|
+
// expected to customize them. Flagging them in the CLI tool's own audit produces
|
|
425
|
+
// noise without surfacing real production risk.
|
|
426
|
+
const isTemplateFile = filePath && /\/(?:templates?|scaffolds?|stubs?|boilerplate)\//i.test(filePath);
|
|
420
427
|
// Skip rate-limit rules when the file installs a global rate limiter via app.use().
|
|
421
428
|
// Covers `app.use(rateLimit({...}))`, `app.use(limiter)`, `app.use('/api', rateLimit({...}))`,
|
|
422
429
|
// and named middleware vars matching limiter naming conventions.
|
|
@@ -490,6 +497,19 @@ export function analyzeCode(code, language, framework, filePath, configDir, rule
|
|
|
490
497
|
// request handler to authorize. Rule still fires inside route handlers and Server Actions.
|
|
491
498
|
if (rule.id === "VG1008" && isBatchScriptFile)
|
|
492
499
|
continue;
|
|
500
|
+
// Skip VG124 (Insecure Random for Security Token) in benchmark/seed/script files —
|
|
501
|
+
// Math.random() in benchmarks (`keys[Math.floor(Math.random() * keys.length)]` to pick
|
|
502
|
+
// a random test key) and seeders (`storeEncryptedKeys: Math.random() > 0.7` for fixture
|
|
503
|
+
// distribution) is intentional fixture randomness, not a security token. Real tokens
|
|
504
|
+
// generated in scripts should still use crypto.randomBytes — but the rule's keyword list
|
|
505
|
+
// (`token|key|code|...`) over-matches non-security `key` references in test code.
|
|
506
|
+
if (rule.id === "VG124" && isBatchScriptFile)
|
|
507
|
+
continue;
|
|
508
|
+
// Skip tRPC educational/scaffold rules (VG970 publicProcedure-DB, VG971 missing-input)
|
|
509
|
+
// in template/scaffold files. CLI tools like create-t3-app ship intentionally simple
|
|
510
|
+
// examples under cli/template/ that the user is expected to replace before deploying.
|
|
511
|
+
if ((rule.id === "VG970" || rule.id === "VG971") && isTemplateFile)
|
|
512
|
+
continue;
|
|
493
513
|
// Skip VG961 (z.any/z.unknown) in batch scripts and cron routes — `data: z.any()` and
|
|
494
514
|
// similar opaque fields in migration/seed scripts and cron job payloads are intentional
|
|
495
515
|
// passthroughs (e.g. Tinybird `tb.buildPipe({ parameters: ..., data: z.any() })`),
|
|
@@ -842,6 +862,23 @@ export function analyzeCode(code, language, framework, filePath, configDir, rule
|
|
|
842
862
|
if (/\b\w*Ref\.current\b/.test(matchedLine))
|
|
843
863
|
continue;
|
|
844
864
|
}
|
|
865
|
+
// VG1021 (AI Tool Schema Enum from User Input): skip when the variable passed to
|
|
866
|
+
// z.enum / "enum": is declared as a static literal array (`const X = [...] as const`,
|
|
867
|
+
// `const X = [...]`) elsewhere in the file. The existing pattern's lowercase-identifier
|
|
868
|
+
// heuristic correctly catches `userActions` style variables, but Zod's idiomatic pattern
|
|
869
|
+
// `const commonStringOperators = ["is", "contains"] as const; z.enum(commonStringOperators)`
|
|
870
|
+
// also has a lowercase-start name and is fully compile-time-static.
|
|
871
|
+
if (rule.id === "VG1021") {
|
|
872
|
+
const varMatch = match[0].match(/(?:z\.enum\s*\(\s*(?:\.\.\.)?|enum["']\s*:\s*)([a-z_$][\w$]*)/);
|
|
873
|
+
if (varMatch) {
|
|
874
|
+
const varName = varMatch[1];
|
|
875
|
+
// Detect: `const NAME = [` (literal array) or `const NAME: SomeType = [`. The array
|
|
876
|
+
// can span multiple lines so just check for the `[` on the assignment.
|
|
877
|
+
const declRe = new RegExp(`(?:const|let|var)\\s+${varName}\\s*(?::\\s*[\\w<>[\\],\\s|.]+)?\\s*=\\s*\\[`);
|
|
878
|
+
if (declRe.test(code))
|
|
879
|
+
continue;
|
|
880
|
+
}
|
|
881
|
+
}
|
|
845
882
|
// VG850 (AI Prompt Injection via User Input): only fire when at least one of the
|
|
846
883
|
// interpolations in the system-prompt template literal looks like user input. The
|
|
847
884
|
// pattern matches any `${...}` interpolation, but apps commonly compose system
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "guardvibe",
|
|
3
|
-
"version": "3.1.
|
|
3
|
+
"version": "3.1.21",
|
|
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",
|