Security ⚑ High severity

Path Traversal in File Read

File paths built from user input allow ../ sequences to escape the intended directory.

When file paths are built from user input using string concatenation, attackers use `../` sequences to escape the intended directory. Linux doesn’t prevent `..` in filenames — it’s a path traversal, not a filesystem bug. Even when the app checks for `..`, encoding tricks (`%2e%2e%2f`), null bytes (`%00`), and double-encoded sequences can bypass naive filters.

❌ Vulnerable
// VULNERABLE
app.get('/download', (req, res) => {
  const file = req.query.file;
  const stream = fs.createReadStream('./uploads/' + file);
  // Attacker: ?file=../../../etc/passwd
});
✓ Fixed
// FIXED — use realpath and containment check
const uploadsDir = path.resolve('./uploads');
app.get('/download', (req, res) => {
  const file = path.resolve(path.join(uploadsDir, req.query.file));
  if (!file.startsWith(uploadsDir)) {
    return res.status(403).send('Forbidden');
  }
  res.sendFile(file);
});
CVE-2024-36137 (Node.js permission model path traversal via crafted relative symlink paths, allowing read of files outside allowed directory). Also CVE-2025-55131 (same issue, incomplete fix).
PullLight flags `fs.createReadStream`, `fs.readFileSync`, `res.sendFile`, `res.download` calls where the path is constructed from `req.query`, `req.body`, or `req.params` without `path.resolve()` containment checks. It detects naive `startsWith()` checks and flags patterns missing proper path normalization.
See it in action — paste a diff into /analyze
Try a vulnerable example: pool.query(`SELECT * FROM users WHERE id = ${req.params.id}`)
Analyze a diff →