High CWE-22 OWASP A01:2021

Path Traversal in Vibe Coded Apps

How attackers escape your directories to read sensitive files like .env and /etc/passwd

Quick Answer

Path traversal happens when attackers use ../ sequences to escape your intended directory and read files like your .env file. AI tools generate vulnerable file handling code. Always validate that resolved paths stay within allowed directories.

#1
OWASP Ranking
CWE-22
Top 25
CWE Most Dangerous
High
Severity

Source: OWASP Top 10 (2021) - Path traversal is part of Broken Access Control

What is path traversal?

Path traversal (also called directory traversal) is a vulnerability where attackers use ../ sequences to escape your intended directory and access files elsewhere on the server. When your code uses user input to construct file paths without validation, attackers can read sensitive files like /etc/passwd, configuration files, or your .env file containing API keys.

Think of it like an elevator that should only go to floors 1-10, but hackers trick it into going to the basement where secrets are stored by pressing "-1" repeatedly.

According to OWASP Top 10 (2021), path traversal falls under Broken Access Control, ranked #1. CWE-22 is in the CWE Top 25 Most Dangerous Software Weaknesses. The PortSwigger Path Traversal Guide provides detailed exploitation and prevention techniques. For vibe coders building file upload or download features, this is critical because AI tools generate vulnerable patterns by default.

How do AI tools cause path traversal?

AI tools generate path traversal vulnerabilities by creating simple, functional file handling code that concatenates user input directly into file paths. When you ask for a "file download endpoint" or "file upload feature," AI prioritizes working code over secure code.

Dangerous pattern: AI-generated file download

When you ask AI for a file download feature, it often generates this:

// VULNERABLE: AI generates this for "download file" feature
app.get('/download', (req, res) => {
  const filename = req.query.file
  const filepath = `./uploads/${filename}` // DANGEROUS!
  res.sendFile(filepath)
})

// Attacker uses: /download?file=../../../etc/passwd
// Result: Server sends /etc/passwd to attacker

This code works for legitimate users, which is why AI generates it. But it allows attackers to escape the uploads directory using ../ sequences.

The false sense of security: Many vibe coders add path.resolve() thinking it prevents traversal. It doesn't. path.resolve() normalizes the path but doesn't restrict it. You must verify the result stays within your allowed directory.

Common vulnerable features in vibe coded apps include file downloads, image uploads, user avatar handling, document exports, and any endpoint that takes a filename parameter. Tools like Cursor, Bolt, and Claude Code can all generate these patterns.

What attack patterns do hackers use?

Attackers use various encoding and sequence techniques to bypass weak filters. Simply removing ../ from input is not sufficient.

Basic traversal

../../../etc/passwd

Simple sequence that works when there's no filtering at all.

URL encoded

%2e%2e%2f%2e%2e%2f%2e%2e%2fetc%2fpasswd

Bypasses filters that check for literal ../ but don't decode first.

Double encoding

%252e%252e%252f

Bypasses filters that decode once. Gets decoded twice by the server.

Windows backslash

..\..\..\windows\system32\config\sam

Windows accepts both / and \ as separators.

Filter bypass

....//....//....//etc/passwd

If filter removes ../ once, this becomes ../../../etc/passwd.

According to PortSwigger, these bypass techniques are commonly successful because developers implement incomplete fixes.

What could happen if I have path traversal?

Path traversal can lead to sensitive file exposure, configuration theft, source code leaks, and in some cases remote code execution.

  • Secrets exposure: Attackers read your .env file containing API keys, database credentials, and JWT secrets - see hardcoded secrets
  • Configuration theft: Access to /etc/passwd, database configs, cloud credentials
  • Source code leak: Reading your application code exposes business logic and other vulnerabilities
  • Data destruction: If combined with write access, attackers can overwrite or delete files
  • Code execution: Overwriting executable files or configs can lead to server compromise

How do I detect path traversal?

Search for file path construction that uses user input. Any code that builds paths from request parameters is potentially vulnerable.

Patterns to search for
// User input in file paths (DANGEROUS)
const filepath = `./uploads/${req.query.file}`
const filepath = path.join(uploadDir, filename)
const filepath = uploadDir + '/' + userInput

// File operations with user input (DANGEROUS)
res.sendFile(filepath)
fs.readFile(filepath)
fs.writeFile(filepath, data)
fs.createReadStream(filepath)

// path.resolve WITHOUT validation (STILL DANGEROUS)
const filepath = path.resolve('./uploads', filename)
res.sendFile(filepath) // No check if it's still in uploads!

// Regex to find vulnerable patterns:
// (sendFile|readFile|writeFile|createReadStream)\s*\([^)]*\$\{
// path\.(join|resolve)\s*\([^)]*req\.(query|params|body)

Don't want to search manually?

Scan your code free

How do I fix path traversal?

Fix path traversal by validating that the resolved path stays within your allowed directory. Use path.resolve() to get the absolute path, then check it starts with your base directory.

AI Fix Prompt

Copy this prompt into Cursor, Claude Code, or Bolt to automatically fix path traversal in your codebase:

