Skip to content

[P1][lens] Reject colon and empty names in collection/table identifiers #20

Description

@cevheri

Summary

A : character in a collection or table name breaks namespace isolation. doc(db, \tenant:${tenantId}`)` — the natural multi-tenant pattern — produces keys that collide with other namespaces and leak into foreign prefix scans. For relational tables, catalog entries can be shadowed. Silent data corruption, no error.

Audit ID: B7 (High) — pre-announcement blocker

Root cause

Document keys are built as string concatenation:

function keyOf(collection: string, id: string): Uint8Array {
  return utf8.encode(`${collection}:${id}`);
}

prefixRange assumes collection: is a byte boundary. If collection contains :, boundaries lie.

Name validation (assertUserName in catalog.ts) checks reserved markers but not : or empty string.

Proposed fix

Extend assertUserName (or lens-local validator) to reject:

  • Empty name
  • Names containing : (U+003A)
  • Optionally other structural chars already reserved (\0, etc.) — follow existing loud-error style in catalog.ts

Apply at doc() and table() entry points (both call assertUserName today — verify and extend).

Tests

  • doc(db, "tenant:abc") → throws with clear message.
  • doc(db, "") → throws.
  • Valid names still work; relational + document unaffected.

Acceptance criteria

  • : and empty names rejected at lens boundary.
  • Error message suggests safe pattern (e.g. use _ or encode tenant id only in collection name).
  • Tests in src/lens/document.test.ts / catalog.test.ts.
  • bun run gate green.
  • Changeset required.

Related issues

  • Independent; lenses only (kernel unchanged).

Agent constraints

  • Do not change on-disk key encoding for existing valid names.
  • English errors with libredb: prefix per convention.

Verification

bun run gate

Metadata

Metadata

Assignees

No one assigned

    Labels

    area/lensesPre-announcement auditbugSomething isn't workingpre-announcementPre-announcement auditpriority/highPre-announcement audit

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions