Sari la conținut
← Toate exercițiile

Sanitizer-ul care scoate ../ o singură dată

TypeScript · Handler-ul de upload al unui serviciu de gestiune a documentelor · Securitate · dificultate 2/3 · ~8 min

Utilizatorii unui serviciu de gestiune a documentelor urcă fișiere în propriul folder de pe disc. Pot alege o cale relativă în interiorul acelui folder (de exemplu 2024/q1/report.pdf), deci subfolderele sunt permise. Handler-ul de upload scrie apoi în <uploads root>/<user id>/<sanitized path>.

Un coleg i-a cerut asistentului AI: „Scrie sanitizeUploadPath(input), care curăță calea primită de la utilizator ca să nu poată ieși niciodată din folderul lui. Păstrează literele, cifrele, punctele, cratimele, underscore-urile și slash-urile; transformă whitespace-ul în underscore; elimină orice altceva.”

Codul a venit cu teste care trec. Contractul promis de docstring:

  • Rezultatul e o cale relativă, fără slash la început.
  • Rezultatul nu conține niciodată un segment care urcă în arbore (..), oricum ar fi arătat inputul.
  • Rămân doar a-z, A-Z, 0-9, ., _, - și /; whitespace-ul devine _.
  • Un input din care nu rămâne nimic utilizabil devine file.

Înainte să ajungă pe branch-ul care scrie pe disc, fă-i review cu răbdarea unui atacator.

Repară codul până trec testele ascunse.

code.ts

Teste vizibile (trec pe codul primit)
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");
  });
});