first commit
This commit is contained in:
@@ -0,0 +1,52 @@
|
||||
# `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.
|
||||
Reference in New Issue
Block a user