/sast-pathtraversal
Detect path traversal vulnerabilities in a codebase using a three-phase approach: recon (find file-loading sinks with dynamic paths), batched verify (trace user input and mitigations in parallel subagents, 3 sinks each), and merge (consolidate batch results). Requires
$ npx -y skills add utkusen/sast-skills --skill sast-pathtraversal --agent claude-codeHow 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-pathtraversal
Context preview
The summary Claude sees to decide when to auto-load this skill.
Detect path traversal vulnerabilities in a codebase using a three-phase approach: recon (find file-loading sinks with dynamic paths), batched verify (trace user input and mitigations in parallel subagents, 3 sinks each), and merge (consolidate batch results). Requires
SKILL.md
sast-pathtraversal.SKILL.mdname: sast-pathtraversal
description: >-
Detect path traversal vulnerabilities in a codebase using a three-phase
approach: recon (find file-loading sinks with dynamic paths), batched verify
(trace user input and mitigations in parallel subagents, 3 sinks each), and
merge (consolidate batch results). Requires sast/architecture.md (run
sast-analysis first). Outputs findings to sast/pathtraversal-results.md. Use
when asked to find path traversal, directory traversal, or file disclosure
bugs.
Path Traversal Detection
You are performing a focused security assessment to find path traversal vulnerabilities in a codebase. This skill uses a three-phase approach with subagents: **recon** (find file-loading sinks with dynamic paths), **batched verify** (trace user input and check mitigations in parallel batches of 3), and **merge** (consolidate batch results into one report).
**Prerequisites**: `sast/architecture.md` must exist. Run the analysis skill first if it doesn't.
---
What is Path Traversal
Path traversal (also called directory traversal) occurs when user-supplied input is incorporated into a file path that is then used to read, write, or serve files from the filesystem — without properly constraining the resulting path to an intended base directory. An attacker can supply sequences like `../` or encoded variants (`%2e%2e%2f`, `..%2f`, `%2e%2e/`) to escape the intended directory and access arbitrary files such as `/etc/passwd`, application source code, credentials, or private keys.
The core pattern: *unvalidated user input reaches a filesystem operation and the resolved path is not verified to remain within the intended base directory.*
What Path Traversal IS
- Serving a user-requested filename directly from a base directory without canonicalizing and checking the resulting path:
`open(os.path.join(BASE_DIR, user_filename))`
- Constructing a file path from a URL parameter and passing it to a file-read function:
`fs.readFile(path.join(__dirname, req.query.file), ...)`
- Template rendering or include directives driven by user input:
`include($_GET['page'] . '.php')`
- Archive extraction (`ZipFile`, `tarfile`, `zipslip`) where entry names are used as output paths without stripping `../` components
- Using `send_file()` / `send_from_directory()` / `res.sendFile()` with an unsanitized user-controlled path
- Reading a file whose path is derived from a user-controlled database value that was stored without sanitization
What Path Traversal is NOT
Do not flag these as path traversal:
- **SSRF**: Fetching a remote URL from user input — that is Server-Side Request Forgery, a separate class
- **RCE via file write**: Writing attacker-controlled content to an arbitrary path — related but a different impact class (flag as RCE or File Upload)
- **Static file serving**: Serving files from a path that is entirely hardcoded with no user influence
- **Safe path joins followed by realpath + prefix check**: The code computes `realpath()` and verifies it starts with the intended base directory
- **basename() before join**: Using only the filename component strips traversal sequences (though note this prevents directory selection, not just traversal)
Patterns That Prevent Path Traversal
When you see these mitigations applied **before** the file operation, the code is likely **not vulnerable**:
**1. `realpath` / `resolve` followed by a base-directory prefix check (most robust fix)**
# Python
import os
BASE = '/var/www/files'
safe_path = os.path.realpath(os.path.join(BASE, user_input))
if not safe_path.startswith(BASE + os.sep):
raise PermissionError("Path escape detected")
with open(safe_path) as f:
...// Node.js
const BASE = path.resolve('/var/www/files');
const resolved = path.resolve(BASE, req.query.file);
if (!resolved.startsWith(BASE + path.sep)) {
return res.status(403).send('Forbidden');
}
fs.readFile(resolved, ...);// Java
Path base = Paths.get("/var/www/files").toRealPath();
Path resolved = base.resolve(userInput).normalize();
if (!resolved.startsWith(base)) {
throw new SecurityException("Path escape");
}
Files.readAllBytes(resolved);**2. `basename()` / `path.basename()` to strip directory components**
# Python — strips all directory parts, only the filename remains
filename = os.path.basename(user_input)
with open(os.path.join(BASE, filename)) as f:
...// PHP
$filename = basename($_GET['file']);
readfile('/var/www/uploads/' . $filename);**3. Allowlist of permitted filenames or extensions**
ALLOWED = {'report.pdf', 'manual.txt', 'logo.png'}
if user_input not in ALLOWED:
abort(400)
with open(os.path.join(BASE, user_input)) as f:
...**4. Framework-provided safe file serving**
# Flask — send_from_directory validates the path stays within the directory
return send_from_directory('/var/www/files', filename)
# Django — FileResponse with a path that was never user-controlled---
Vulnerable vs. Secure Examples
Python — Flask
# VULNERABLE: user-controlled filename joined without realpath check
@app.route('/download')
def download():
filename = request.args.get('file')
filepath = os.path.join('/var/www/files', filename)
return send_file(filepath)
# SECURE: resolve and verify the path stays within the base directory
@app.route('/download')
def download():
filename = request.args.get('file')
base = os.path.realpath('/var/www/files')
filepath = os.path.realpath(os.path.join(base, filename))
if not filepath.startswith(base + os.sep):
abort(403)
return send_file(filepath)Python — FastAPI
# VULNERABLE: path parameter used directly in file read
@app.get('/file/{name}')
async def get_file(name: str):
return FileResponse(f'/app/static/{name}')
# SECURE: basename strips traversal sequences
@app.get('/file/{name}')
async def get_file(name: str):Read more
name: sast-pathtraversal description: >- Detect path traversal vulnerabilities in a codebase using a three-phase approach: recon (find file-loading sinks with dynamic paths), batched verify (trace user input and mitigations in parallel subagents, 3 sinks each), and merge (consolidate batch results). Requires sast/architecture.md (run sast-analysis first). Outputs findings to sast/pathtraversal-results.md. Use when asked to find path traversal, directory traversal, or file disclosure bugs.
Path Traversal Detection
You are performing a focused security assessment to find path traversal vulnerabilities in a codebase. This skill uses a three-phase approach with subagents: **recon** (find file-loading sinks with dynamic paths), **batched verify** (trace user input and check mitigations in parallel batches of 3), and **merge** (consolidate batch results into one report).
**Prerequisites**: `sast/architecture.md` must exist. Run the analysis skill first if it doesn't.
---
What is Path Traversal
Path traversal (also called directory traversal) occurs when user-supplied input is incorporated into a file path that is then used to read, write, or serve files from the filesystem — without properly constraining the resulting path to an intended base directory. An attacker can supply sequences like `../` or encoded variants (`%2e%2e%2f`, `..%2f`, `%2e%2e/`) to escape the intended directory and access arbitrary files such as `/etc/passwd`, application source code, credentials, or private keys.
The core pattern: *unvalidated user input reaches a filesystem operation and the resolved path is not verified to remain within the intended base directory.*
What Path Traversal IS
- Serving a user-requested filename directly from a base directory without canonicalizing and checking the resulting path:
`open(os.path.join(BASE_DIR, user_filename))`
- Constructing a file path from a URL parameter and passing it to a file-read function:
`fs.readFile(path.join(__dirname, req.query.file), ...)`
- Template rendering or include directives driven by user input:
`include($_GET['page'] . '.php')`
- Archive extraction (`ZipFile`, `tarfile`, `zipslip`) where entry names are used as output paths without stripping `../` components
- Using `send_file()` / `send_from_directory()` / `res.sendFile()` with an unsanitized user-controlled path
- Reading a file whose path is derived from a user-controlled database value that was stored without sanitization
What Path Traversal is NOT
Do not flag these as path traversal:
- **SSRF**: Fetching a remote URL from user input — that is Server-Side Request Forgery, a separate class
- **RCE via file write**: Writing attacker-controlled content to an arbitrary path — related but a different impact class (flag as RCE or File Upload)
- **Static file serving**: Serving files from a path that is entirely hardcoded with no user influence
- **Safe path joins followed by realpath + prefix check**: The code computes `realpath()` and verifies it starts with the intended base directory
- **basename() before join**: Using only the filename component strips traversal sequences (though note this prevents directory selection, not just traversal)
Patterns That Prevent Path Traversal
When you see these mitigations applied **before** the file operation, the code is likely **not vulnerable**:
**1. `realpath` / `resolve` followed by a base-directory prefix check (most robust fix)**
# Python
import os
BASE = '/var/www/files'
safe_path = os.path.realpath(os.path.join(BASE, user_input))
if not safe_path.startswith(BASE + os.sep):
raise PermissionError("Path escape detected")
with open(safe_path) as f:
...// Node.js
const BASE = path.resolve('/var/www/files');
const resolved = path.resolve(BASE, req.query.file);
if (!resolved.startsWith(BASE + path.sep)) {
return res.status(403).send('Forbidden');
}
fs.readFile(resolved, ...);// Java
Path base = Paths.get("/var/www/files").toRealPath();
Path resolved = base.resolve(userInput).normalize();
if (!resolved.startsWith(base)) {
throw new SecurityException("Path escape");
}
Files.readAllBytes(resolved);**2. `basename()` / `path.basename()` to strip directory components**
# Python — strips all directory parts, only the filename remains
filename = os.path.basename(user_input)
with open(os.path.join(BASE, filename)) as f:
...// PHP
$filename = basename($_GET['file']);
readfile('/var/www/uploads/' . $filename);**3. Allowlist of permitted filenames or extensions**
ALLOWED = {'report.pdf', 'manual.txt', 'logo.png'}
if user_input not in ALLOWED:
abort(400)
with open(os.path.join(BASE, user_input)) as f:
...**4. Framework-provided safe file serving**
# Flask — send_from_directory validates the path stays within the directory
return send_from_directory('/var/www/files', filename)
# Django — FileResponse with a path that was never user-controlled---
Vulnerable vs. Secure Examples
Python — Flask
# VULNERABLE: user-controlled filename joined without realpath check
@app.route('/download')
def download():
filename = request.args.get('file')
filepath = os.path.join('/var/www/files', filename)
return send_file(filepath)
# SECURE: resolve and verify the path stays within the base directory
@app.route('/download')
def download():
filename = request.args.get('file')
base = os.path.realpath('/var/www/files')
filepath = os.path.realpath(os.path.join(base, filename))
if not filepath.startswith(base + os.sep):
abort(403)
return send_file(filepath)Python — FastAPI
# VULNERABLE: path parameter used directly in file read
@app.get('/file/{name}')
async def get_file(name: str):
return FileResponse(f'/app/static/{name}')
# SECURE: basename strips traversal sequences
@app.get('/file/{name}')
async def get_file(name: str):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.
Repo: utkusen/sast-skills
Other skills on sast-skills.
- /sast-analysis
Perform codebase analysis and architecture mapping as the first phase of a security assessment. Explores the tech stack, frameworks, entry points, data flows, and trust boundaries. Outputs sast/architecture.md. Run this before any vulnerability detection skill. Use when asked to
Open skill - /sast-businesslogic
Detect business logic vulnerabilities in a codebase using a three-phase approach: threat modeling (domain analysis and attack scenarios), batched verify (check exploitable gaps in parallel subagents, 3 scenarios each), and merge (consolidate batch results). Covers price
Open skill - /sast-fileupload
Detect insecure file upload vulnerabilities in a codebase using a three-phase approach: discovery (find all upload sites), batched verify (check extension bypass and related issues in parallel subagents, 3 sites each), and merge (consolidate batch results). Requires
Open skill - /sast-graphql
Detect GraphQL injection vulnerabilities in a codebase using a three-phase approach: recon (confirm GraphQL usage and find unsafe operation document assembly sites), batched verify (trace user input to those sites in parallel subagents, up to 3 candidate sites each), and merge
Open skill - /sast-hardcodedsecrets
Detect hardcoded sensitive data (API keys, access tokens, private keys, passwords, etc.) in publicly accessible code — frontend JavaScript, mobile apps, client-side bundles, and HTML templates. Uses a three-phase approach: recon (find secret candidates), batched verify (confirm
Open skill - /sast-idor
Detect Insecure Direct Object Reference (IDOR) vulnerabilities in a codebase using a three-phase approach: recon (find candidates), batched verify (check authorization in parallel subagents, 3 candidates each), and merge (consolidate batch results). Checks endpoints for missing
Open skill

