first commit
This commit is contained in:
@@ -0,0 +1,62 @@
|
||||
# key-sanitizer
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## 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.
|
||||
Reference in New Issue
Block a user