Path Traversal
Improper Limitation of a Pathname to a Restricted Directory (Path Traversal)
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
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)Explanation
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
Compliance framework mapping
| Framework | Controls |
|---|---|
| OWASP Top 10 (2021) | A01:2021 Broken Access Control |
| NIST 800-53 Rev 5 | AC-3 Access EnforcementSI-10 Information Input Validation |
| PCI-DSS v4.0 | 6.2.4 Software engineering techniques |
| CMMC 2.0 L2 | AC.L2-3.1.3 CUI flow control |
Related CWEs
Deva detects CWE-22 alongside 970+ other CWE patterns at write time, with AI-assisted fix generation that maintains compliance.