guardvibe 3.1.10 → 3.1.11
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
|
@@ -392,7 +392,7 @@ export const coreRules = [
|
|
|
392
392
|
severity: "high",
|
|
393
393
|
owasp: "A10:2025 Server-Side Request Forgery",
|
|
394
394
|
description: "User-controlled input is passed directly to fetch(), axios, or http.request() as the URL. Attackers can make the server request internal services (169.254.169.254 for cloud metadata, localhost admin panels, internal APIs) leading to data exfiltration or remote code execution.",
|
|
395
|
-
pattern: /(
|
|
395
|
+
pattern: /(?:\bfetch|axios\.(?:get|post|put|delete|patch|request)|got(?:\.(?:get|post|put|delete|patch))?|http\.(?:get|request)|https\.(?:get|request)|urllib\.request\.urlopen)\s*\(\s*(?!["'`]https?:\/\/|["'`]\/|`\/|`\$\{process\.env|new\s+URL)(?:[a-zA-Z_$]\w*)\s*[,)]/gi,
|
|
396
396
|
languages: ["javascript", "typescript", "python"],
|
|
397
397
|
fix: "Validate URLs against an allowlist of trusted domains. Block private/internal IP ranges (10.x, 172.16-31.x, 192.168.x, 127.x, 169.254.x, ::1). Use a URL parser to check the hostname before making the request.",
|
|
398
398
|
fixCode: '// Validate URL before fetching\nconst ALLOWED_HOSTS = ["api.example.com", "cdn.example.com"];\nconst parsed = new URL(userUrl);\nif (!ALLOWED_HOSTS.includes(parsed.hostname)) {\n throw new Error("Blocked: untrusted host");\n}\n// Also block private IPs\nconst ip = await dns.resolve(parsed.hostname);\nif (isPrivateIP(ip)) throw new Error("Blocked: internal host");\nawait fetch(parsed.href);',
|
|
@@ -321,6 +321,18 @@ export function analyzeCode(code, language, framework, filePath, configDir, rule
|
|
|
321
321
|
/(?:Date\.now\(\)|timestamp|uuid|nanoid|crypto\.randomUUID)[\s\S]{0,80}?\.\s*(?:ext|split|pop)/i.test(code);
|
|
322
322
|
const isPeerDeps = /["']peerDependencies["']/i.test(code);
|
|
323
323
|
const codeHasAuthSession = /(?:supabase\.auth\.getUser|supabase\.auth\.getSession|getServerSession|auth\(\)|getSession\(\)|currentUser\(\))/i.test(code);
|
|
324
|
+
// Variables assigned a hardcoded URL literal in the same file
|
|
325
|
+
// (`let requestUrl = "https://graph.microsoft.com/..."`). Used by VG120 to
|
|
326
|
+
// skip server-side fetch calls whose URL is a compile-time constant. Built
|
|
327
|
+
// once per file — re-running the regex per match was the dominant cost when
|
|
328
|
+
// a file had many fetch sites.
|
|
329
|
+
const literalUrlVars = new Set();
|
|
330
|
+
if (/\bhttps?:\/\//.test(code)) {
|
|
331
|
+
const literalUrlAssignRe = /\b(?:const|let|var)\s+([a-zA-Z_$][\w$]*)\s*(?::\s*[\w<>[\]| ]+)?\s*=\s*["'`]https?:\/\//g;
|
|
332
|
+
let lit;
|
|
333
|
+
while ((lit = literalUrlAssignRe.exec(code)) !== null)
|
|
334
|
+
literalUrlVars.add(lit[1]);
|
|
335
|
+
}
|
|
324
336
|
// Config: check custom auth function names from .guardviberc
|
|
325
337
|
if (!codeHasAuthGuard && config.authFunctions && config.authFunctions.length > 0) {
|
|
326
338
|
const customPattern = new RegExp(`(?:${config.authFunctions.join("|")})\\s*\\(`, "i");
|
|
@@ -953,6 +965,25 @@ export function analyzeCode(code, language, framework, filePath, configDir, rule
|
|
|
953
965
|
if (isClientFile)
|
|
954
966
|
continue;
|
|
955
967
|
}
|
|
968
|
+
// Skip VG120 (SSRF) when the file is a React client component. SSRF requires the
|
|
969
|
+
// server to make the request; browser-side fetch in a client component runs from
|
|
970
|
+
// the user's own machine, so an attacker controlling the URL is just talking to
|
|
971
|
+
// their own network. Same client-marker shape used by VG106/VG407/VG678 narrowings.
|
|
972
|
+
if (rule.id === "VG120") {
|
|
973
|
+
const isClientFile = /^['"]use client['"]/.test(code.trimStart()) ||
|
|
974
|
+
/\b(?:useState|useEffect|useReducer|useRef|useMemo|useCallback|useContext|useTransition|useSyncExternalStore|useLayoutEffect)\s*\(/.test(code);
|
|
975
|
+
if (isClientFile)
|
|
976
|
+
continue;
|
|
977
|
+
// Skip when the URL variable is assigned a hardcoded literal in the same file
|
|
978
|
+
// (`let requestUrl = "https://..."` then `fetch(requestUrl)`). Mirrors the
|
|
979
|
+
// v3.1.7 VG409 literal-redirect skip — same shape, different sink. The set of
|
|
980
|
+
// literal-URL vars is built once per file (see top of analyzeCode).
|
|
981
|
+
if (literalUrlVars.size > 0) {
|
|
982
|
+
const urlVar = /(?:fetch|axios\.\w+|got(?:\.\w+)?|http\.\w+|https\.\w+|urllib\.request\.urlopen)\s*\(\s*([a-zA-Z_$][\w$]*)/.exec(match[0]);
|
|
983
|
+
if (urlVar && literalUrlVars.has(urlVar[1]))
|
|
984
|
+
continue;
|
|
985
|
+
}
|
|
986
|
+
}
|
|
956
987
|
// Skip VG1005 (.or() filter injection) when all interpolated variables are
|
|
957
988
|
// server-verified auth IDs (user.id, session.user.id, auth.uid, currentUser.id)
|
|
958
989
|
if (rule.id === "VG1005" && codeHasAuthSession) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "guardvibe",
|
|
3
|
-
"version": "3.1.
|
|
3
|
+
"version": "3.1.11",
|
|
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",
|