Path Traversal via path.join() and path.resolve() in Express

High Risk Path Traversal
javascriptexpresspath-traversalfile-accessdirectory-traversal

What it is

Path traversal vulnerabilities occur when Express applications use path.join() or path.resolve() with user-controlled input without proper validation. Attackers can use '../' sequences to escape the intended directory and access sensitive files. While these functions normalize paths, they don't prevent traversal outside the base directory unless explicitly validated.

const express = require('express');
const path = require('path');
const fs = require('fs');

const app = express();
const BASE_DIR = '/app/files';

// VULNERABLE: path.join normalizes but doesn't prevent traversal
app.get('/download', (req, res) => {
    const filename = req.query.file;
    
    // path.join normalizes '../' but still allows escaping BASE_DIR
    const filePath = path.join(BASE_DIR, filename);
    
    if (fs.existsSync(filePath)) {
        res.sendFile(filePath);
    } else {
        res.status(404).send('Not found');
    }
});

// Attack: /download?file=../../../etc/passwd
const express = require('express');
const path = require('path');
const fs = require('fs');

const app = express();
const BASE_DIR = '/app/files';

// SECURE: validate path stays within base directory
app.get('/download', (req, res) => {
    const filename = req.query.file;
    
    // Normalize and resolve the full path
    const filePath = path.normalize(path.join(BASE_DIR, filename));
    const baseDir = path.normalize(BASE_DIR);
    
    // Validate path stays within base directory
    if (!filePath.startsWith(baseDir + path.sep)) {
        return res.status(400).send('Invalid path');
    }
    
    if (fs.existsSync(filePath) && fs.statSync(filePath).isFile()) {
        res.sendFile(filePath);
    } else {
        res.status(404).send('Not found');
    }
});

💡 Why This Fix Works

The vulnerable code uses path.join() which normalizes the path but doesn't prevent escaping the base directory. Attackers can use '../' sequences to access files outside the intended directory. The secure version validates that the normalized path starts with the base directory, rejecting any paths that escape it.

Why it happens

Using path.join() to combine base directory with unvalidated user input.

Root causes

Using path.join() with User Input

Using path.join() to combine base directory with unvalidated user input.

Missing Path Validation

Not verifying that resolved paths remain within the intended base directory.

Trusting Normalized Paths

Assuming path normalization alone prevents directory traversal.

Fixes

1

Validate Resolved Paths

Check that normalized path starts with base directory using path.relative() or startsWith().

2

Sanitize Filenames

Reject filenames containing path separators or traversal sequences.

3

Use Allowlists

Maintain allowlist of valid filenames rather than constructing paths from user input.

Detect This Vulnerability in Your Code

Sourcery automatically identifies path traversal via path.join() and path.resolve() in express and many other security issues in your codebase.