We take the security of File-Organizer-MCP seriously. If you discover a security vulnerability, please report it responsibly by following these guidelines:
- Do not disclose the vulnerability publicly or create a public GitHub issue
- Do not attempt to exploit the vulnerability beyond what is necessary to confirm it exists
- Do not access, modify, or delete data that does not belong to you
- Email your findings to the security team with a detailed description
- Include steps to reproduce the vulnerability
- Provide any relevant code samples or payloads
- Describe the potential impact and suggested fix if possible
- Initial acknowledgment: 24-48 hours
- Vulnerability assessment: 3-5 business days
- Resolution and patch deployment: Varies by severity
File-Organizer-MCP implements a comprehensive 8-Layer Path Validation pipeline to ensure secure file operations. This is a non-negotiable security requirement for all path-handling operations.
Reference: AGENTS.md - 8-Layer Path Validation
| Layer | Purpose | Implementation |
|---|---|---|
| 1. Zod Schema | Type and format validation | Validate path structure against defined Zod schemas |
| 2. Env Expansion | Resolve environment variables | Expand variables like $HOME, %APPDATA% |
| 3. Sanitization | Remove dangerous sequences | Block ../, null bytes, and invalid characters |
| 4. Absolute Resolution | Convert to absolute paths | Use path.resolve() with base directory enforcement |
| 5. Security Check | Whitelist/blacklist validation | Compare resolved paths against access control lists |
| 6. Symlink Safety | Follow or block symlinks | Use O_NOFOLLOW flag where applicable |
| 7. Containment | Verify path stays within allowed root | Ensure path prefix matches permitted directories |
| 8. Permissions | OS-level access verification | Final read/write/execute permission check |
All path validation is centralized in src/services/PathValidatorService.ts and must be used for every file system operation.
The following security issues were identified and resolved through comprehensive security auditing:
- Fixed improper handling of
../sequences in path construction - Resolved null byte injection possibilities in file names
- Fixed Windows-specific path traversal with backslash manipulation
- Addressed double-encoding vulnerabilities in URL-encoded paths
- Fixed Unicode normalization bypass techniques
- Resolved case-sensitivity bypass on case-insensitive filesystems
- Fixed path truncation exploitation
- Addressed symlink path traversal via relative symlinks
- Fixed NTFS alternate data stream exploitation paths
- Resolved device path access (COM1, LPT1, etc.)
- Fixed environment variable injection in paths
- Addressed home directory expansion bypasses
- Fixed temporary file creation vulnerabilities
- Resolved race condition in path validation
- Fixed canonicalization inconsistencies
- Fixed file existence check followed by file operation race
- Resolved directory creation race conditions
- Fixed symlink target race between check and use
- Addressed permission check race conditions
- Fixed file attribute read race with modification
- Resolved hash calculation race conditions
- Fixed metadata read race with file deletion
- Addressed file move operations across filesystems
- Fixed backup creation race conditions
- Resolved configuration load race conditions
- Fixed watch directory event handling races
- Addressed rollback manifest update races
- Fixed arbitrary symlink creation for privilege escalation
- Resolved symlink overwriting attacks
- Fixed symlink pointing outside intended directory
- Addressed symlink loops and denial of service
- Fixed symlink permission manipulation
- Resolved symlink ownership attacks
- Fixed hard link creation vulnerabilities
- Addressed junction point exploitation on Windows
- Fixed reparse point manipulation
- Resolved symlink with reserved names (CON, AUX, etc.)
- Fixed Windows reserved name protection (CON, PRN, AUX, NUL, COM1-9, LPT1-9)
- Resolved Windows path case sensitivity issues
- Fixed long path prefix (
\\?\) handling - Addressed Windows short name (8.3) exploitation
- Fixed Windows junction point vulnerabilities
- Resolved NTFS permission inheritance issues
- Fixed Windows-specific character encoding issues
- Addressed Windows temporary file naming conflicts
- Fixed JSON injection in configuration files
- Resolved YAML parsing vulnerabilities
- Fixed template injection in file naming patterns
- Addressed command injection via file extensions
- Fixed regex denial of service (ReDoS) in validators
- Resolved prototype pollution in configuration merging
- Fixed type coercion vulnerabilities
- Addressed unsafe deserialization in file metadata
- Fixed information disclosure in error messages
- Resolved log injection vulnerabilities
- Fixed temporary file cleanup issues
- Addressed permission elevation through configuration
All 57 bugs now have corresponding security tests:
- 5/5 Critical Security Tests Passing
- 100+ Security Assertions in Test Suite
- Cross-Platform Validation (Windows, macOS, Linux)
TOCTOU (Time-of-Check-Time-of-Use) vulnerabilities occur when there's a race condition between checking a condition and using the result.
// ❌ VULNERABLE: Race condition between check and use
const exists = await fileExists(path);
if (exists) {
await renameFile(path, newPath); // File could be deleted/modified between check and use
}// ✅ SECURE: Atomic operations without race conditions
await renameFileAtomic(path, newPath); // Uses file descriptors directly- Use File Descriptors: Work with open file handles instead of paths
- Atomic Operations: Use
rename(),copyFile()withCOPYFILE_EXCL - Directory Locking: Implement file-level locking for critical operations
- Retry Logic: Detect and retry operations that fail due to race conditions
- Immediate Verification: Verify results immediately after operations
- All file operations use
fs/promiseswith file descriptors rename()provides atomic move operationscopyFile()withCOPYFILE_EXCLprevents overwriteslstat()is used before operations to verify file state- Backup manifests track all operations for rollback
Symlinks can be exploited to access files outside the intended directory or cause denial of service.
// ❌ VULNERABLE: Follows symlinks to any location
const files = await readdir(rootDir);
for (const file of files) {
const stats = await stat(path.join(rootDir, file)); // Follows symlinks!
}// ✅ SECURE: Uses lstat() to detect symlinks before following
const files = await readdir(rootDir);
for (const file of files) {
const fullPath = path.join(rootDir, file);
const lstats = await lstat(fullPath); // Does NOT follow symlinks
if (lstats.isSymbolicLink()) {
// Handle symlink safely - either block or resolve within bounds
continue;
}
}- Always Use
lstat()First: Detect symlinks before operations - Explicit
O_NOFOLLOW: Useaccess()withF_OKinstead ofstat() - Block External Symlinks: Only allow symlinks within the permitted directory
- Prevent Symlink Creation: Do not create symlinks during organization
- Log Symlink Detection: Report symlinks found during scanning
// In config.ts
interface SecurityConfig {
symlinkHandling: "block" | "follow-safe" | "ignore";
maxSymlinkDepth: number;
allowSymlinksWithinRoot: boolean;
}Windows has several reserved device names that cannot be used as filenames and can cause security issues.
CON- ConsolePRN- PrinterAUX- Auxiliary deviceNUL- Null deviceCOM1throughCOM9- Serial portsLPT1throughLPT9- Parallel portsCOM0,LPT0(legacy)
Any of these names with any extension (e.g., CON.txt, NUL.exe) are problematic.
// ❌ VULNERABLE: No reserved name checking
const filePath = path.join(targetDir, fileName);
await renameFile(originalPath, filePath); // Could create CON device file// ✅ SECURE: Validates against reserved names
const filePath = path.join(targetDir, fileName);
if (isWindowsReservedName(fileName)) {
throw new ValidationError(`Windows reserved name: ${fileName}`);
}
await renameFile(originalPath, filePath);const WINDOWS_RESERVED_NAMES = new Set([
"CON",
"PRN",
"AUX",
"NUL",
"COM1",
"COM2",
"COM3",
"COM4",
"COM5",
"COM6",
"COM7",
"COM8",
"COM9",
"LPT1",
"LPT2",
"LPT3",
"LPT4",
"LPT5",
"LPT6",
"LPT7",
"LPT8",
"LPT9",
]);
export function isWindowsReservedName(name: string): boolean {
const baseName = name.split(".")[0].toUpperCase();
return WINDOWS_RESERVED_NAMES.has(baseName);
}Path traversal attacks use ../ sequences to access files outside the intended directory.
// ❌ VULNERABLE: Allows path traversal
const fileName = req.body.filename; // e.g., "../../../etc/passwd"
const filePath = path.join(userDir, fileName);
await readFile(filePath); // Reads /etc/passwd!// ✅ SECURE: Comprehensive path traversal prevention
const fileName = sanitizeFilename(req.body.filename);
const userDir = await resolveSecurePath(userDir); // Resolves to absolute, canonical
const filePath = path.join(userDir, fileName);
const canonicalPath = await resolveSecurePath(filePath); // Canonicalizes
// Verify containment
if (!canonicalPath.startsWith(userDir)) {
throw new AccessDeniedError("Path traversal detected");
}
await readFile(filePath);- Input Sanitization: Remove
../,..\, and null bytes - Canonicalization: Use
path.resolve()andpath.normalize() - Absolute Path Enforcement: Reject relative paths
- Root Containment: Verify final path starts with allowed root
- Symlink Resolution: Resolve symlinks before containment check
async function resolveSecurePath(
inputPath: string,
rootDir: string,
): Promise<string> {
// Layer 1: Sanitize input
const sanitized = sanitizePath(inputPath);
// Layer 2: Expand environment variables
const expanded = expandEnvVars(sanitized);
// Layer 3: Resolve to absolute path
const absolute = path.isAbsolute(expanded)
? expanded
: path.join(rootDir, expanded);
// Layer 4: Normalize (removes .. and .)
const normalized = path.normalize(absolute);
// Layer 5: Resolve symlinks
const resolved = await resolveSymlinks(normalized);
// Layer 6: Verify containment
if (!resolved.startsWith(rootDir)) {
throw new AccessDeniedError("Path escapes root directory");
}
return resolved;
}- ALWAYS use
PathValidatorServicefor any path operations - NEVER construct paths using string concatenation with user input
- ALWAYS validate and sanitize any path from external sources
- ALWAYS check containment after canonicalization
- ALWAYS use
lstat()instead ofstat()when you need to detect symlinks - ALWAYS use atomic operations (
rename(),copyFile()) - NEVER use
readdir()followed by individualstat()calls without validation - ALWAYS use file descriptors when possible
- NEVER expose full paths in error messages to users
- ALWAYS sanitize paths in error logs
- NEVER include sensitive information in error responses
- ALWAYS use custom error classes (
ValidationError,AccessDeniedError)
- NEVER allow user configuration to specify absolute paths outside whitelist
- ALWAYS validate configuration files against schemas
- NEVER include sensitive data in default configurations
- ALWAYS use secure defaults (block symlinks, strict path validation)
- ALWAYS write security tests for new file operation features
- INCLUDE path traversal test cases for any path-handling code
- TEST edge cases: empty paths, very long paths, special characters
- TEST Windows-specific scenarios on Windows systems
Before submitting any PR, ensure:
- All new paths use
PathValidatorService - No use of
stat()without correspondinglstat()for symlink detection - All user inputs are validated against Zod schemas
- Error messages are sanitized (no full paths exposed)
- Security tests are added for new functionality
- Documentation is updated for security changes
- No hardcoded paths or secrets
- Dependencies are updated and audited (
npm audit)
# Run security-specific tests
npm run test:security
# Run all tests including security assertions
npm test
# Check for known vulnerabilities in dependencies
npm audit
# Check for outdated dependencies
npm outdated- AGENTS.md - Agent orchestration and security guardrails
- ARCHITECTURE.md - System architecture documentation
- TESTS.md - Test documentation including security tests
- src/services/PathValidatorService.ts - Path validation implementation
- src/utils/file-utils.ts - File security utilities
| Version | Date | Security Changes |
|---|---|---|
| 3.4.2 | 2026-02-18 | Integration fixes, dead code cleanup, schema consolidation |
| 3.1.4 | 2026-02-07 | Code linting cleanup, security audit fixes |
| 3.0.0 | 2026-02-02 | Complete TypeScript rewrite with 8-layer path validation |
| 3.0.0-beta.1 | 2026-02-02 | Initial security hardening (57 bugs fixed) |
Last Updated: 2026-02-18 Maintained By: Security Team