The sanitizer that strips ../ exactly once
The file upload handler of a document management service · difficulty 2/3 · ~8 min
Users of a document management service upload files into their own folder on disk. They may choose a relative path inside that folder (for example `2024/q1/report.pdf`), so subfolders are allowed. The upload handler then writes to `<uploads root>/<user id>/<sanitized path>`.
A teammate asked the AI assistant: "Write `sanitizeUploadPath(input)` that cleans the user-supplied path so it can never escape the user's folder. Keep letters, digits, dots, dashes, underscores and forward slashes; turn whitespace into underscores; drop everything else."
The code arrived with tests that pass. The contract promised by the docstring:
- The result is a relative path with no leading slash.
- The result never contains a segment that moves up the tree (`..`), whatever the input looked like.
- Only `a-z`, `A-Z`, `0-9`, `.`, `_`, `-` and `/` survive; whitespace becomes `_`.
- An input that leaves nothing usable becomes `file`.
Before it lands on the branch that writes to disk, review it with an attacker's patience.
Repair the code until the hidden tests pass.
code.ts
Visible tests (they pass on the code as given)
import { describe, it, expect } from "@debunk/harness";
import { sanitizeUploadPath } from "./code";
describe("sanitizeUploadPath", () => {
it("keeps a normal relative path with subfolders", () => {
expect(sanitizeUploadPath("2024/q1/report.pdf")).toBe("2024/q1/report.pdf");
});
it("strips directory traversal and leading slashes", () => {
expect(sanitizeUploadPath("../secret.txt")).toBe("secret.txt");
expect(sanitizeUploadPath("/etc/passwd")).toBe("etc/passwd");
});
it("replaces whitespace and drops disallowed characters", () => {
expect(sanitizeUploadPath("report final <v2>.pdf")).toBe("report_final_v2.pdf");
});
});