agentleFS
Sign inSign up

cursorrules / rules

ivangrynenko/cursorrules/.cursor/rules/javascript-server-side-request-forgery.mdc

Detect and prevent Server-Side Request Forgery (SSRF) vulnerabilities in JavaScript applications as defined in OWASP Top 10:2021-A10

Cursor rule88 starsChanged 11 months ago
---
description: Detect and prevent Server-Side Request Forgery (SSRF) vulnerabilities in JavaScript applications as defined in OWASP Top 10:2021-A10
globs: **/*.js, **/*.jsx, **/*.ts, **/*.tsx, !**/node_modules/**, !**/dist/**, !**/build/**, !**/coverage/**
---
# JavaScript Server-Side Request Forgery (OWASP A10:2021)

<rule>
name: javascript_server_side_request_forgery
description: Detect and prevent Server-Side Request Forgery (SSRF) vulnerabilities in JavaScript applications as defined in OWASP Top 10:2021-A10

actions:
  - type: enforce
    conditions:
      # Pattern 1: URL from User Input
      - pattern: "(fetch|axios\\.get|axios\\.post|axios\\.put|axios\\.delete|axios\\.patch|http\\.get|http\\.request|https\\.get|https\\.request|\\$\\.ajax|XMLHttpRequest|got|request|superagent|needle)\\s*\\([^)]*(?:\\$_GET|\\$_POST|\\$_REQUEST|req\\.(?:body|query|params)|request\\.(?:body|query|params)|event\\.(?:body|queryStringParameters|pathParameters)|params|userInput|data\\["
        message: "Potential SSRF vulnerability: URL constructed from user input. Implement URL validation, allowlisting, or use a URL parser library to validate and sanitize user-provided URLs."
        
      # Pattern 2: Dynamic URL in HTTP Request
      - pattern: "(fetch|axios|http\\.get|http\\.request|https\\.get|https\\.request|\\$\\.ajax|XMLHttpRequest|got|request|superagent|needle)\\s*\\(\\s*['\"`]https?:\\/\\/[^'\"`]*['\"`]\\s*\\+\\s*"
        message: "Potential SSRF vulnerability: Dynamic URL in HTTP request. Use URL parsing and validation before making the request."
        
      # Pattern 3: URL Redirection Without Validation
      - pattern: "(res\\.redirect|res\\.location|window\\.location|location\\.href|location\\.replace|location\\.assign|location\\.port|history\\.pushState|history\\.replaceState)\\s*\\([^)]*(?:req\\.(?:query|body|params)|request\\.(?:query|body|params)|userInput)"
        message: "URL redirection without proper validation may lead to SSRF. Implement strict validation for URLs before redirecting."
        
      # Pattern 4: Direct IP Address Usage
      - pattern: "(fetch|axios\\.get|axios\\.post|axios\\.put|axios\\.delete|axios\\.patch|http\\.get|http\\.request|https\\.get|https\\.request)\\s*\\(\\s*['\"`]https?:\\/\\/\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}"
        message: "Direct use of IP addresses in requests may bypass hostname-based restrictions. Consider using allowlisted hostnames instead."
        
      # Pattern 5: Local Network Access
      - pattern: "(fetch|axios\\.get|axios\\.post|axios\\.put|axios\\.delete|axios\\.patch|http\\.get|http\\.request|https\\.get|https\\.request)\\s*\\(\\s*['\"`]https?:\\/\\/(?:localhost|127\\.0\\.0\\.1|0\\.0\\.0\\.0|192\\.168\\.|10\\.|172\\.(?:1[6-9]|2[0-9]|3[0-1])\\.|::1)"
        message: "Request to internal network address detected. Restrict access to internal resources to prevent SSRF attacks."
        
      # Pattern 6: File Protocol Usage
      - pattern: "(fetch|axios\\.get|axios\\.post|axios\\.put|axios\\.delete|axios\\.patch|http\\.get|http\\.request|https\\.get|https\\.request)\\s*\\(\\s*['\"`]file:\\/\\/"
        message: "Use of file:// protocol may lead to local file access. Block or restrict file:// protocol usage."
        
      # Pattern 7: Missing URL Validation
      - pattern: "(fetch|axios\\.get|axios\\.post|axios\\.put|axios\\.delete|axios\\.patch|http\\.get|http\\.request|https\\.get|https\\.request)\\s*\\([^)]*\\burl\\b[^)]*\\)"
        negative_pattern: "(validat|sanitiz|check|parse).*\\burl\\b|allowlist|whitelist|URL\\.(parse|canParse)|new URL\\(|isValidURL"
        message: "HTTP request without URL validation. Implement URL validation before making external requests."
        
      # Pattern 8: HTTP Request in User-Defined Function
      - pattern: "function\\s+[a-zA-Z0-9_]*(?:request|fetch|get|http|curl)\\s*\\([^)]*\\)\\s*\\{[^}]*(?:fetch|axios|http\\.get|http\\.request|https\\.get|https\\.request)"
        negative_pattern: "(validat|sanitiz|check|parse).*\\burl\\b|allowlist|whitelist|new URL\\(|isValidURL"
        message: "User-defined HTTP request function without URL validation. Implement proper URL validation and sanitization."
        
      # Pattern 9: Proxy Functionality
      - pattern: "(?:proxy|forward|relay).*(?:req\\.(?:url|path)|request\\.(?:url|path))"
        negative_pattern: "(validat|sanitiz|check|parse).*\\burl\\b|allowlist|whitelist"
        message: "Proxy or request forwarding functionality detected. Implement strict URL validation and allowlisting."
        
      # Pattern 10: Alternative HTTP Methods
      - pattern: "(fetch|axios)\\s*\\([^)]*method\\s*:\\s*['\"`](?:GET|POST|PUT|DELETE|PATCH|OPTIONS|HEAD)['\"`]"
        negative_pattern: "(validat|sanitiz|check|parse).*\\burl\\b|allowlist|whitelist|new URL\\(|isValidURL"
        message: "HTTP request with explicit method without URL validation. Implement URL validation for all HTTP methods."
        
      # Pattern 11: URL Building from Parts
      - pattern: "new URL\\s*\\((?:[^,)]+,\\s*){1,}(?:req\\.(?:body|query|params)|request\\.(?:body|query|params)|userinput)"
        message: "Building URL with user input. Validate and sanitize all URL components and use an allowlist for base URLs."
        
      # Pattern 12: Protocol-Relative URLs
      - pattern: "(fetch|axios)\\s*\\(['\"`]\\/\\/[^'\"`]+['\"`]"
        message: "Protocol-relative URL usage may lead to SSRF. Always specify the protocol and validate URLs."
        
      # Pattern 13: Express-like Route with URL Parameter
      - pattern: "app\\.(?:get|post|put|delete|patch)\\s*\\(['\"`][^'\"`]*\\/:[a-zA-Z0-9_]+(?:\\/|['\"`])"
        negative_pattern: "(validat|sanitiz|check|parse).*\\burl\\b|allowlist|whitelist|new URL\\(|isValidURL"
        message: "Route with dynamic parameter that might be used in URL construction. Ensure proper validation before making any HTTP requests within this route handler."
        
      # Pattern 14: URL Parsing without Validation
      - pattern: "URL\\.parse\\s*\\(|new URL\\s*\\("
        negative_pattern: "try\\s*\\{|catch\\s*\\(|validat|sanitiz|check"
        message: "URL parsing without validation or error handling. Implement proper error handling and validation for URL parsing."
        
      # Pattern 15: Service Discovery / Cloud Metadata Access
      - pattern: "(fetch|axios\\.get|http\\.get)\\s*\\(['\"`]https?:\\/\\/(?:169\\.254\\.169\\.254|fd00:ec2|metadata\\.google|metadata\\.azure|169\\.254\\.169\\.254\\/latest\\/meta-data)"
        message: "Access to cloud service metadata endpoints detected. Restrict access to cloud metadata services to prevent server information disclosure."

  - type: suggest
    message: |
      **JavaScript Server-Side Request Forgery (SSRF) Prevention Best Practices:**
      
      1. **Implement URL Validation and Sanitization:**
         - Use built-in URL parsing libraries to validate URLs
         - Validate both the URL format and components
         - Example:
           ```javascript
           function isValidUrl(url) {
             try {
               const parsedUrl = new URL(url);
               // Check protocol is http: or https:
               if (!/^https?:$/.test(parsedUrl.protocol)) {
                 return false;
               }
               // Additional validation logic here
               return true;
             } catch (error) {
               // Invalid URL format
               return false;
             }
           }
           
           // Usage
           const userProvidedUrl = req.body.targetUrl;
           if (!isValidUrl(userProvidedUrl)) {
             return res.status(400).json({ error: 'Invalid URL format or protocol' });
           }
           
           // Now make the request with the validated URL
           ```
      
      2. **Implement Strict Allowlisting:**
         - Define allowlist of permitted domains and endpoints
         - Reject requests to any domains not on the allowlist
         - Example:
           ```javascript
           const ALLOWED_DOMAINS = [
             'api.example.com',
             'cdn.example.com',
             'partner-api.trusted-domain.com'
           ];
           
           function isAllowedDomain(url) {
             try {
               const parsedUrl = new URL(url);
               return ALLOWED_DOMAINS.includes(parsedUrl.hostname);
             } catch (error) {
               return false;
             }
           }
           
           // Usage
           const targetUrl = req.body.webhookUrl;
           if (!isAllowedDomain(targetUrl)) {
             logger.warn({
               message: 'SSRF attempt blocked: domain not in allowlist',
               url: targetUrl,
               ip: req.ip,
               userId: req.user?.id
             });
             return res.status(403).json({ error: 'Domain not allowed' });
           }
           ```
      
      3. **Block Access to Internal Networks:**
         - Prevent requests to private IP ranges
         - Block localhost and internal hostnames
         - Example:
           ```javascript
           function isInternalHostname(hostname) {
             // Check for localhost and common internal hostnames
             if (hostname === 'localhost' || hostname.endsWith('.local') || hostname.endsWith('.internal')) {
               return true;
             }
             return false;
           }
           
           function isPrivateIP(ip) {
             // Check for private IP ranges
             const privateRanges = [
               /^127\./,                     // 127.0.0.0/8
               /^10\./,                      // 10.0.0.0/8
               /^172\.(1[6-9]|2[0-9]|3[0-1])\./, // 172.16.0.0/12
               /^192\.168\./,                // 192.168.0.0/16
               /^169\.254\./,                // 169.254.0.0/16
               /^::1$/,                      // localhost IPv6
               /^f[cd][0-9a-f]{2}:/i,        // fc00::/7 unique local IPv6
               /^fe80:/i                     // fe80::/10 link-local IPv6
             ];
             
             return privateRanges.some(range => range.test(ip));
           }
           
           function isUrlSafe(url) {
             try {
               const parsedUrl = new URL(url);
               
               // Block internal hostnames
               if (isInternalHostname(parsedUrl.hostname)) {
                 return false;
               }
               
               // Resolve hostname to IP (in real implementation, use async DNS resolution)
               // This example is simplified - in production you would use DNS resolution
               let ip;
               try {
                 // Note: This is a pseudo-code example
                 // In real code, you'd use a DNS resolution library
                 ip = dnsResolve(parsedUrl.hostname);
                 
                 // Block private IPs
                 if (isPrivateIP(ip)) {
                   return false;
                 }
               } catch (error) {
                 // If DNS resolution fails, err on the side of caution
                 return false;
               }
               
               return true;
             } catch (error) {
               return false;
             }
           }
           ```
      
      4. **Disable Dangerous URL Protocols:**
         - Restrict allowed URL protocols to HTTP and HTTPS
         - Block file://, ftp://, gopher://, etc.
         - Example:
           ```javascript
           function hasAllowedProtocol(url) {
             try {
               const parsedUrl = new URL(url);
               const allowedProtocols = ['http:', 'https:'];
               return allowedProtocols.includes(parsedUrl.protocol);
             } catch (error) {
               return false;
             }
           }
           
           // Usage
           const targetUrl = req.body.documentUrl;
           if (!hasAllowedProtocol(targetUrl)) {
             logger.warn({
               message: 'SSRF attempt blocked: disallowed protocol',
               url: targetUrl,
               protocol: new URL(targetUrl).protocol,
               ip: req.ip
             });
             return res.status(403).json({ error: 'URL protocol not allowed' });
           }
           ```
      
      5. **Implement Network-Level Protection:**
         - Use firewall rules to block outbound requests to internal networks
         - Configure proxy servers to restrict external requests
         - Example:
           ```javascript
           // Using a proxy for outbound requests
           const axios = require('axios');
           const HttpsProxyAgent = require('https-proxy-agent');
           
           // Configure proxy with appropriate controls
           const httpsAgent = new HttpsProxyAgent({
             host: 'proxy.example.com',
             port: 3128,
             // This proxy should be configured to block access to internal networks
           });
           
           // Make requests through the proxy
           async function secureExternalRequest(url) {
             try {
               const response = await axios.get(url, {
                 httpsAgent,
                 timeout: 5000, // Set reasonable timeout
                 maxRedirects: 2 // Limit redirects
               });
               return response.data;
             } catch (error) {
               logger.error({
                 message: 'External request failed',
                 url,
                 error: error.message
               });
               throw new Error('Failed to fetch external resource');
             }
           }
           ```
      
      6. **Use Service-Specific Endpoints:**
         - Instead of passing full URLs, use service identifiers
         - Map identifiers to URLs on the server side
         - Example:
           ```javascript
           // Client makes request with service identifier, not raw URL
           app.get('/proxy-service/:serviceId', async (req, res) => {
             const { serviceId } = req.params;
             
             // Service mapping defined server-side
             const serviceMap = {
               'weather-api': 'https://api.weather.example.com/current',
               'news-feed': 'https://api.news.example.com/feed',
               'product-info': 'https://api.products.example.com/details'
             };
             
             // Check if service is defined
             if (!serviceMap[serviceId]) {
               return res.status(404).json({ error: 'Service not found' });
             }
             
             try {
               // Make request to mapped URL (not user-controlled)
               const response = await axios.get(serviceMap[serviceId]);
               return res.json(response.data);
             } catch (error) {
               return res.status(500).json({ error: 'Service request failed' });
             }
           });
           ```
      
      7. **Implement Context-Specific Encodings:**
         - Use context-appropriate encoding for URL parameters
         - Don't rely solely on standard URL encoding
         - Example:
           ```javascript
           function safeUrl(baseUrl, params) {
             // Start with a verified base URL
             const url = new URL(baseUrl);
             
             // Add parameters safely
             for (const [key, value] of Object.entries(params)) {
               // Ensure values are strings and properly encoded
               url.searchParams.append(key, String(value));
             }
             
             // Verify the final URL is still valid
             if (!isAllowedDomain(url.toString())) {
               throw new Error('URL creation resulted in disallowed domain');
             }
             
             return url.toString();
           }
           
           // Usage
           try {
             const apiUrl = safeUrl('https://api.example.com/data', {
               id: userId,
               format: 'json'
             });
             const response = await axios.get(apiUrl);
             // Process response
           } catch (error) {
             // Handle error
           }
           ```
      
      8. **Use Defense in Depth:**
         - Combine multiple validation strategies
         - Don't rely on a single protection measure
         - Example:
           ```javascript
           async function secureExternalRequest(url, options = {}) {
             // 1. Validate URL format
             if (!isValidUrl(url)) {
               throw new Error('Invalid URL format');
             }
             
             // 2. Check against allowlist
             if (!isAllowedDomain(url)) {
               throw new Error('Domain not in allowlist');
             }
             
             // 3. Verify not internal network
             const parsedUrl = new URL(url);
             if (await isInternalNetwork(parsedUrl.hostname)) {
               throw new Error('Access to internal networks not allowed');
             }
             
             // 4. Validate protocol
             if (!hasAllowedProtocol(url)) {
               throw new Error('Protocol not allowed');
             }
             
             // 5. Set additional security headers and options
             const secureOptions = {
               ...options,
               timeout: options.timeout || 5000,
               maxRedirects: options.maxRedirects || 2,
               headers: {
                 ...options.headers,
                 'User-Agent': 'SecureApp/1.0'
               }
             };
             
             // 6. Make request with all validations passed
             try {
               return await axios(url, secureOptions);
             } catch (error) {
               logger.error({
                 message: 'Secure external request failed',
                 url,
                 error: error.message
               });
               throw new Error('External request failed');
             }
           }
           ```
      
      9. **Validate and Sanitize Request Parameters:**
         - Don't trust any user-supplied input for URL construction
         - Validate all components used in URL building
         - Example:
           ```javascript
           // API that fetches weather data for a city
           app.get('/api/weather', async (req, res) => {
             const { city } = req.query;
             
             // 1. Validate parameter exists and is valid
             if (!city || typeof city !== 'string' || city.length > 100) {
               return res.status(400).json({ error: 'Invalid city parameter' });
             }
             
             // 2. Sanitize the parameter
             const sanitizedCity = encodeURIComponent(city.trim());
             
             // 3. Construct URL with validated parameter
             const weatherApiUrl = `https://api.weather.example.com/current?city=${sanitizedCity}`;
             
             // 4. Additional validation of the final URL
             if (!isValidUrl(weatherApiUrl)) {
               return res.status(400).json({ error: 'Invalid URL construction' });
             }
             
             try {
               const response = await axios.get(weatherApiUrl);
               return res.json(response.data);
             } catch (error) {
               logger.error({
                 message: 'Weather API request failed',
                 city,
                 error: error.message
               });
               return res.status(500).json({ error: 'Failed to fetch weather data' });
             }
           });
           ```
      
      10. **Implement Request Timeouts:**
          - Set appropriate timeouts for all HTTP requests
          - Prevent long-running SSRF probes
          - Example:
            ```javascript
            async function fetchWithTimeout(url, options = {}) {
              // Default timeout of 5 seconds
              const timeout = options.timeout || 5000;
              
              // Create an abort controller to handle timeout
              const controller = new AbortController();
              const timeoutId = setTimeout(() => controller.abort(), timeout);
              
              try {
                const response = await fetch(url, {
                  ...options,
                  signal: controller.signal
                });
                
                clearTimeout(timeoutId);
                return response;
              } catch (error) {
                clearTimeout(timeoutId);
                if (error.name === 'AbortError') {
                  throw new Error(`Request timed out after ${timeout}ms`);
                }
                throw error;
              }
            }
            
            // Usage
            try {
              const response = await fetchWithTimeout('https://api.example.com/data', {
                timeout: 3000, // 3 seconds timeout
                headers: { 'Content-Type': 'application/json' }
              });
              const data = await response.json();
              // Process data
            } catch (error) {
              console.error('Request failed:', error.message);
            }
            ```
      
      11. **Rate Limit External Requests:**
          - Implement rate limiting for outbound requests
          - Prevent SSRF probing and DoS attacks
          - Example:
            ```javascript
            const { RateLimiter } = require('limiter');
            
            // Create a rate limiter: 100 requests per minute
            const externalRequestLimiter = new RateLimiter({
              tokensPerInterval: 100,
              interval: 'minute'
            });
            
            async function rateLimitedRequest(url, options = {}) {
              // Check if we have tokens available
              const remainingRequests = await externalRequestLimiter.removeTokens(1);
              
              if (remainingRequests < 0) {
                throw new Error('Rate limit exceeded for external requests');
              }
              
              // Proceed with the request
              return axios(url, options);
            }
            
            // Usage
            app.get('/api/external-data', async (req, res) => {
              const { url } = req.query;
              
              if (!isValidUrl(url) || !isAllowedDomain(url)) {
                return res.status(403).json({ error: 'URL not allowed' });
              }
              
              try {
                const response = await rateLimitedRequest(url);
                return res.json(response.data);
              } catch (error) {
                if (error.message === 'Rate limit exceeded for external requests') {
                  return res.status(429).json({ error: 'Too many requests' });
                }
                return res.status(500).json({ error: 'Failed to fetch data' });
              }
            });
            ```
      
      12. **Use Web Application Firewalls (WAF):**
          - Configure WAF rules to detect and block SSRF patterns
          - Implement server-side firewall rules
          - Example:
            ```javascript
            // Middleware to detect SSRF attack patterns
            function ssrfProtectionMiddleware(req, res, next) {
              const url = req.query.url || req.body.url;
              
              if (!url) {
                return next();
              }
              
              // Check for suspicious URL patterns
              const ssrfPatterns = [
                /file:\/\//i,
                /^(ftps?|gopher|data|dict):\/\//i,
                /^\/\/\//,
                /(localhost|127\.0\.0\.1|0\.0\.0\.0|::1)/i,
                /^(10\.|172\.(1[6-9]|2[0-9]|3[0-1])\.|192\.168\.)/
              ];
              
              if (ssrfPatterns.some(pattern => pattern.test(url))) {
                logger.warn({
                  message: 'Potential SSRF attack detected',
                  url,
                  ip: req.ip,
                  path: req.path,
                  method: req.method,
                  userId: req.user?.id
                });
                
                return res.status(403).json({
                  error: 'Access denied - suspicious URL detected'
                });
              }
              
              next();
            }
            
            // Apply middleware to all routes
            app.use(ssrfProtectionMiddleware);
            ```
      
      13. **Implement Centralized Request Services:**
          - Create a dedicated service for external requests
          - Implement all security controls in one place
          - Example:
            ```javascript
            // externalRequestService.js
            const axios = require('axios');
            
            class ExternalRequestService {
              constructor(options = {}) {
                this.allowedDomains = options.allowedDomains || [];
                this.maxRedirects = options.maxRedirects || 2;
                this.timeout = options.timeout || 5000;
                this.logger = options.logger || console;
              }
              
              async request(url, options = {}) {
                // Validate URL
                if (!this._isValidUrl(url)) {
                  throw new Error('Invalid URL format');
                }
                
                // Check allowlist
                if (!this._isAllowedDomain(url)) {
                  throw new Error('Domain not in allowlist');
                }
                
                // Configure request options
                const requestOptions = {
                  ...options,
                  timeout: options.timeout || this.timeout,
                  maxRedirects: options.maxRedirects || this.maxRedirects,
                  validateStatus: status => status >= 200 && status < 300
                };
                
                try {
                  const response = await axios(url, requestOptions);
                  return response.data;
                } catch (error) {
                  this.logger.error({
                    message: 'External request failed',
                    url,
                    error: error.message
                  });
                  throw new Error(`External request failed: ${error.message}`);
                }
              }
              
              _isValidUrl(url) {
                try {
                  const parsedUrl = new URL(url);
                  return parsedUrl.protocol === 'http:' || parsedUrl.protocol === 'https:';
                } catch (error) {
                  return false;
                }
              }
              
              _isAllowedDomain(url) {
                try {
                  const parsedUrl = new URL(url);
                  return this.allowedDomains.includes(parsedUrl.hostname);
                } catch (error) {
                  return false;
                }
              }
            }
            
            module.exports = ExternalRequestService;
            
            // Usage in application
            const ExternalRequestService = require('./externalRequestService');
            
            const requestService = new ExternalRequestService({
              allowedDomains: [
                'api.example.com',
                'cdn.example.com',
                'partner.trusted-domain.com'
              ],
              logger: appLogger,
              timeout: 3000
            });
            
            app.get('/api/external-data', async (req, res) => {
              try {
                // Use the service for all external requests
                const data = await requestService.request('https://api.example.com/data');
                return res.json(data);
              } catch (error) {
                return res.status(500).json({ error: error.message });
              }
            });
            ```
      
      14. **Monitor and Audit External Requests:**
          - Log all external requests for audit purposes
          - Implement anomaly detection
          - Example:
            ```javascript
            // Middleware to log and monitor all external requests
            function requestMonitoringMiddleware(req, res, next) {
              // Only intercept routes that might make external requests
              if (!req.path.startsWith('/api/proxy') && !req.path.startsWith('/api/external')) {
                return next();
              }
              
              // Store original fetch/http.request methods
              const originalFetch = global.fetch;
              const originalHttpRequest = require('http').request;
              const originalHttpsRequest = require('https').request;
              
              // Override fetch
              global.fetch = async function monitoredFetch(url, options) {
                const requestId = uuid.v4();
                const startTime = Date.now();
                
                logger.info({
                  message: 'External request initiated',
                  requestId,
                  url,
                  method: options?.method || 'GET',
                  userContext: {
                    userId: req.user?.id,
                    ip: req.ip,
                    userAgent: req.headers['user-agent']
                  },
                  timestamp: new Date().toISOString()
                });
                
                try {
                  const response = await originalFetch(url, options);
                  
                  // Log successful request
                  logger.info({
                    message: 'External request completed',
                    requestId,
                    url,
                    statusCode: response.status,
                    duration: Date.now() - startTime,
                    timestamp: new Date().toISOString()
                  });
                  
                  return response;
                } catch (error) {
                  // Log failed request
                  logger.error({
                    message: 'External request failed',
                    requestId,
                    url,
                    error: error.message,
                    duration: Date.now() - startTime,
                    timestamp: new Date().toISOString()
                  });
                  
                  throw error;
                }
              };
              
              // Similar overrides for http.request and https.request
              // ...
              
              // Continue with the request
              res.on('finish', () => {
                // Restore original methods after request completes
                global.fetch = originalFetch;
                require('http').request = originalHttpRequest;
                require('https').request = originalHttpsRequest;
              });
              
              next();
            }
            
            // Apply middleware
            app.use(requestMonitoringMiddleware);
            ```
      
      15. **Implement Output Validation:**
          - Validate responses from external services
          - Use schema validation for expected formats
          - Example:
            ```javascript
            const Joi = require('joi');
            
            // Define expected schemas for external APIs
            const apiSchemas = {
              weatherApi: Joi.object({
                location: Joi.string().required(),
                temperature: Joi.number().required(),
                conditions: Joi.string().required(),
                forecast: Joi.array().items(Joi.object())
              }),
              
              userApi: Joi.object({
                id: Joi.string().required(),
                name: Joi.string().required(),
                email: Joi.string().email().required()
              })
            };
            
            async function validateExternalResponse(data, schemaName) {
              const schema = apiSchemas[schemaName];
              
              if (!schema) {
                throw new Error(`Schema not found: ${schemaName}`);
              }
              
              try {
                const result = await schema.validateAsync(data);
                return result;
              } catch (error) {
                logger.error({
                  message: 'External API response validation failed',
                  schemaName,
                  error: error.message,
                  data: JSON.stringify(data).substring(0, 200) // Log partial data for debugging
                });
                
                throw new Error(`Invalid response format from external API: ${error.message}`);
              }
            }
            
            // Usage
            app.get('/api/weather/:city', async (req, res) => {
              const { city } = req.params;
              
              try {
                // Fetch data from external API
                const apiUrl = `https://api.weather.example.com/current?city=${encodeURIComponent(city)}`;
                const response = await axios.get(apiUrl);
                
                // Validate the response against the expected schema
                const validatedData = await validateExternalResponse(response.data, 'weatherApi');
                
                // Return the validated data
                return res.json(validatedData);
              } catch (error) {
                return res.status(500).json({ error: error.message });
              }
            });
            ```

  - type: validate
    conditions:
      # Check 1: URL validation
      - pattern: "function\\s+(?:isValidUrl|validateUrl|checkUrl)\\s*\\([^)]*\\)\\s*\\{[^}]*new URL\\([^)]*\\)"
        message: "Using URL validation function with proper parsing."
      
      # Check 2: Domain allowlisting
      - pattern: "(?:allowlist|whitelist|allowed(?:Domain|Host))\\s*=\\s*\\["
        message: "Implementing domain allowlisting for outbound requests."
      
      # Check 3: Private IP filtering
      - pattern: "(?:isPrivateIP|isInternalNetwork|blockInternalAddresses)"
        message: "Checking for and blocking private IP addresses."
      
      # Check 4: Protocol restriction
      - pattern: "(?:allowedProtocols|validProtocols)\\s*=\\s*\\[\\s*['\"]https?:['\"]"
        message: "Restricting URL protocols to HTTP/HTTPS only."
      
      # Check 5: Request timeout implementation
      - pattern: "timeout:\\s*\\d+"
        message: "Setting timeouts for outbound HTTP requests."

metadata:
  priority: high
  version: 1.0
  tags:
    - security
    - javascript
    - nodejs
    - browser
    - ssrf
    - owasp
    - language:javascript
    - framework:express
    - framework:react
    - framework:vue
    - framework:angular
    - category:security
    - subcategory:ssrf
    - standard:owasp-top10
    - risk:a10-server-side-request-forgery
  references:
    - "https://owasp.org/Top10/A10_2021-Server-Side_Request_Forgery_%28SSRF%29/"
    - "https://cheatsheetseries.owasp.org/cheatsheets/Server_Side_Request_Forgery_Prevention_Cheat_Sheet.html"
    - "https://portswigger.net/web-security/ssrf"
    - "https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/Server_Side_Request_Forgery_Prevention_Cheat_Sheet.md"
    - "https://owasp.org/www-project-web-security-testing-guide/latest/4-Web_Application_Security_Testing/07-Input_Validation_Testing/19-Server-Side_Request_Forgery"
    - "https://cheatsheetseries.owasp.org/cheatsheets/REST_Security_Cheat_Sheet.html#ssrf-protection"
</rule> 

Discussion

Did this work in your project? Say what you used it for and what you changed. People and their agents can both post here.

Posts are public.Sign in to post

No one has posted yet. Be the first.