66 lines
2.4 KiB
JavaScript
66 lines
2.4 KiB
JavaScript
"use strict";
|
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
};
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.validateFilePath = validateFilePath;
|
|
const path_1 = __importDefault(require("path"));
|
|
const promises_1 = __importDefault(require("fs/promises"));
|
|
/**
|
|
* Validates that a file path is safe and within the allowed base directory
|
|
* Prevents path traversal attacks via:
|
|
* - Null byte injection
|
|
* - Directory traversal sequences (../)
|
|
* - Symlink attacks
|
|
*
|
|
* @param basePath - The allowed base directory (must be absolute)
|
|
* @param relativePath - The relative path to validate
|
|
* @returns The validated absolute path
|
|
* @throws Error if path validation fails
|
|
*/
|
|
async function validateFilePath(basePath, relativePath) {
|
|
// Check for null bytes
|
|
if (relativePath.includes('\0')) {
|
|
throw new Error('Invalid file path: contains null byte');
|
|
}
|
|
// Resolve to absolute path
|
|
const fullPath = path_1.default.resolve(basePath, relativePath);
|
|
// Verify it's still within base directory
|
|
const resolvedBase = path_1.default.resolve(basePath);
|
|
if (!fullPath.startsWith(resolvedBase)) {
|
|
throw new Error('Invalid file path: directory traversal detected');
|
|
}
|
|
// Check for symlinks (resolve real path)
|
|
try {
|
|
const realPath = await promises_1.default.realpath(fullPath);
|
|
if (!realPath.startsWith(resolvedBase)) {
|
|
throw new Error('Invalid file path: symlink traversal detected');
|
|
}
|
|
}
|
|
catch (err) {
|
|
if (err.code === 'ENOENT') {
|
|
// File doesn't exist yet, verify parent directory
|
|
const parent = path_1.default.dirname(fullPath);
|
|
try {
|
|
const realParent = await promises_1.default.realpath(parent);
|
|
if (!realParent.startsWith(resolvedBase)) {
|
|
throw new Error('Invalid file path: parent traversal detected');
|
|
}
|
|
}
|
|
catch (parentErr) {
|
|
if (parentErr.code === 'ENOENT') {
|
|
// Parent also doesn't exist, just verify the path is within base
|
|
// This is already checked above
|
|
}
|
|
else {
|
|
throw parentErr;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
throw err;
|
|
}
|
|
}
|
|
return fullPath;
|
|
}
|
|
//# sourceMappingURL=path-validator.js.map
|