37 lines
950 B
JavaScript
37 lines
950 B
JavaScript
import { realpathSync } from "fs";
|
|
import path from "path";
|
|
export default function isSafePath({ filePath, allowedDir, }) {
|
|
const allowed = path.resolve(allowedDir);
|
|
const resolved = path.resolve(filePath);
|
|
if (!isInside(resolved, allowed)) {
|
|
return false;
|
|
}
|
|
try {
|
|
return isInside(realpathSync(resolved), resolveRealDir(allowed));
|
|
}
|
|
catch {
|
|
return false;
|
|
}
|
|
}
|
|
function isInside(target, dir) {
|
|
return target === dir || target.startsWith(dir + path.sep);
|
|
}
|
|
const REAL_DIRS = new Map();
|
|
/**
|
|
* Resolves symlinks in the allowed directory itself (e.g. a project under a
|
|
* symlinked path), so real file paths inside it still match.
|
|
*/
|
|
function resolveRealDir(dir) {
|
|
const cached = REAL_DIRS.get(dir);
|
|
if (cached)
|
|
return cached;
|
|
try {
|
|
const real = realpathSync(dir);
|
|
REAL_DIRS.set(dir, real);
|
|
return real;
|
|
}
|
|
catch {
|
|
return dir;
|
|
}
|
|
}
|