Copy-paste this prompt
Fix all path traversal vulnerabilities in my codebase. ## What to look for Search for these dangerous patterns: 1. File path construction with user input: - `./uploads/${filename}` - path.join(baseDir, userInput) - path.resolve(baseDir, userInput) WITHOUT validation 2. File operations with dynamic paths: - res.sendFile(filepath) - fs.readFile(filepath) - fs.writeFile(filepath, data) - fs.createReadStream(filepath) 3. File upload handling: - multer, express-fileupload, formidable - Anywhere originalname or filename from upload is used ## How to fix Always validate the resolved path is within the allowed directory: ### For file downloads: ```javascript // Before (vulnerable) app.get('/download', (req, res) => { const filename = req.query.file const filepath = path.resolve('./uploads', filename) res.sendFile(filepath) // DANGEROUS - no validation! }) // After (secure) const UPLOADS_DIR = path.resolve('./uploads') app.get('/download', (req, res) => { const filename = req.query.file const filepath = path.resolve(UPLOADS_DIR, filename) // CRITICAL: Verify path is within uploads directory if (!filepath.startsWith(UPLOADS_DIR + path.sep)) { return res.status(403).send('Access denied') } res.sendFile(filepath) }) ``` ### For file uploads: ```javascript // Sanitize uploaded filenames function sanitizeFilename(filename) { // path.basename removes directory components // Replace special chars with underscore return path.basename(filename).replace(/[^a-zA-Z0-9._-]/g, '_') } ``` ### For Next.js API routes: ```typescript const FILES_DIR = path.resolve(process.cwd(), 'public/files') export async function GET(request, { params }) { const filename = params.filename const filepath = path.resolve(FILES_DIR, filename) // Validate path is within allowed directory if (!filepath.startsWith(FILES_DIR + path.sep)) { return NextResponse.json({ error: 'Access denied' }, { status: 403 }) } // ... serve file } ``` ## Framework-specific notes - Express: Check all res.sendFile, res.download calls - Next.js: Check API routes under /app/api/ and /pages/api/ - File uploads: Sanitize filename before saving, validate path before serving - Remember: path.sep is '\' on Windows, '/' on Unix ## After fixing 1. Search for remaining patterns: (sendFile|readFile|writeFile|download) 2. Ensure all file paths are validated against base directory 3. Test with ../ in filenames to verify fix works 4. List all files you modified with before/after snippets Please proceed systematically through my codebase.

Manual Fix

The fix is: resolve the path, then verify it's still inside your allowed directory. Never trust user-supplied filenames.

VULNERABLE
// path.resolve alone is NOT enough!
const path = require('path')

app.get('/download', (req, res) => {
  const filename = req.query.file
  const filepath = path.resolve('./uploads', filename)
  res.sendFile(filepath) // STILL DANGEROUS!
})

// Attacker inputs: ../../../etc/passwd
// path.resolve returns: /etc/passwd
// File is served - attack succeeds!
SECURE
// Validate resolved path stays in allowed directory
const path = require('path')
const UPLOADS_DIR = path.resolve('./uploads')

app.get('/download', (req, res) => {
  const filename = req.query.file
  const filepath = path.resolve(UPLOADS_DIR, filename)

  // CRITICAL: Check path is within uploads
  if (!filepath.startsWith(UPLOADS_DIR + path.sep)) {
    return res.status(403).send('Access denied')
  }

  res.sendFile(filepath)
})

// Attacker inputs: ../../../etc/passwd
// filepath resolves to: /etc/passwd
// Check fails: /etc/passwd doesn't start with /uploads/
// Returns 403 - attack blocked!

Why + path.sep? Without the separator, /uploads-evil/file.txt would pass the check because it starts with /uploads. Adding the separator ensures only paths inside the directory are allowed.

Frequently asked questions

What is path traversal vulnerability?

Path traversal (also called directory traversal or CWE-22) is a vulnerability where attackers use special character sequences like ../ to escape the intended directory and access files elsewhere on the server. This can expose sensitive files like /etc/passwd, configuration files, or your .env file containing secrets.

How do I prevent path traversal in Node.js?

Use path.resolve() to get the absolute path, then verify it starts with your allowed base directory plus the path separator. For example: if (!filepath.startsWith(UPLOADS_DIR + path.sep)) return error. Never trust user-supplied filenames without validation.

What is the difference between path traversal and LFI?

Path traversal is the technique of using ../ sequences to navigate directories. LFI (Local File Inclusion) is an attack that uses path traversal to include local files in application output, often to execute code or read sensitive data. LFI is essentially path traversal exploited in include/require functions.

Can path traversal lead to remote code execution?

Yes. If attackers can traverse to writable directories, they may upload malicious files then access them. They might also overwrite configuration files, access log files for log poisoning attacks, or read source code to find other vulnerabilities. The risk depends on what files are accessible.

How do I test for path traversal vulnerabilities?

Test by sending ../ sequences in any parameter that handles file paths: upload filenames, download parameters, image sources. Try URL encoding (%2e%2e%2f), double encoding (%252e%252e%252f), and backslashes on Windows. Tools like Burp Suite can automate this testing.

Related content

Scan your code for path traversal

Check your codebase for path traversal and other file handling vulnerabilities in AI-generated code.

Try Vibeship Scanner