Files
skills/docs/api-stack/api/api-plugin-file-upload/src/storage/file-system.md
2026-05-29 08:20:56 +01:00

2.5 KiB

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

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

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.