fix(worker): contain workspace paths against symlinks
This commit is contained in:
@@ -0,0 +1,39 @@
|
||||
import { lstat, realpath } from 'node:fs/promises';
|
||||
import path from 'node:path';
|
||||
|
||||
const realpathOrDeepestExisting = async (target: string): Promise<string> => {
|
||||
let current = target;
|
||||
const tail: string[] = [];
|
||||
for (;;) {
|
||||
try {
|
||||
const real = await realpath(current);
|
||||
return path.join(real, ...tail.reverse());
|
||||
} catch (error) {
|
||||
if ((error as { code?: string }).code !== 'ENOENT') throw error;
|
||||
const parent = path.dirname(current);
|
||||
if (parent === current) throw error;
|
||||
tail.push(path.basename(current));
|
||||
current = parent;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const assertContainedRealPath = async (
|
||||
root: string,
|
||||
requested: string,
|
||||
opts: { forWrite?: boolean } = {},
|
||||
): Promise<string> => {
|
||||
const lexical = path.resolve(root, requested);
|
||||
const realRoot = await realpath(root);
|
||||
const resolved = await realpathOrDeepestExisting(lexical);
|
||||
if (resolved !== realRoot && !resolved.startsWith(`${realRoot}${path.sep}`)) {
|
||||
throw new Error(`Refusing to access path outside root: ${requested}`);
|
||||
}
|
||||
if (opts.forWrite) {
|
||||
const info = await lstat(lexical).catch(() => null);
|
||||
if (info?.isSymbolicLink()) {
|
||||
throw new Error(`Refusing to write through a symlink: ${requested}`);
|
||||
}
|
||||
}
|
||||
return resolved;
|
||||
};
|
||||
Reference in New Issue
Block a user