You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
harden: assorted server-side tightening for 3.0.2 (#7784)
* harden: assorted security tightening across server entry points
A bundle of defence-in-depth hardening picked up during an internal
audit pass. Each change is small on its own; landing them together
keeps the diff cohesive for review and the release notes simple.
Production-side changes:
- src/node/handler/APIHandler.ts: tighten the OAuth JWT validation
path on the HTTP API. Verify the signature before reading any
claim off the payload, and require the admin claim to be strictly
true (not just present). Switch the apikey comparison to
crypto.timingSafeEqual.
- src/node/handler/{Import,Export}Handler.ts: derive temp-file path
tokens from crypto.randomBytes(16) instead of Math.random.
- src/node/hooks/express/tokenTransfer.ts: enforce a 5-minute TTL
on transfer records, make redemption single-use (remove before
response), and drop the author token from the response body —
the HttpOnly cookie is the only delivery channel.
- src/node/utils/sanitizeProxyPath.ts (new): shared sanitiser for
the `x-proxy-path` header. Used by admin.ts (HTML/JS/CSS
substitution) and specialpages.ts (legacy timeslider redirect).
Strips characters outside [A-Za-z0-9_./-], collapses leading
`//+` to a single `/`, rejects `..` traversal. admin.ts also
emits Vary: x-proxy-path and Cache-Control: private, no-store.
- src/node/db/Pad.ts + src/node/utils/ImportHtml.ts: centralise
the "every insert op carries an author attribute" invariant in
Pad.appendRevision so all non-wire callers (setText, setHTML,
restoreRevision, plugin paths) get the same check the socket
handler already enforces. Pad.init and setPadHTML now
substitute SYSTEM_AUTHOR_ID when no author is supplied — same
pattern setText/spliceText already use.
Tests:
- src/tests/backend/specs/api/jwtAdminClaim.ts (5 cases)
- src/tests/backend/specs/tokenTransfer.ts (6 cases)
- src/tests/backend/specs/proxyPathRedirect.ts (5 cases)
- src/tests/backend/specs/padInsertAuthorInvariant.ts (4 cases)
- src/tests/backend-new/specs/sanitizeProxyPath.test.ts (14 vitest cases)
- src/tests/backend/common.ts: add generateJWTTokenAdminFalse helper.
Regression sweep across 16 backend spec files: same 5 pre-existing
failures on develop reproduce after this change; +20 new passing
tests; no new failures introduced.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* harden: rewrite imported .etherpad records to satisfy the insert-op invariant
Legacy .etherpad exports (and exports from older server-internal flows
that didn't substitute SYSTEM_AUTHOR_ID) can contain `+content` insert
ops without an `author` attribute. The previous commit's appendRevision
guard rejects that shape, but setPadRaw bulk-writes records directly
to the DB and never goes through appendRevision -- so a hand-crafted
.etherpad file could persist non-conforming data that any subsequent
setText / setHTML / restoreRevision call would then refuse to extend.
Add a pre-pass over the parsed import that:
- walks revs in numeric order, sanitising each changeset's `+` ops
against the cumulative pad pool (mutating the pool to register
SYSTEM_AUTHOR_ID when needed);
- re-applies each (post-sanitisation) changeset to a running atext
so the head atext and any key-rev meta.atext / meta.pool
snapshots are re-derived in lock-step with the rewritten revs
(otherwise pad.check's deep-equal at the end of setPadRaw would
fail on the attribute-number drift between the sanitised head
state and the now-stale key-rev snapshot);
- leaves already-conforming payloads untouched (no log noise on
good imports).
Returns the number of ops rewritten so the import can log a single
warning per legacy file rather than per-record. Pure-newline `+` ops
are exempted -- same whitelist as the wire-side guard.
Tests:
- tests/backend/specs/padInsertAuthorInvariant.ts: two new cases
drive setPadRaw end-to-end with a hand-crafted legacy payload
(asserts the head atext gains a `*N` attribute reference and the
pool registers `[author, a.etherpad-system]`) and with a
conforming payload (asserts the data round-trips unchanged).
Regression sweep across 17 backend spec files: 209 passing / 12
pending / 5 failing -- same 5 pre-existing failures present on
unmodified develop. +2 new passing tests; no new failures introduced.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
0 commit comments