etherpad-lite/src/node/db
John McLear 9706e08260
security(padid): reject ueberdb delimiter ':' in pad ids (copyPad/movePad injection) (#8073)
* security(padid): reject ueberdb delimiter ':' in pad ids (copyPad/movePad injection)

GHSA-wg58-mhwv-35pq. copyPad / movePad / copyPadWithoutHistory take their
destination via the `destinationID` API field, which — unlike padID /
padName — is never run through sanitizePadId. Because isValidPadId only
forbade `$`, a destinationID like `victim:revs:0` survived into the
engine: the embedded `:` (the ueberdb key-namespace delimiter) let it
address another pad's internal `pad:<id>:revs:<n>` records. That both
bypassed the force=false "destination already exists" guard (which checks
only the top-level `atext`) and clobbered the victim pad's revision
history.

- isValidPadId now rejects `:` (never legal in a pad id; it's the DB
  key delimiter). The name portion excludes `$` and `:`.
- Pad.copy() and Pad.copyPadWithoutHistory() validate destinationID via
  isValidPadId before any db write; movePad routes through copy(), so the
  check runs before the source is removed.

Adds isValidPadId unit cases and an integration test proving copyPad /
copyPadWithoutHistory reject a `:`-bearing destinationID with force=false
and leave the victim's rev-0 changeset untouched, while a normal copy
still works. Reported privately (finder credited on the advisory).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* security(padid): sanitize pad URL before validating so legacy ":" URLs still redirect

Addresses Qodo review on #8073. Rejecting ":" in isValidPadId made
padurlsanitize's validate-first ordering return 404 for a browser
visiting a legacy `/p/<id with ":">` URL, instead of redirecting it to
the sanitized `_` form.

Reorder padurlsanitize to sanitize FIRST (sanitizePadId maps whitespace
and ":" to "_"), then validate the sanitized id. `/p/foo:bar` now
redirects to `/p/foo_bar` as before; an id that stays invalid after
sanitizing (e.g. containing "$") is still forbidden. This restores the
pre-fix redirect behavior while keeping ":" out of stored pad ids.

Adds a regression test for the ":" redirect, the "$" 404, and a clean id.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* security(padid): keep existing pads with ":" reachable

Rejecting ":" in isValidPadId also gates padManager.getPad(), so pads whose id
contains a ":" — legal before GHSA-wg58-mhwv-35pq, which is why padIdTransforms
maps ":" at all — became unopenable, not just uncreatable: sanitizePadId returns
such an id unchanged once the pad exists, and getPad then threw.

Refuse an invalid id only when no pad with that exact id exists (in getPad and in
the pad-URL param). The injection primitive stays closed: doesPadExist() requires
a top-level `atext`, which the `pad:<id>:revs:<n>` sub-records an injected
destinationID addresses do not have. Copy destinations keep the strict check, so
no new ":" id can be created.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: SamTV12345 <40429738+samtv12345@users.noreply.github.com>
2026-07-27 20:36:24 +02:00
..
API.ts feat(pad): suppress deletion token for durable identities + relabel recovery action (#7926) (#7930) 2026-06-13 14:47:11 +01:00
AuthorManager.ts feat(gdpr): admin UI for author erasure (follow-up to #7550) (#7667) 2026-05-14 08:11:48 +01:00
DB.ts fix: page sessionstorage cleanup to avoid OOM (#7830) (#7831) 2026-05-22 10:56:35 +01:00
GroupManager.ts feat(gdpr): pad deletion controls (PR1 of #6701) (#7546) 2026-05-01 13:50:04 +01:00
Pad.ts security(padid): reject ueberdb delimiter ':' in pad ids (copyPad/movePad injection) (#8073) 2026-07-27 20:36:24 +02:00
PadDeletionManager.ts feat(gdpr): pad deletion controls (PR1 of #6701) (#7546) 2026-05-01 13:50:04 +01:00
PadManager.ts security(padid): reject ueberdb delimiter ':' in pad ids (copyPad/movePad injection) (#8073) 2026-07-27 20:36:24 +02:00
ReadOnlyManager.ts chore: moved first files to esm (#7061) 2025-08-04 19:59:28 +02:00
SecurityManager.ts chore: migrated settings to esm6 (#7062) 2025-08-04 22:42:50 +02:00
SessionManager.ts chore: moved first files to esm (#7061) 2025-08-04 19:59:28 +02:00
SessionStore.ts fix: page sessionstorage cleanup to avoid OOM (#7830) (#7831) 2026-05-22 10:56:35 +01:00