fix(browser): reject normalized media traversal

This commit is contained in:
Peter Steinberger
2026-05-30 23:40:24 +01:00
parent 45e6b5b97b
commit be08e6c8a8
2 changed files with 24 additions and 1 deletions

View File

@@ -345,6 +345,24 @@ describe("resolveExistingUploadPaths", () => {
});
});
it("rejects traversal-shaped inbound media URI references before URL normalization", async () => {
await withFixtureRoot(async ({ inboundMediaDir, uploadsDir }) => {
const inboundFile = path.join(inboundMediaDir, "report.pdf");
await fs.writeFile(inboundFile, "pdf", "utf8");
const result = await resolveExistingUploadPaths({
uploadDir: uploadsDir,
inboundMediaDir,
requestedPaths: ["media://inbound/nested/../report.pdf"],
});
expect(result.ok).toBe(false);
if (!result.ok) {
expect(result.error).toContain("Invalid media reference");
}
});
});
it("rejects nested absolute inbound media paths", async () => {
await withFixtureRoot(async ({ inboundMediaDir, uploadsDir }) => {
const nestedDir = path.join(inboundMediaDir, "nested");

View File

@@ -94,6 +94,8 @@ function resolveManagedInboundMediaRef(
}
if (/^media:\/\//i.test(normalizedSource)) {
const rawUriMatch = /^media:\/\/[^/?#]*([^?#]*)/iu.exec(normalizedSource);
const rawPath = rawUriMatch?.[1] ?? "";
let parsed: URL;
try {
parsed = new URL(normalizedSource);
@@ -106,7 +108,10 @@ function resolveManagedInboundMediaRef(
error: `Unsupported media reference location: ${parsed.hostname || "(missing)"}`,
};
}
const decoded = decodeInboundMediaId(parsed.pathname.replace(/^\/+/, ""), normalizedSource);
if (!rawPath.startsWith("/") || rawPath.slice(1).includes("/") || rawPath.includes("\\")) {
return { ok: false, error: `Invalid media reference: ${normalizedSource}` };
}
const decoded = decodeInboundMediaId(rawPath.slice(1), normalizedSource);
return decoded?.ok
? {
ok: true,