first commit
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
# `key-sanitizer`
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
Validates and normalises every caller-supplied storage key before any filesystem call. The sanitiser is the security boundary that prevents a caller-controlled key (`../../etc/passwd`) from escaping the configured `rootDir`. Every public method on `NodeFsStorage` passes its key through `sanitizeKey` before resolving any path or issuing any `node:fs` call. Failing the sanitiser is unrecoverable from the caller's perspective — it always reflects a programming error or a malicious input.
|
||||
|
||||
## Features
|
||||
|
||||
- Rejects empty strings, `..` traversal segments, POSIX absolute paths (`/foo`), Windows drive-letter prefixed paths (`C:\foo`, `c:/foo`).
|
||||
- Rejects null bytes (`\x00`) and any control char in the C0 range (`0x00`–`0x1F`) plus DEL (`0x7F`).
|
||||
- Enforces a maximum length of `MAX_KEY_LENGTH` (1024) characters; over-length keys surface as `STORAGE_FILE.KEY_TOO_LONG` (a distinct code from `INVALID_KEY` so callers can hint at the truncation).
|
||||
- Normalises Windows `\` separators to POSIX `/` so the same key resolves identically on every platform.
|
||||
- Pure function — no allocation beyond the normalised string return; safe to call on every operation.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import { sanitizeKey } from "@suaveplan/storage-file";
|
||||
|
||||
const safe = sanitizeKey("orders/2026/05/12.json");
|
||||
// → "orders/2026/05/12.json"
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import { sanitizeKey, StorageFileError, STORAGE_FILE_CODES } from "@suaveplan/storage-file";
|
||||
|
||||
function safeKey(input: string): string | null {
|
||||
try {
|
||||
return sanitizeKey(input);
|
||||
} catch (err) {
|
||||
if (err instanceof StorageFileError && (err.code === STORAGE_FILE_CODES.INVALID_KEY || err.code === STORAGE_FILE_CODES.KEY_TOO_LONG)) {
|
||||
return null;
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
const normalised = sanitizeKey("orders\\2026\\05\\12.json");
|
||||
// → "orders/2026/05/12.json"
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `sanitizeKey(key: string): string` — returns the normalised POSIX-separator form on success.
|
||||
- Throws `StorageFileError` with code `STORAGE_FILE.INVALID_KEY` on empty input, control chars, absolute paths, drive-letter prefix, or `..` traversal.
|
||||
- Throws `StorageFileError` with code `STORAGE_FILE.KEY_TOO_LONG` when input length exceeds `MAX_KEY_LENGTH` (1024).
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The control-char check iterates the string code unit by code unit rather than using a regex with a control-char range. This keeps biome's `noControlCharactersInRegex` lint happy without a suppression comment and is also marginally faster on short keys.
|
||||
- `..` is rejected segment-by-segment after `\\` normalisation, so both `foo\\..\\bar` and `foo/../bar` are caught.
|
||||
- Empty segments produced by consecutive separators (`foo//bar`) are NOT rejected — they are allowed for caller convenience and resolve to the same path. The host filesystem normalises them internally.
|
||||
- The key argument is not constrained beyond the rules above; arbitrary Unicode (including emoji and CJK) is permitted because `node:fs` accepts them and most modern filesystems do too.
|
||||
Reference in New Issue
Block a user