Skip to content
Security
Skill

/sast-ssrf

Detect Server-Side Request Forgery (SSRF) vulnerabilities in a codebase using a three-phase approach: recon (find outbound call sites), batched verify (trace user input to destinations in parallel subagents, 3 sites each), and merge (consolidate batch results). Requires

From plugin
sast-skills
1.3k16 skills
Install
$ npx -y skills add utkusen/sast-skills --skill sast-ssrf --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/sast-ssrf

Context preview

The summary Claude sees to decide when to auto-load this skill.

Detect Server-Side Request Forgery (SSRF) vulnerabilities in a codebase using a three-phase approach: recon (find outbound call sites), batched verify (trace user input to destinations in parallel subagents, 3 sites each), and merge (consolidate batch results). Requires

SKILL.md

sast-ssrf.SKILL.md
name: sast-ssrf
description: >-
  Detect Server-Side Request Forgery (SSRF) vulnerabilities in a codebase using
  a three-phase approach: recon (find outbound call sites), batched verify (trace
  user input to destinations in parallel subagents, 3 sites each), and merge
  (consolidate batch results). Requires sast/architecture.md (run sast-analysis
  first). Outputs findings to sast/ssrf-results.md. Use when asked to find SSRF
  or server-side request forgery bugs.

Server-Side Request Forgery (SSRF) Detection

You are performing a focused security assessment to find SSRF vulnerabilities in a codebase. This skill uses a three-phase approach with subagents: **recon** (find all places that make outbound TCP, DNS, or HTTP requests), **batched verify** (trace whether user-supplied input reaches those call sites, in parallel batches of 3), and **merge** (consolidate batch reports into one file).

**Prerequisites**: `sast/architecture.md` must exist. Run the analysis skill first if it doesn't.

---

What is SSRF

SSRF occurs when an attacker can cause the server to make outbound network requests to an arbitrary destination — including internal services, cloud metadata endpoints, or other external targets — by supplying or influencing the URL, hostname, IP, or port used in a server-side request.

The core pattern: *unvalidated, user-controlled input reaches the destination argument of an outbound network call.*

What SSRF IS

  • HTTP client calls where the URL or host is built from user input: `requests.get(user_url)`
  • Fetching a resource whose location is provided by the client: `fetch(req.body.webhook_url)`
  • DNS lookups on a hostname supplied by the user: `dns.lookup(req.query.host)`
  • Raw TCP connections to a host/port derived from user input: `socket.connect((user_host, user_port))`
  • File-fetching functions used with HTTP/FTP URLs from user input: `file_get_contents($user_url)`
  • URL redirectors that forward to a user-supplied destination without validation
  • Webhooks, import-from-URL, screenshot services, PDF renderers, image proxies — any feature that fetches a remote resource on behalf of the user

What SSRF is NOT

Do not flag these:

  • **Open redirects**: Redirecting the browser (HTTP 302) to a user-supplied URL — that's a client-side redirect, not a server-side request
  • **XSS via URL**: Rendering a user-supplied URL in an `<a>` tag without escaping — that's XSS
  • **IDOR**: Accessing another user's data by changing an object ID — separate vulnerability class
  • **Hardcoded outbound calls**: HTTP requests to fixed, fully hardcoded URLs with no user influence — not SSRF

Patterns That Prevent SSRF

When you see these patterns, the code is likely **not vulnerable**:

**1. Strict allowlist of permitted destinations**

ALLOWED_HOSTS = {"api.example.com", "cdn.example.com"}
parsed = urlparse(user_url)
if parsed.hostname not in ALLOWED_HOSTS:
    raise ValueError("Destination not allowed")
requests.get(user_url)

**2. Allowlist of permitted URL prefixes / schemes**

ALLOWED_PREFIXES = ["https://api.example.com/", "https://cdn.example.com/"]
if not any(user_url.startswith(p) for p in ALLOWED_PREFIXES):
    abort(400)
requests.get(user_url)

**3. No user influence on the destination**

# Destination fully hardcoded — no user input involved
response = requests.get("https://api.thirdparty.com/data")

> **Note**: IP blocklists (blocking 169.254.0.0/16, 10.0.0.0/8, etc.) are **not** sufficient protection — they can be bypassed via DNS rebinding, URL encoding, IPv6 notation, decimal IP representation, or redirect chains. Do not treat a blocklist as making a site safe; classify it as Likely Vulnerable.

---

Vulnerable vs. Secure Examples

Python — requests

# VULNERABLE: URL fully controlled by user
@app.route('/fetch')
def fetch():
    url = request.args.get('url')
    response = requests.get(url)
    return response.text

# SECURE: strict allowlist on destination host
ALLOWED = {"api.example.com"}
@app.route('/fetch')
def fetch():
    url = request.args.get('url')
    if urlparse(url).hostname not in ALLOWED:
        abort(403)
    response = requests.get(url)
    return response.text

Python — urllib

# VULNERABLE: user controls the URL passed to urlopen
def preview(request):
    target = request.GET.get('target')
    data = urllib.request.urlopen(target).read()
    return HttpResponse(data)

# SECURE: only allow https scheme to a hardcoded host
def preview(request):
    target = request.GET.get('target')
    parsed = urlparse(target)
    if parsed.scheme != 'https' or parsed.hostname != 'media.example.com':
        return HttpResponse(status=400)
    data = urllib.request.urlopen(target).read()
    return HttpResponse(data)

Node.js — fetch / axios

// VULNERABLE: webhook URL comes directly from request body
app.post('/webhook/test', async (req, res) => {
  const { url } = req.body;
  const result = await fetch(url);
  res.json(await result.json());
});

// SECURE: allowlist check before fetch
const ALLOWED_HOSTS = new Set(['hooks.example.com']);
app.post('/webhook/test', async (req, res) => {
  const { url } = req.body;
  const { hostname } = new URL(url);
  if (!ALLOWED_HOSTS.has(hostname)) return res.status(403).send('Forbidden');
  const result = await fetch(url);
  res.json(await result.json());
});

Node.js — http.request

// VULNERABLE: host and path from query string
app.get('/proxy', (req, res) => {
  const { host, path } = req.query;
  http.get({ host, path }, (proxyRes) => proxyRes.pipe(res));
});

Ruby on Rails — Net::HTTP / OpenURI

# VULNERABLE: open() fetches arbitrary URL
def import
  url = params[:url]
  content = URI.open(url).read  # also triggers for open(url) via Kernel#open
  # ...
end

# SECURE: restrict scheme and host
def import
  url = params[:url]
  uri = URI.parse(url)
  raise "Forbidden" unless uri.is_a?(URI:
Read more
Ships withsast-skills

A collection of agent skills that turn your LLM coding assistant into a fully functional SAST scanner to find vulnerabilities in your codebase. Works natively with Claude Code, Codex, Opencode, Cursor and any other assistant that supports agent skills.

Get the whole plugin
Stats
1,266
Stars
61
Forks
Maintained
Maintenance
MIT
License
4mo ago
Last commit
4mo ago
Created

Repo: utkusen/sast-skills

Other skills on sast-skills.