UNPKG

vibesec

Version:

Security scanner for AI-generated code - detects vulnerabilities in vibe-coded projects

355 lines (307 loc) 10.9 kB
# SSRF (Server-Side Request Forgery) Security Rules # Detects server-side requests using unsanitized user input rules: - id: ssrf-fetch-user-input name: SSRF via fetch/axios with User Input description: Making HTTP requests to user-controlled URLs allows SSRF attacks severity: critical category: injection languages: - javascript - typescript enabled: true patterns: - regex: "fetch\\s*\\(\\s*req\\.(body|query|params)\\." flags: gi - regex: "fetch\\s*\\(\\s*[`\"].*\\$\\{\\s*req\\.(body|query|params)" flags: gi - regex: "axios\\.(get|post|put|delete)\\s*\\(\\s*req\\.(body|query|params)\\." flags: gi - regex: "axios\\.(get|post|put|delete)\\s*\\(\\s*[`\"].*\\$\\{\\s*req\\.(body|query|params)" flags: gi fix: template: | Validate and whitelist URLs before making requests. Before: const response = await fetch(req.body.url); After: const allowedDomains = ['api.example.com', 'cdn.example.com']; function isUrlSafe(url) { try { const parsed = new URL(url); // Block private IPs const privateIpRegex = /^(10\.|172\.(1[6-9]|2[0-9]|3[01])\.|192\.168\.|127\.|169\.254\.|::1|fc00:)/; if (privateIpRegex.test(parsed.hostname)) { return false; } // Whitelist domains return allowedDomains.includes(parsed.hostname); } catch { return false; } } if (!isUrlSafe(req.body.url)) { return res.status(400).json({ error: 'Invalid URL' }); } const response = await fetch(req.body.url); references: - https://owasp.org/www-community/attacks/Server_Side_Request_Forgery - https://cheatsheetseries.owasp.org/cheatsheets/Server_Side_Request_Forgery_Prevention_Cheat_Sheet.html metadata: cwe: CWE-918 owasp: "A10:2021" tags: - ssrf - fetch - http-request - id: ssrf-http-request name: SSRF via HTTP Request Libraries description: HTTP requests with user-controlled URLs in request/got/node-fetch severity: critical category: injection languages: - javascript - typescript enabled: true patterns: - regex: "request\\s*\\(\\s*\\{[^}]*url\\s*:\\s*req\\.(body|query|params)" flags: gi - regex: "got\\s*\\(\\s*req\\.(body|query|params)\\." flags: gi - regex: "got\\.(get|post)\\s*\\(\\s*[`\"].*\\$\\{\\s*req\\.(body|query|params)" flags: gi - regex: "http(s)?\\.get\\s*\\(\\s*req\\.(body|query|params)" flags: gi fix: template: | Implement URL validation before making HTTP requests. Before: const request = require('request'); request(req.body.webhook_url, callback); After: const { URL } = require('url'); function validateUrl(urlString, allowedHosts) { try { const url = new URL(urlString); // Only allow HTTPS if (url.protocol !== 'https:') { return false; } // Block private networks const hostname = url.hostname.toLowerCase(); if (hostname === 'localhost' || hostname.startsWith('192.168.') || hostname.startsWith('10.') || hostname.startsWith('172.')) { return false; } // Whitelist allowed hosts return allowedHosts.includes(hostname); } catch { return false; } } const allowedHosts = ['hooks.slack.com', 'discord.com']; if (!validateUrl(req.body.webhook_url, allowedHosts)) { return res.status(400).json({ error: 'Invalid webhook URL' }); } request(req.body.webhook_url, callback); references: - https://owasp.org/www-community/attacks/Server_Side_Request_Forgery - https://cwe.mitre.org/data/definitions/918.html metadata: cwe: CWE-918 owasp: "A10:2021" tags: - ssrf - http-client - request - id: ssrf-python-requests name: SSRF in Python Requests description: Python requests library with user-controlled URLs severity: critical category: injection languages: - python enabled: true patterns: - regex: "requests\\.(get|post|put|delete)\\s*\\(\\s*request\\.(args|form|json)\\." flags: gi - regex: "requests\\.(get|post|put|delete)\\s*\\(\\s*f[\"'].*\\{.*request\\.(args|form|json)" flags: gi - regex: "urllib\\.request\\.urlopen\\s*\\(.*request\\.(args|form|json)" flags: gi fix: template: | Validate URLs and block private networks in Python. Before: import requests response = requests.get(request.args.get('url')) After: import requests from urllib.parse import urlparse import ipaddress def is_safe_url(url_string, allowed_domains): try: parsed = urlparse(url_string) # Only allow HTTPS if parsed.scheme != 'https': return False # Block private IPs try: ip = ipaddress.ip_address(parsed.hostname) if ip.is_private or ip.is_loopback or ip.is_link_local: return False except ValueError: pass # Not an IP, check domain # Whitelist domains return parsed.hostname in allowed_domains except Exception: return False url = request.args.get('url') allowed = ['api.example.com', 'cdn.example.com'] if not is_safe_url(url, allowed): return jsonify({'error': 'Invalid URL'}), 400 response = requests.get(url, timeout=5) references: - https://owasp.org/www-community/attacks/Server_Side_Request_Forgery - https://cwe.mitre.org/data/definitions/918.html metadata: cwe: CWE-918 owasp: "A10:2021" tags: - ssrf - python - requests - id: ssrf-redirect-follow name: Unsafe Redirect Following in HTTP Requests description: Following redirects without validation allows SSRF via redirect chains severity: high category: injection languages: - javascript - typescript - python enabled: true patterns: - regex: "fetch\\s*\\([^,)]*,\\s*\\{[^}]*redirect\\s*:\\s*[\"']follow[\"']" flags: gi - regex: "axios\\s*\\(\\s*\\{[^}]*maxRedirects\\s*:\\s*[1-9]" flags: gi - regex: "requests\\.(get|post)\\s*\\([^,)]*,\\s*allow_redirects\\s*=\\s*True" flags: gi fix: template: | Disable redirects or validate each redirect URL. Before: const response = await fetch(url, { redirect: 'follow' }); After: // Option 1: Disable redirects const response = await fetch(url, { redirect: 'manual' }); // Option 2: Use custom redirect handler async function safeFetch(url) { let response = await fetch(url, { redirect: 'manual' }); if (response.status >= 300 && response.status < 400) { const redirectUrl = response.headers.get('location'); if (!isUrlSafe(redirectUrl)) { throw new Error('Unsafe redirect'); } return safeFetch(redirectUrl); } return response; } references: - https://owasp.org/www-community/attacks/Server_Side_Request_Forgery metadata: cwe: CWE-918 owasp: "A10:2021" tags: - ssrf - redirects - id: ssrf-image-processing name: SSRF via Image/File Processing description: Processing images or files from user-provided URLs severity: high category: injection languages: - javascript - typescript - python enabled: true patterns: - regex: "sharp\\s*\\(\\s*req\\.(body|query|params)\\." flags: gi - regex: "jimp\\.read\\s*\\(\\s*req\\.(body|query|params)\\." flags: gi - regex: "Image\\.open\\s*\\(.*request\\.(args|form|json)" flags: gi - regex: "cv2\\.imread\\s*\\(.*request\\.(args|form|json)" flags: gi fix: template: | Validate URLs before processing images/files. Before: const image = await sharp(req.body.imageUrl).resize(200, 200).toBuffer(); After: const allowedDomains = ['cdn.example.com', 'images.example.com']; if (!isUrlSafe(req.body.imageUrl, allowedDomains)) { return res.status(400).json({ error: 'Invalid image URL' }); } // Download with timeout and size limits const response = await fetch(req.body.imageUrl, { signal: AbortSignal.timeout(5000) }); const contentLength = response.headers.get('content-length'); if (contentLength && parseInt(contentLength) > 10_000_000) { throw new Error('Image too large'); } const buffer = await response.arrayBuffer(); const image = await sharp(buffer).resize(200, 200).toBuffer(); references: - https://owasp.org/www-community/attacks/Server_Side_Request_Forgery metadata: cwe: CWE-918 owasp: "A10:2021" tags: - ssrf - image-processing - file-upload - id: ssrf-dns-rebinding name: DNS Rebinding Vulnerability description: Making requests without DNS rebinding protection severity: medium category: injection languages: - javascript - typescript enabled: true patterns: - regex: "fetch\\s*\\([^)]*\\)(?!.*dns.*cache|.*resolve)" flags: gi fix: template: | Implement DNS rebinding protection by caching DNS resolution. Before: const response = await fetch(userUrl); After: const dns = require('dns').promises; async function safeFetch(url) { const parsed = new URL(url); // Resolve hostname once const addresses = await dns.resolve4(parsed.hostname); // Check if resolved IP is private if (addresses.some(ip => isPrivateIp(ip))) { throw new Error('Private IP detected'); } // Make request using resolved IP return fetch(url); } references: - https://owasp.org/www-community/attacks/Server_Side_Request_Forgery - https://portswigger.net/web-security/ssrf metadata: cwe: CWE-918 owasp: "A10:2021" tags: - ssrf - dns-rebinding