ESLint InterlaceESLint Interlace
Plugin: lambda-securityRules

no-user-controlled-requests

ESLint rule documentation for no-user-controlled-requests

📡 Live from GitHub — This documentation is fetched directly from no-user-controlled-requests.md and cached for 6 hours.

Keywords: SSRF, Server-Side Request Forgery, fetch, axios, user input, CWE-918, Lambda, serverless CWE: CWE-918
OWASP: A10:2021-Server-Side Request Forgery

Detect HTTP requests with user-controlled URLs (SSRF)

Detects HTTP requests where the URL contains user-controlled input from Lambda events. This rule is part of eslint-plugin-lambda-security and provides LLM-optimized error messages.

🚨 Security rule | 💡 Provides suggestions | ⚠️ Set to error in recommended

Quick Summary

AspectDetails
CWE ReferenceCWE-918 (SSRF)
CVSS Score9.1 Critical
Auto-Fix💡 Suggests URL allowlist
CategorySecurity
Best ForLambda functions making outbound HTTP requests

Vulnerability and Risk

Vulnerability: When a Lambda function makes HTTP requests using URLs derived from user input (event body, query parameters, headers), attackers can redirect those requests to internal services or external systems.

Risk: SSRF attacks can:

  • Access internal AWS metadata (http://169.254.169.254/)
  • Reach VPC-internal services not exposed to the internet
  • Exfiltrate data through controlled external servers
  • Perform port scanning of internal networks

Rule Details

This rule tracks data flow from Lambda event properties to HTTP client calls, detecting when user-controlled data reaches URL parameters.

Why This Matters

RiskImpactSolution
🔓 Metadata AccessAWS credentials from IMDS endpointBlock 169.254.x.x addresses
🏠 Internal AccessReach VPC services, databasesUse URL allowlist
📤 Data ExfiltrationSend data to attacker-controlled serversValidate and sanitize URLs

Configuration

OptionTypeDefaultDescription
allowInTestsbooleantrueAllow in test files
{
  rules: {
    'lambda-security/no-user-controlled-requests': ['error', {
      allowInTests: true
    }]
  }
}

Examples

❌ Incorrect

export const handler = async (event) => {
  const body = JSON.parse(event.body);

  // User controls the URL - CRITICAL SSRF
  const response = await fetch(body.webhookUrl); // ❌

  // User input in template literal
  const data = await axios.get(
    `https://${event.queryStringParameters.domain}/api`,
  ); // ❌

  // Object config with user-controlled URL
  await axios({
    url: body.targetUrl, // ❌ User-controlled
    method: 'POST',
    data: payload,
  });
};

✅ Correct

const ALLOWED_HOSTS = ['api.trusted.com', 'webhook.partner.com'];

export const handler = async (event) => {
  const body = JSON.parse(event.body);

  // Validate URL against allowlist
  const url = new URL(body.webhookUrl);
  if (!ALLOWED_HOSTS.includes(url.host)) {
    // ✅ Allowlist check
    return { statusCode: 400, body: 'Invalid webhook URL' };
  }

  // Block internal addresses
  if (isInternalUrl(url)) {
    // ✅ Internal address check
    return { statusCode: 400, body: 'Invalid URL' };
  }

  const response = await fetch(url.toString());
  return { statusCode: 200, body: await response.text() };
};

function isInternalUrl(url) {
  const host = url.hostname;
  return (
    host === 'localhost' ||
    host === '127.0.0.1' ||
    host.startsWith('169.254.') || // Link-local (IMDS)
    host.startsWith('10.') || // Private Class A
    host.startsWith('192.168.') || // Private Class C
    host.match(/^172\.(1[6-9]|2[0-9]|3[0-1])\./) // Private Class B
  );
}

SSRF in AWS Lambda Context

AWS Metadata Service (IMDS)

// Attacker payload: { "url": "http://169.254.169.254/latest/meta-data/iam/security-credentials/" }
// This exposes temporary AWS credentials attached to the Lambda role

// Always block these patterns:
const BLOCKED_PATTERNS = [
  /^169\.254\./, // Link-local (IMDS)
  /^fd[0-9a-f]{2}:/i, // IPv6 link-local
  /\.internal$/, // AWS internal DNS
  /\.amazonaws\.com$/, // AWS services (unless specifically allowed)
];

VPC Internal Services

// Attacker payload: { "url": "http://internal-api.vpc:3000/admin" }
// This reaches services not exposed to the internet

// Use strict allowlisting for internal access patterns

Security Impact

VulnerabilityCWEOWASPCVSSImpact
SSRF918A10:20219.1 CriticalInternal service access
Credential Theft522A07:20218.6 HighAWS credential exposure

Known False Negatives

Multi-Step Taint Flow

Why: Complex data transformations not fully tracked.

// ❌ NOT DETECTED - multi-step flow
const userInput = event.body;
const parsed = JSON.parse(userInput);
const url = buildUrl(parsed); // Lost track of taint
await fetch(url);

Mitigation: Apply validation at the point of URL construction.

Further Reading

On this page

No Headings