# `file-system.ts` ## Purpose Production-grade `UploadStorage` adapter backed by `node:fs/promises`. Stores bytes as flat files under a configurable `rootDir`. Every key is validated for path-traversal escape attempts BEFORE touching disk, so a malicious `../etc/passwd` key never reaches the filesystem syscall layer — it raises `FileUploadError(UPLOAD.STORAGE_FAILURE)` from the resolver. ## Features - Path-traversal hardening: rejects keys containing `/`, `\`, or NUL bytes; resolves keys via `path.resolve` and verifies the result is inside `rootDir`. - Lazy `mkdir -p rootDir` on first `put` so callers can construct the adapter before the directory exists. - Atomic `writeFile` on POSIX so partial writes never leave half-files visible to a parallel reader. - `delete` uses `rm({ force: true })` so absent files do not raise — matching the abort route's idempotent semantics. - `exists` swallows `stat` errors and returns `false` rather than leaking the underlying syscall error code. ## Usage: Basic ```ts import { FileSystemUploadStorage } from "@suaveplan/api-plugin-file-upload/storage/file-system"; const storage = new FileSystemUploadStorage({ rootDir: "/var/lib/suaveplan/uploads", }); await storage.put("upload-001", new Uint8Array([0xff])); ``` ## Usage: Advanced ```ts import { FileSystemUploadStorage, FileUploadError } from "@suaveplan/api-plugin-file-upload"; const storage = new FileSystemUploadStorage({ rootDir: "/tmp/uploads" }); try { await storage.put("../etc/passwd", new Uint8Array([0])); } catch (err) { if (err instanceof FileUploadError && err.uploadCode === "UPLOAD.STORAGE_FAILURE") { console.warn("path-traversal rejected:", err.message); } } ``` ## API Reference - `FileSystemUploadStorageOptions` — `{ rootDir: string }`. - `class FileSystemUploadStorage implements UploadStorage` — constructor `(options: FileSystemUploadStorageOptions)`. - `put(key, bytes)` / `get(key)` / `delete(key)` / `exists(key)` — same shape as `UploadStorage`. ## Implementation Notes - The path-traversal check rejects ANY separator in the raw key, then resolves and re-verifies the resolved path is inside `rootDir`. The double check defends against both crafted strings AND quirky platform path normalisation. - `readFile` returns a `Buffer`; the wrapper re-wraps as `Uint8Array` so callers see a tier-neutral byte view. - Storage failures preserve the underlying `cause` so operators can trace EACCES / ENOSPC at the OTel log layer without losing context.