Sast SsrfBLOCK
Collection of agent skills to find vulnerabilities inside your web/mobile apps.
Overview
Collection of agent skills to find vulnerabilities inside your web/mobile apps.
d9c848ebb5f1OBSERVED · 2026-10-13What it tells the agent
The instruction file, verbatim from the audited commit — this is the text the model reads, and the surface the audit's instruction layer examines. Quoted here so you can judge it without cloning anything.
---
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**
```python
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**
```python
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**
```python
# 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
```python
# 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
```python
# 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
```javascript
// 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
```javascript
// 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
```ruby
# 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:Trust audit
BLOCKgrade D · trust 69/100 Do not install this without reading the findings. The audit found something that could harm you or your machine.
| Layer | What it checks | Result |
|---|---|---|
| L0 | Provenance & inventory | PASS |
| L1 | Static analysis of the code | NA |
| L2 | Instruction surface (what it tells the agent) | PASS |
| L3 | Class-specific surface | PASS |
| L4 | Behavioural (sandbox) | SKIPPED |
What the source does
- Filesystem
- none-observed
- Network
- none-observed
- Shell
- none-observed
- Dependencies
- pinned
- Secrets in source
- none-found
Findings (4)
> - **Blocklist of private IP ranges / metadata endpoints**: `169.254.169.254`, `10.0.0.0/8`, `192.168.0.0/16`, etc. — **not** sufficient. Bypassable via DNS rebinding, alternate IP representations, a
> - **Impact**: [What an attacker can do — access cloud metadata at 169.254.169.254, pivot to internal services, port scan the internal network, exfiltrate data, bypass firewalls, etc.]
> Example: curl "https://app.example.com/fetch?url=http://169.254.169.254/latest/meta-data/"
> Example: curl "https://app.example.com/fetch?url=http://169.254.169.254/latest/meta-data/"
Gates applied: no_behavioural_pass.
d9c848ebb5f1full audit observations/trust-audit/skill/utkusen__sast-ssrf.json · Report an issue / request a re-scanAudit history
Every audit this skill has had.
| Date | Source | Verdict | Grade | Score | Change |
|---|---|---|---|---|---|
| 2026-10-13 | d9c848ebb5f1 | BLOCK | D | 69 | first audit |
Questions
What does the Sast Ssrf skill do?
Collection of agent skills to find vulnerabilities inside your web/mobile apps.
Is Sast Ssrf safe to install?
No — not without reading the findings first. The audit graded it D (69/100) and found 3 critical or high issues in the source. Each one is listed on this page with the file and line it is on.
What can Sast Ssrf access on my machine?
The audit observed no filesystem, network or shell use at all in its source.
How current is this page?
The grade is for one exact copy of the source (d9c848ebb5f1), read on 2026-10-13. The repository is watched, and a new audit runs when it changes — this is the first audit.