Path Traversal
Improper Limitation of a Pathname to a Restricted Directory (Path Traversal)
CWE-22 · High severity
What it is
CWE-22 occurs when an application accepts user input as a file path and resolves it without restricting access to an intended directory. Attackers use sequences like ../ or absolute paths to escape the intended directory and read or write arbitrary files (typically /etc/passwd, application source code, or AWS credentials).
Why it matters
Path traversal exposes anything the application's service account can read: configuration files with secrets, SSH keys, AWS credentials, source code, customer data files. In write contexts, it allows attackers to overwrite application code or system binaries. The pattern recurs in image servers, document viewers, backup utilities, and file-upload-then-download flows.
Common patterns
- Concatenating user input into a path: fs.readFile('/uploads/' + req.params.filename).
- Using req.params.path or req.query.file directly in os.path.join.
- Static file servers with insufficient root restriction.
- Archive extraction that does not validate entry paths (the 'Zip Slip' variant).
- URL-based file routing in image proxies and document viewers.
Languages affected
Python, JavaScript, TypeScript, Ruby, Go, Java, PHP
What Deva detects
Deva tracks input from request handlers, message bodies, and archive entries to file-system operations (fs.readFile, fs.writeFile, fs.createReadStream, open, Path.read_text, FileInputStream, ServletContext.getResource). The scanner reports a finding when an input flows to a path operation without passing through a recognized canonicalization or allowlist check.
Example
Vulnerable
from flask import send_from_directory, request
@app.route('/download')
def download():
filename = request.args.get('file')
return send_from_directory('/var/uploads', filename)Fixed
import os
from flask import send_from_directory, request, abort
UPLOAD_ROOT = '/var/uploads'
@app.route('/download')
def download():
filename = request.args.get('file', '')
# Normalize and confirm the resolved path stays within UPLOAD_ROOT
resolved = os.path.realpath(os.path.join(UPLOAD_ROOT, filename))
if not resolved.startswith(UPLOAD_ROOT + os.sep):
abort(404)
return send_from_directory(UPLOAD_ROOT, filename)send_from_directory has some protections but is bypassable with crafted paths (../, absolute paths, URL-encoded variants). The fix realpath()'s the candidate, then verifies the resolved path is still inside the upload root. This canonical-then-check pattern is the standard defense for path traversal across languages.
Where this fits in OWASP Top 10
- A01 Broken Access ControlAuthorization that fails to enforce who can do what.
Compliance framework mapping
| Framework | Controls |
|---|---|
| OWASP Top 10:2025 | A01:2025 Broken Access Control |
| NIST 800-53 Rev 5 | AC-3 Access Enforcement, SI-10 Information Input Validation |
| PCI-DSS v4.0.1 | 6.2.4 Software engineering techniques |
| CMMC L2 | AC.L2-3.1.3 CUI flow control |
Related CWEs
Deva detects CWE-22 alongside its other CWE rules at write time, with AI-assisted fix generation that maintains compliance.