← All fixes

Fix it

Path traversal from user input, how to fix it

High severityCWE-22 (Path Traversal)
We scanned nearly 2,000 AI-built apps and 1 in 8 shipped a high-severity flaw. Is this one in yours?Scan free →

When a filename or path from a request is joined onto a directory, an attacker can send ../ sequences to escape that directory and read or write files elsewhere on the server. The fix is to resolve the final path and confirm it still sits inside the directory you intended before you touch it.

Why it's a problem

A path like ../../../../etc/passwd or an absolute path can pull files far outside your intended folder, exposing configuration, other users' uploads, or credentials. The same flaw on a write or delete operation lets an attacker overwrite files they should never reach.

The pattern

// user controls the filename
const file = path.join(uploadDir, req.query.name);
res.sendFile(file);

The fix

const requested = path.resolve(uploadDir, req.query.name);
// confirm the resolved path is still inside uploadDir
if (!requested.startsWith(path.resolve(uploadDir) + path.sep)) {
  return res.sendStatus(400);
}
res.sendFile(requested);

Why AI tools write this

Joining a directory and a request parameter is the obvious way to express 'serve the file the user asked for,' and it works perfectly for normal filenames. The traversal only shows up when someone deliberately sends ../ sequences, which a happy-path test never does.

The quick fix

  • Resolve the final path and confirm it starts inside your intended directory.
  • Strip path separators or use path.basename when you only expect a bare filename.
  • Prefer an allowlist of known files or ids over accepting arbitrary paths.

Common questions

Is path.join alone enough to prevent traversal?

No. path.join normalizes slashes but does not prevent ../ sequences from escaping the base directory. You must resolve the final path and confirm it is still inside the intended directory.

How do I handle this for a file download endpoint?

Resolve the full path from the base directory, confirm the result starts with the base directory plus a separator, and only then open the file. Reject anything that resolves outside.

What if I only accept a bare filename with no path?

Use path.basename to strip any directory component before joining. An input like ../../../../etc/passwd becomes passwd, which is then safe to join with your base directory.

Want to know if this pattern is already in something you shipped? Scan your live app or a public repo free, no account needed.

Scan my app →

Catch this automatically: scan your GitHub repo · website vulnerability scanner · review every pull request · SAST for AI code · OWASP Top 10 for AI code

Path Traversal (../) From User Input, How to Fix It | Prbl