Files
skills/docs/persistence/storage/storage-browser-indexeddb/src/key-sanitizer/key-sanitizer.md
2026-05-29 08:20:56 +01:00

3.6 KiB

sanitizeKey

(no submodules)

Purpose

The sanitizeKey boundary validates every caller-supplied IndexedDB primary key before it crosses the public API surface of IndexedDbStorage. Strict validation up front prevents two classes of failure: silent corruption from malformed keys (control characters that confuse later debugging tooling, empty strings that masquerade as falsy values), and security issues where attacker-controlled keys could mimic path-traversal patterns even though IndexedDB itself does not interpret slashes. The sanitiser keeps the package's key contract aligned with the sibling @suaveplan/storage-browser-opfs sanitizeKey, so a caller can swap backends behind AbstractStorage without seeing a different validation contract.

Features

  • Rejects non-string input, empty strings, and keys longer than 1024 characters (KEY_TOO_LONG).
  • Rejects keys with C0 control characters (0x00-0x1F) or DEL (0x7F).
  • Rejects POSIX-absolute keys (/foo) and .. traversal segments — defensive guards for callers that later use keys as path-like identifiers.
  • Returns the validated key unchanged on success — no normalisation surprises.
  • All rejections throw IndexedDbError with INDEXEDDB.INVALID_KEY or INDEXEDDB.KEY_TOO_LONG and attach the offending key (truncated for KEY_TOO_LONG) to the error's key discriminant.

Usage: Basic Example

import { sanitizeKey } from "@suaveplan/storage-browser-indexeddb/key-sanitizer";

const safe = sanitizeKey("user/42/profile");
console.log(safe); // "user/42/profile"

Usage: Advanced Example

import {
 INDEXEDDB_CODES,
 IndexedDbError,
} from "@suaveplan/storage-browser-indexeddb/error-codes";
import { sanitizeKey } from "@suaveplan/storage-browser-indexeddb/key-sanitizer";

function validateBatch(keys: string[]): { ok: string[]; bad: { key: string; reason: string }[] } {
 const ok: string[] = [];
 const bad: { key: string; reason: string }[] = [];
 for (const key of keys) {
  try {
   ok.push(sanitizeKey(key));
  } catch (err) {
   if (err instanceof IndexedDbError && err.code === INDEXEDDB_CODES.INVALID_KEY) {
    bad.push({ key, reason: err.message });
   } else if (err instanceof IndexedDbError && err.code === INDEXEDDB_CODES.KEY_TOO_LONG) {
    bad.push({ key, reason: "too long" });
   } else {
    throw err;
   }
  }
 }
 return { ok, bad };
}

API Reference

  • sanitizeKey(key: string): string — Returns key unchanged when valid. Throws IndexedDbError(INDEXEDDB.INVALID_KEY) for non-strings, empty strings, control characters, absolute paths, and .. segments. Throws IndexedDbError(INDEXEDDB.KEY_TOO_LONG) when the key exceeds 1024 characters; the error's key discriminant carries the first 64 characters followed by an ellipsis so logs don't leak large payloads.

Implementation Notes

The maximum length of 1024 characters is shared with @suaveplan/storage-browser-opfs/key-sanitizer and @suaveplan/storage's own AbstractStorage.validateKey (where the limit is also 1024). Aligning the boundary across backends keeps the public contract predictable. The sanitiser does NOT normalise Windows \ separators to POSIX / (the OPFS sanitiser does) because IndexedDB primary keys are opaque strings — there is no filesystem semantic to enforce — and quiet rewriting would surprise callers that compare keys later. The runtime-level typeof guard is defensive against JavaScript callers that pass non-strings (the TypeScript signature already excludes them at compile time); we preserve the check rather than rely on type erasure alone.