El sanitizador que quita ../ una sola vez
TypeScript · El handler de subida de archivos de un servicio de gestión documental · Seguridad · dificultad 2/3 · ~8 min
Los usuarios de un servicio de gestión documental suben archivos a su propia carpeta en disco. Pueden elegir una ruta relativa dentro de esa carpeta (por ejemplo 2024/q1/report.pdf), así que se permiten subcarpetas. Después, el handler de subida escribe en <uploads root>/<user id>/<sanitized path>.
Un compañero de equipo le pidió al asistente de IA: "Escribe sanitizeUploadPath(input), que limpie la ruta que envía el usuario para que nunca pueda salirse de su carpeta. Conserva letras, dígitos, puntos, guiones, guiones bajos y barras normales; convierte los espacios en blanco en guiones bajos; elimina todo lo demás."
El código llegó con tests que pasan. El contrato que promete el docstring:
- El resultado es una ruta relativa sin barra inicial.
- El resultado nunca contiene un segmento que suba en el árbol (
..), sea cual sea la entrada. - Solo sobreviven
a-z,A-Z,0-9,.,_,-y/; los espacios en blanco se convierten en_. - Una entrada que no deja nada utilizable se convierte en
file.
Antes de que llegue a la rama que escribe en disco, revísalo con la paciencia de un atacante.
code.ts
Pruebas visibles (pasan con el código tal como está)
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");
});
});