refactor: extract tmp media resolver helper and dedupe sandbox-path tests
This commit is contained in:
@@ -18,6 +18,11 @@ async function expectSandboxRejection(media: string, sandboxRoot: string, patter
|
||||
await expect(resolveSandboxedMediaSource({ media, sandboxRoot })).rejects.toThrow(pattern);
|
||||
}
|
||||
|
||||
function isPathInside(root: string, target: string): boolean {
|
||||
const relative = path.relative(path.resolve(root), path.resolve(target));
|
||||
return relative === "" || (!relative.startsWith("..") && !path.isAbsolute(relative));
|
||||
}
|
||||
|
||||
describe("resolveSandboxedMediaSource", () => {
|
||||
// Group 1: /tmp paths (the bug fix)
|
||||
it.each([
|
||||
@@ -94,9 +99,15 @@ describe("resolveSandboxedMediaSource", () => {
|
||||
if (process.platform === "win32") {
|
||||
return;
|
||||
}
|
||||
const outsideTmpTarget = path.resolve(process.cwd(), "package.json");
|
||||
if (isPathInside(os.tmpdir(), outsideTmpTarget)) {
|
||||
return;
|
||||
}
|
||||
|
||||
await withSandboxRoot(async (sandboxDir) => {
|
||||
await fs.access(outsideTmpTarget);
|
||||
const symlinkPath = path.join(sandboxDir, "tmp-link-escape");
|
||||
await fs.symlink("/etc/passwd", symlinkPath);
|
||||
await fs.symlink(outsideTmpTarget, symlinkPath);
|
||||
await expectSandboxRejection(symlinkPath, sandboxDir, /symlink|sandbox/i);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -90,12 +90,12 @@ export async function resolveSandboxedMediaSource(params: {
|
||||
throw new Error(`Invalid file:// URL for sandboxed media: ${raw}`);
|
||||
}
|
||||
}
|
||||
const resolved = path.resolve(resolveSandboxInputPath(candidate, params.sandboxRoot));
|
||||
const tmpDir = path.resolve(os.tmpdir());
|
||||
const candidateIsAbsolute = path.isAbsolute(expandPath(candidate));
|
||||
if (candidateIsAbsolute && isPathInside(tmpDir, resolved)) {
|
||||
await assertNoSymlinkEscape(path.relative(tmpDir, resolved), tmpDir);
|
||||
return resolved;
|
||||
const tmpMediaPath = await resolveAllowedTmpMediaPath({
|
||||
candidate,
|
||||
sandboxRoot: params.sandboxRoot,
|
||||
});
|
||||
if (tmpMediaPath) {
|
||||
return tmpMediaPath;
|
||||
}
|
||||
const sandboxResult = await assertSandboxPath({
|
||||
filePath: candidate,
|
||||
@@ -105,6 +105,23 @@ export async function resolveSandboxedMediaSource(params: {
|
||||
return sandboxResult.resolved;
|
||||
}
|
||||
|
||||
async function resolveAllowedTmpMediaPath(params: {
|
||||
candidate: string;
|
||||
sandboxRoot: string;
|
||||
}): Promise<string | undefined> {
|
||||
const candidateIsAbsolute = path.isAbsolute(expandPath(params.candidate));
|
||||
if (!candidateIsAbsolute) {
|
||||
return undefined;
|
||||
}
|
||||
const resolved = path.resolve(resolveSandboxInputPath(params.candidate, params.sandboxRoot));
|
||||
const tmpDir = path.resolve(os.tmpdir());
|
||||
if (!isPathInside(tmpDir, resolved)) {
|
||||
return undefined;
|
||||
}
|
||||
await assertNoSymlinkEscape(path.relative(tmpDir, resolved), tmpDir);
|
||||
return resolved;
|
||||
}
|
||||
|
||||
async function assertNoSymlinkEscape(
|
||||
relative: string,
|
||||
root: string,
|
||||
|
||||
Reference in New Issue
Block a user