# key-sanitizer _(no submodules)_ ## Purpose Validates and normalises caller-supplied storage keys before any fetch is issued against Rackspace Cloud Files, and provides the per-segment URL encoder used to build absolute object URLs. Keys must be POSIX-form relative paths — no `..`, no leading `/`, no Windows drive prefixes, no control characters, no values longer than `MAX_KEY_LENGTH` (2048 chars). Rejections throw `StorageCdnRackspaceError` with code `STORAGE_CDN_RACKSPACE.INVALID_OPTIONS`. Unlike `@suaveplan/storage-http`'s key sanitiser, this one collapses key-length and key-content errors into a single code because operators monitoring Rackspace traffic don't distinguish between "key too long" and "key has a control char" — both are caller bugs caught at the boundary. ## Features - Strict validation: empty, non-string, control char, leading `/`, drive-letter prefix, `..` traversal, overlong (>2048). - Normalisation: Windows `\` → POSIX `/` (idempotent on already-POSIX input). - Per-segment URL encoder (`encodeKey`) applies `encodeURIComponent` to each `/`-separated segment, preserving slashes. - All rejections share one canonical error code for uniform operator alerting. ## Usage: Basic ```ts import { encodeKey, sanitizeKey } from "@suaveplan/storage-cdn-rackspace"; const safe = sanitizeKey("a/b/file name.txt"); const encoded = encodeKey(safe); // → "a/b/file%20name.txt" ``` ## Usage: Advanced ```ts import { encodeKey, sanitizeKey, StorageCdnRackspaceError } from "@suaveplan/storage-cdn-rackspace"; function compose(storageEndpoint: string, container: string, key: string): string { const safe = sanitizeKey(key); return `${storageEndpoint}${encodeURIComponent(container)}/${encodeKey(safe)}`; } try { compose(endpoint, "myc", "../etc/passwd"); } catch (err) { if (err instanceof StorageCdnRackspaceError) operatorLog.warn({ err }, "Caller bug — bad key"); } ``` ## API Reference ### `sanitizeKey(key: string): string` Throws `StorageCdnRackspaceError(STORAGE_CDN_RACKSPACE.INVALID_OPTIONS)` for any rejection reason. Returns the input with Windows `\` replaced by `/`. ### `encodeKey(key: string): string` Splits on `/`, applies `encodeURIComponent` per segment, re-joins. Preserves path hierarchy while encoding spaces, `?`, `#`, `&`, and non-ASCII characters. ## Implementation Notes - Combining key-length and key-content rejections into one code is a deliberate UX choice — Rackspace operators report on a single rule, and the `details` field on the error preserves the message text for diagnostic logs. - The traversal check runs after `\` → `/` normalisation so `a\..\b` is rejected (post-normalisation it has a `..` segment). - Control-character detection is a single O(n) pass scanning every code unit; it costs negligible time relative to the subsequent `fetch`. - `encodeKey` is separate from `sanitizeKey` because URL encoding is a transport concern — callers that compose paths for non-fetch destinations (TempURL signature paths, log messages) want the sanitised but unencoded form. - The `MAX_KEY_LENGTH = 2048` cap matches `@suaveplan/storage-http`'s default — Rackspace's documented limit is 1024, so this is conservatively wider; the sibling `storage-cdn-rackspace.ops` layer prepends the container name which keeps the total path under documented limits.