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).
Install / Use
npx skills add utkusen/sast-skills --skill sast-ssrfInstalls into whichever agent you are using.
SKILL.md
Installable skill definition
Quality Score
Category
SecuritySupported Platforms
Our assessment of sast-ssrf
sast-ssrf scores 89/100 on our quality scale, 493rd of 971 Security skills we index.
Its SKILL.md is 26 KB long, well organised into 33 sections with 15 code examples: a thorough specification that gives an agent plenty to work with.
With 1,321 GitHub stars, it is one of the more widely adopted skills in the catalogue.
Maintenance, license and trust
- The repository was last updated about 6 months ago. That is recent enough to be usable, but agent tooling moves fast, so check the instructions against your agent's current version.
- It is released under the MIT license, a permissive license that allows use, modification and commercial use with attribution.
- Its trust signals score 98/100, with no cautions. These come from repository metadata, not a code audit — read the skill file before letting an agent act on it.
sast-ssrf compared with similar skills
All 4 of these similar skills score higher than sast-ssrf; compare them before choosing.
| Skill | Score | Stars | Updated | Format |
|---|---|---|---|---|
| sast-ssrf (this skill)by utkusen | 89 | 1.3k | 6mo ago | SKILL.md |
| algorithmic-artby anthropics | 100 | 177.9k | 8d ago | SKILL.md |
| pptxby anthropics | 100 | 177.9k | 8d ago | SKILL.md |
| designby nextlevelbuilder | 100 | 130.2k | 9d ago | SKILL.md |
| ui-ux-pro-maxby nextlevelbuilder | 100 | 130.2k | 9d ago | SKILL.md |
Frequently asked questions
- How do I install sast-ssrf?
- Run
npx skills add utkusen/sast-skills --skill sast-ssrf. The install tabs above show the steps for each supported agent. - Which AI agents does sast-ssrf work with?
- It is written for Universal, as a SKILL.md file. Other agents that read the same format can often use it too.
- Is sast-ssrf safe to use?
- It is MIT-licensed and scores 98/100 on trust signals. Skills are instructions an agent will follow, so read the file before installing it and do not approve commands you do not understand.
- Is sast-ssrf still maintained?
- The repository was last updated about 6 months ago. That is recent enough to be usable, but agent tooling moves fast, so check the instructions against your agent's current version.
Skill content
View source on GitHubname: 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::HTTPS) && uri.host == "data.example.com"
content = uri.open.read
# ...
end
PHP — cURL
// VULNERABLE: user-supplied URL piped into curl
function fetch_preview($url) {
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$result = curl_exec($ch);
curl_close($ch);
return $result;
}
// Called as: fetch_preview($_GET['url'])
// SECURE: validate URL against allowlist before curl
function fetch_preview($url) {
$allowed = ['https://cdn.example.com/'];
foreach ($allowed as $prefix) {
if (strpos($url, $prefix) === 0) {
// ... proceed with curl
}
}
throw new Exception("Destination not allowed");
}
PHP — file_get_contents
// VULNERABLE: file_get_contents with http:// wrapper and user input
$url = $_GET['source'];
$data = file_get_contents($url); // fetches remote URL if scheme is http/https/ftp
Java — Spring / OkHttp
// VULNERABLE: RestTemplate with user-controlled URL
@GetMapping("/proxy")
public ResponseEntity<String> proxy(@RequestParam String url) {
RestTemplate restTemplate = new RestTemplate();
return restTemplate.getForEntity(url, String.class);
}
// VULNERABLE: OkHttp with user-controlled host
public String fetch(String host, String path) {
Request request = new Request.Builder()
.url("https://" + host + path)
.build();
return client.newCall(request).execute().body().string();
}
Go — net/http
// VULNERABLE: user-supplied URL passed to http.Get
func proxyHandler(w http.ResponseWriter, r *http.Request) {
target := r.URL.Query().Get("url")
resp, err := http.Get(target)
if err != nil {
http.Error(w, err.Error(), 500)
return
}
io.Copy(w, resp.Body)
}
// VULNERABLE: user controls host in net.Dial
func dialHandler(w http.ResponseWriter, r *http.Request) {
host := r.URL.Query().Get("host")
port := r.URL.Query().Get("port")
conn, _ := net.Dial("tcp", host+":"+port)
// ...
}
C# — HttpClient
// VULNERABLE: user-supplied URL passed to HttpClient
[HttpGet("proxy")]
public async Task<IActionResult> Proxy([FromQuery] string url)
{
var response = await _httpClient.GetAsync(url);
var content = await response.Content.ReadAsStringAsync();
return Content(content);
}
Execution
This skill runs in three phases using subagents. Pass the contents of sast/architecture.md to all subagents as context.
Phase 1: Find All Outbound Network Call Sites
Launch a subagent with the following instructions:
Goal: Find every location in the codebase where the application makes an outbound network request — HTTP, HTTPS, FTP, TCP, or DNS — regardless of whether that destination is user-controlled. Write results to
sast/ssrf-recon.md.Context: You will be given the project's architecture summary. Use it to understand the tech stack, HTTP client libraries in use, and any networking or webhook-related components.
What to search for — outbound request call sites:
You are looking for any code that opens a network connection or fetches a remote resource. Flag ANY call where a non-trivially-hardcoded URL, host, or address value is passed as an argument. You are not yet tracing whether that value is user-controlled; that is Phase 2's job.
- Python HTTP clients:
requests.get(url),requests.post(url),requests.put(url),requests.request(method, url),requests.Session().get(url)urllib.request.urlopen(url),urllib2.urlopen(url)httpx.get(url),httpx.post(url),httpx.AsyncClient().get(url)aiohttp.ClientSession().get(url),aiohttp.ClientSession().post(url)- Python socket / DNS:
socket.connect((host, port)),socket.create_connection((host, port))dns.resolver.resolve(name),socket.getaddrinfo(host, ...)- Python file-fetching with remote schemes:
urllib.request.urlopen(url)where url may be http/https/ftpopen(url)viafrom urllib.request import urlopenor similar (flag if url may be remote)- Node.js / JavaScript HTTP clients:
fetch(url),node-fetch(url)axios.get(url),axios.post(url),axios.request({url})http.get(url),https.get(url),http.request(options),https.request(options)got(url),superagent.get(url),needle.get(url),undici.request(url)require('request')(options)- Node.js socket / DNS:
net.createConnection({host, port}),net.connect(port, host)dns.lookup(hostname, ...),dns.resolve(hostname, ...),dns.resolve4(hostname)- Ruby HTTP clients:
Net::HTTP.get(uri),Net::HTTP.start(host, ...),Net::HTTP.get_response(url)URI.open(url),open(url)(Kernel#open / OpenURI)RestClient.get(url),RestClient::Resource.new(url)Faraday.new(url).get(path),HTTParty.get(url)Typhoeus::Request.new(url)- PHP HTTP clients and file functions:
curl_setopt($ch, CURLOPT_URL, $url)followed bycurl_exec($ch)file_get_contents($url)— flag when$urlmay be an http/https/ftp URLfopen($url, 'r')with a remote URL schemeGuzzle:$client->request('GET', $url),$client->get($url)Symfony HttpClient:$client->request('GET', $url)- Java HTTP clients:
new URL(url).openConnection(),new URL(url).openStream()HttpURLConnection/HttpsURLConnectionwith a dynamic URLOkHttpClient().newCall(new Request.Builder().url(url)...)RestTemplate.getForObject(url, ...),RestTemplate.getForEntity(url, ...)WebClient.get().uri(url),WebClient.create(url)Apache HttpClient:httpClient.execute(new HttpGet(url))- Go HTTP clients and network dials:
http.Get(url), `http.Post(url, ...)
Truncated for display — read the full file on GitHub.
Related Skills
algorithmic-art
177.9kCreating algorithmic art using p5.js with seeded randomness and interactive parameter exploration. Use this when users request creating art using code, generative art, algorithmic art, flow fields, or particle systems.
pptx
177.9kUse this skill any time a .pptx or .potx file is involved in any way — as input, output, or both. This includes: creating slide decks, pitch decks, or presentations; reading, parsing, or extracting text from any .pptx or .potx file (even if the extracted content will be used elsewhere, like in an em…
design
130.2kComprehensive design skill: brand identity, design tokens, UI styling, logo generation (55 styles, Gemini, Atlas Cloud, or MuAPI AI), corporate identity program (50 deliverables, CIP mockups), HTML presentations (Chart.js), banner design (22 styles, social/ads/web/print), icon design (15 styles, SVG…
ui-ux-pro-max
130.2kUI/UX design intelligence for web, mobile, and desktop. This skill should be used when designing, building, reviewing, or fixing interfaces, including pages, components, design systems, accessibility, interaction, responsive layout, typography, color, charts, and stack-specific UI implementation.
Trust signals
From repository metadata: license, adoption, age and documentation. Not a code audit — see the Safety scan above for what the skill file itself contains.
