highYour server fetching attacker-chosen URLs

Server Fetches a URL Supplied by the Request

The server will make a request to any address the caller supplies.

Why it matters

Your server sits inside your network and next to your cloud metadata service, database and internal admin tools. A fetch to an address chosen by the caller can reach those places on the caller's behalf. Limiting the destination to hosts you expect keeps the feature and removes the exposure.

What it looks like

This is the shape of code that triggers the rule. AI tools produce it because it works, and nothing tells them it is unsafe.

fetch of req.body.url in an Express handler
const express = require('express');
const app = express();
app.post('/preview', async (req, res) => {
  const r = await fetch(req.body.url);
  res.send(await r.text());
});

The smallest fix

minimal patch
const target = new URL(req.body.url);
const ALLOWED_HOSTS = ['api.github.com', 'images.example.com'];
if (!['http:', 'https:'].includes(target.protocol) || !ALLOWED_HOSTS.includes(target.hostname)) {
  return res.status(400).json({ error: 'That address is not allowed' });
}
const upstream = await fetch(target.toString(), { redirect: 'manual' });

The better fix

If you have a few more minutes, this is the approach that holds up as the app grows.

safe alternative
// For open-ended features (link previews), resolve the hostname first
// and reject private and loopback ranges, disable redirects, and put a
// short timeout and a size limit on the response. Libraries such as
// "ssrf-req-filter" or "private-ip" do the range check for you.

Let your AI tool fix it

When the scanner finds this in your project, it fills in the file and line for you. This is the prompt it gives you to paste into Claude Code, Cursor or whatever you use.

In [the file] at line [the line number] the server fetches a URL from the request: [the flagged code]. Parse it with new URL(), allow only http/https, and either compare the hostname to an allow-list or reject private, loopback and link-local addresses after DNS resolution. Disable automatic redirects, add a timeout and a response size limit, and return 400 for anything rejected.

How to check the fix worked

1. Send a request with url=http://127.0.0.1:3000/ and confirm a 400.
2. Send url=http://169.254.169.254/ and confirm a 400.
3. Send an allowed URL and confirm the feature still works.

Further reading