Skip to content

Commit c1912ef

Browse files
Handle Windows path aliases safely
1 parent 911c163 commit c1912ef

2 files changed

Lines changed: 30 additions & 5 deletions

File tree

packages/core/src/media-validation.ts

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,22 @@ export class MediaValidationError extends Error {
2929
}
3030
}
3131

32+
async function assertNoSymbolicLinkSegments(filePath: string): Promise<void> {
33+
const root = path.parse(filePath).root;
34+
const relative = path.relative(root, filePath);
35+
let current = root;
36+
for (const segment of relative.split(path.sep)) {
37+
if (!segment) continue;
38+
current = path.join(current, segment);
39+
const segmentStat = await fs.lstat(current);
40+
if (segmentStat.isSymbolicLink()) {
41+
throw new MediaValidationError(
42+
"Media path must not traverse a symbolic link or reparse point.",
43+
);
44+
}
45+
}
46+
}
47+
3248
export function isMp4Container(
3349
bytes: Uint8Array,
3450
totalSize: number = bytes?.byteLength ?? 0,
@@ -65,11 +81,9 @@ async function assertRegularFile(filePath: string): Promise<{
6581
return process.platform === "win32" ? resolved.toLowerCase() : resolved;
6682
};
6783
if (normalizeForCompare(realPath) !== normalizeForCompare(resolvedInput)) {
68-
// realpath changed the path: an intermediate link or reparse point was
69-
// traversed. The leaf-link case was rejected above.
70-
throw new MediaValidationError(
71-
"Media path must not traverse a symbolic link or reparse point.",
72-
);
84+
// realpath can also expand a harmless Windows 8.3 path alias. Inspect each
85+
// segment so only an actual symlink or junction is rejected.
86+
await assertNoSymbolicLinkSegments(resolvedInput);
7387
}
7488
const stat = await fs.stat(realPath);
7589
if (!stat.isFile()) {

packages/core/test/media-validation.test.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,17 @@ test("validateVideoFile enforces extension, size, ftyp, no symlink", async () =>
6161
await fs.symlink(good, link);
6262
await assert.rejects(() => validateVideoFile(link), /symbolic link/);
6363
}
64+
65+
const linkedRoot = path.join(root, "linked-root");
66+
await fs.symlink(
67+
root,
68+
linkedRoot,
69+
process.platform === "win32" ? "junction" : "dir",
70+
);
71+
await assert.rejects(
72+
() => validateVideoFile(path.join(linkedRoot, "background.mp4")),
73+
/symbolic link|reparse point/,
74+
);
6475
} finally {
6576
await fs.rm(root, { recursive: true, force: true });
6677
}

0 commit comments

Comments
 (0)