detect-non-literal-fs-filename
Detects variable in filename argument of fs calls, which might allow an attacker to access anything on your system. This rule is part of [`eslint-plugin-secure-
Keywords: path traversal, CWE-22, security, ESLint rule, file system, fs module, directory traversal, file access, auto-fix, LLM-optimized, code security
Detects variable in filename argument of fs calls, which might allow an attacker to access anything on your system. This rule is part of eslint-plugin-secure-coding and provides LLM-optimized error messages with fix suggestions.
🚨 Security rule | 💡 Provides LLM-optimized guidance | ⚠️ Set to error in recommended
Quick Summary
| Aspect | Details |
|---|---|
| CWE Reference | CWE-22 (Path Traversal) |
| Severity | High (security vulnerability) |
| Auto-Fix | ⚠️ Suggests fixes (manual application) |
| Category | Security |
| ESLint MCP | ✅ Optimized for ESLint MCP integration |
| Best For | Node.js applications, file processing systems, file upload handlers |
Vulnerability and Risk
Vulnerability: Using non-literal (dynamic) values for filesystem operations (like opening, reading, or writing files) without strict validation allows users to control file paths.
Risk: Attackers can manipulate file paths to access files outside the intended directory (Path Traversal), overwriting critical system files, or disclosing sensitive information (like configuration files or source code).
Rule Details
This rule detects dangerous use of Node.js fs methods with dynamic paths that can lead to path traversal attacks (also known as directory traversal).
Error Message Format
The rule provides LLM-optimized error messages (Compact 2-line format) with actionable security guidance:
🔒 CWE-22 OWASP:A01 CVSS:7.5 | Path Traversal detected | HIGH [SOC2,PCI-DSS,HIPAA,ISO27001]
Fix: Review and apply the recommended fix | https://owasp.org/Top10/A01_2021/Message Components
| Component | Purpose | Example |
|---|---|---|
| Risk Standards | Security benchmarks | CWE-22 OWASP:A01 CVSS:7.5 |
| Issue Description | Specific vulnerability | Path Traversal detected |
| Severity & Compliance | Impact assessment | HIGH [SOC2,PCI-DSS,HIPAA,ISO27001] |
| Fix Instruction | Actionable remediation | Follow the remediation steps below |
| Technical Truth | Official reference | OWASP Top 10 |
Configuration
| Option | Type | Default | Description |
|---|---|---|---|
allowLiterals | boolean | false | Allow literal string paths |
additionalMethods | string[] | [] | Additional fs methods to check |
Examples
❌ Incorrect
// Path traversal - HIGH risk
const { readFile } = require('fs');
readFile(userPath, callback); // Attacker can access ../../../etc/passwd
// Directory traversal - HIGH risk
fs.readdir(userDir, callback); // Can list any directory
// File creation - MEDIUM risk
fs.writeFile(userFile, data, callback); // Can write anywhere✅ Correct
// Safe file reading
const SAFE_UPLOADS_DIR = path.join(__dirname, 'uploads');
const safePath = path.join(SAFE_UPLOADS_DIR, path.basename(userFile));
fs.readFile(safePath, callback);
// Safe directory listing
const ALLOWED_DIRS = [path.join(__dirname, 'public')];
const resolvedDir = path.resolve(ALLOWED_DIRS[0], userSubDir);
if (resolvedDir.startsWith(ALLOWED_DIRS[0])) {
fs.readdir(resolvedDir, callback);
}
// Safe file writing
const safeWritePath = path.join(SAFE_WRITES_DIR, path.basename(userFile));
fs.writeFile(safeWritePath, data, callback);Path Traversal Prevention
Basic Protection
const path = require('path');
// ❌ Vulnerable
fs.readFile(userInput, callback);
// ✅ Protected
const safePath = path.resolve(SAFE_DIR, userInput);
if (!safePath.startsWith(SAFE_DIR)) {
return callback(new Error('Invalid path'));
}
fs.readFile(safePath, callback);Advanced Protection
function securePath(baseDir: string, userPath: string): string {
const resolved = path.resolve(baseDir, userPath);
// Check if resolved path is within base directory
if (!resolved.startsWith(baseDir)) {
throw new Error('Path traversal detected');
}
// Additional security: remove any remaining ..
const normalized = path.normalize(resolved);
if (normalized.includes('..')) {
throw new Error('Invalid path segments');
}
return resolved;
}Common Attack Vectors
| Attack | Example Input | Result |
|---|---|---|
| Basic traversal | ../../../etc/passwd | Access system files |
| Windows traversal | ....\\....\\windows\\system32 | Access Windows files |
| Encoded traversal | %2e%2e%2f%2e%2e%2fetc/passwd | URL-encoded attack |
| Unicode traversal | ..\\u002f..\\u002fetc/passwd | Unicode bypass |
Method-Specific Guidance
File Reading (readFile, readFileSync)
- Use
path.basename()to strip directory components - Combine with safe base directory
- Validate file extensions if needed
File Writing (writeFile, writeFileSync)
- Same as reading, but consider separate write directories
- Check disk space and file size limits
- Validate file types on upload
Directory Operations (readdir, stat)
- Always resolve and validate directory paths
- Check directory existence and permissions
- Consider rate limiting for directory listings
Stream Operations (createReadStream, createWriteStream)
- Apply same path validation as regular file operations
- Be careful with relative paths in streams
- Validate stream destinations
Security Best Practices
Directory Structure
project/
├── uploads/ # User uploads (read-only from here)
├── public/ # Public files
├── temp/ # Temporary files
└── user-data/ # User-specific dataPath Validation
class SecurePath {
constructor(private baseDir: string) {}
resolve(userPath: string): string {
const resolved = path.resolve(this.baseDir, userPath);
// Security checks
if (!resolved.startsWith(this.baseDir)) {
throw new Error('Path traversal attempt');
}
// Remove dangerous segments
const normalized = path.normalize(resolved);
if (normalized.includes('..') || normalized.includes('\0')) {
throw new Error('Invalid path');
}
return resolved;
}
}Migration Guide
Phase 1: Discovery
{
rules: {
'secure-coding/detect-non-literal-fs-filename': 'warn'
}
}Phase 2: Implementation
// Add security utilities
const SECURE_PATHS = {
uploads: path.join(__dirname, 'uploads'),
public: path.join(__dirname, 'public'),
temp: path.join(__dirname, 'temp')
};
// Replace unsafe calls
fs.readFile(userPath) → fs.readFile(securePath.resolve(userPath))Phase 3: Testing
// Test path traversal attempts
const attacks = [
'../../../etc/passwd',
'..\\..\\windows\\system32',
'....//....//etc/passwd',
];
for (const attack of attacks) {
expect(() => securePath.resolve(attack)).toThrow();
}Comparison with Alternatives
| Feature | detect-non-literal-fs-filename | eslint-plugin-security | eslint-plugin-node |
|---|---|---|---|
| Path Traversal Detection | ✅ Yes | ⚠️ Limited | ⚠️ Limited |
| CWE Reference | ✅ CWE-22 included | ⚠️ Limited | ⚠️ Limited |
| LLM-Optimized | ✅ Yes | ❌ No | ❌ No |
| ESLint MCP | ✅ Optimized | ❌ No | ❌ No |
| Fix Suggestions | ✅ Detailed | ⚠️ Basic | ⚠️ Basic |
Related Rules
detect-eval-with-expression- Prevents code injection via eval()detect-child-process- Prevents command injectiondetect-object-injection- Prevents prototype pollutiondetect-non-literal-regexp- Prevents ReDoS attacks
Known False Negatives
The following patterns are not detected due to static analysis limitations:
Path from Variable
Why: Path strings from variables not traced.
// ❌ NOT DETECTED - Path from variable
const filePath = userInput;
fs.readFile(filePath);Mitigation: Validate and sanitize all paths.
Indirect Path Construction
Why: Complex path building not analyzed.
// ❌ NOT DETECTED - Indirect
const path = buildPath(base, userInput);
fs.readFile(path);Mitigation: Use path whitelisting.
Custom FS Wrappers
Why: FS wrappers not recognized.
// ❌ NOT DETECTED - Wrapper
fileManager.read(userPath);Mitigation: Apply rule to wrapper implementations.
Further Reading
- OWASP Path Traversal - Path traversal attack guide
- Node.js File System Security - Node.js fs module security
- CWE-22: Path Traversal - Official CWE entry
- ESLint MCP Setup - Enable AI assistant integration