github ether/etherpad v3.2.0

4 hours ago

3.2.0

3.2 adds first-class reverse-proxy / ingress support — X-Forwarded-Prefix and X-Ingress-Path are now honoured under trustProxy, so Etherpad can live under a subpath (Traefik, Nginx, Kubernetes Ingress) without breaking the PWA manifest, social-meta URLs, or any of the bootstrap asset links. The admin settings page learns to show resolved runtime values next to ${VAR:default} placeholders, the v3.1.0 admin pad-list filter chips now apply server-side (so "show empty pads" no longer returns 0–12 of hundreds), and the v3.1.0 redesigned outdated-version gritter actually fires in production now (the session-based author lookup it shipped with always returned null for pad visitors).

Notable enhancements

  • HTTP — accept X-Forwarded-Prefix and X-Ingress-Path under trustProxy (#7802 / #7806). With trustProxy: true, Etherpad now honours X-Forwarded-Prefix (de-facto Traefik / Spring) and X-Ingress-Path (Kubernetes Ingress) in addition to the prefix it already inferred from the request path. The shared sanitizeProxyPath helper added in 3.1.0 (defence-in-depth: [A-Za-z0-9_./-] only, //+ collapsed, .. traversal rejected) is extended to the new headers and applied consistently across /manifest.json, socialMeta og:url / og:image, and the index.html / pad.html / timeslider.html / export_html.html templates (manifest links, jslicense links, reconnect URLs). A pre-existing .. segment-count miscalculation in pad.html / timeslider.html that broke the manifest link when served from a deep subpath is also fixed in passing. New end-to-end suite covers the prefix-applied / prefix-ignored matrix under trustProxy=true|false for both header names. settings.json.template documents the new headers alongside the existing trustProxy notes.
  • Admin settings — resolved runtime values surface on env-pill chips (#7803 / #7807). The /admin/settings socket payload now carries a new resolved field alongside the existing raw-file results blob, carrying the actual in-memory settings module run through a new redactor (AdminSettingsRedact) that replaces known-sensitive paths (users.*.password, dbSettings.password, sso.clients[*].client_secret, sessionKey, …) with [REDACTED]. The admin SPA's EnvPill renders a → active value chip when the path is resolved, or → •••••• with a redacted tooltip when the server returned the sentinel — so port: ${PORT:9001} now shows → 9001 (or whatever the live value is) instead of silently falling back to the template default. Old admin SPAs that don't read resolved continue to work; the save round-trip is unchanged so ${VAR:default} literals are still preserved verbatim on disk. The admin test script glob picks up .test.tsx alongside .test.ts so the new EnvPill and resolveByPath tests run under tsx --test.

Notable fixes

  • Admin pads — filter chip now applies server-side, before pagination (#7798). The 3.1.0 admin pad-list filter chips (active / recent / empty / stale) ran on the client after the 12-row page slice had already arrived. On a deployment with hundreds of pads, clicking "empty pads" on page 1 only matched the 0–12 empties that happened to land in the current page, with the pagination footer reporting nonsense totals (reported on a 3.1.0 deployment). The filter is now part of the padLoad socket query — pattern filter on names runs first (cheap), metadata hydration for the matching pad universe is gated on a non-all filter or a non-padName sort and runs under a 16-way concurrency cap (was unbounded Promise.all, which fanned out to thousands of in-flight padManager.getPad() reads on busy deployments), then the filter chip, then sort + slice. total reflects the filtered universe so the footer makes sense. Older admin clients that don't send filter keep working — the server defaults to all. The if/else if ladder that duplicated the hydrate-and-sort loop per sortBy is folded into one pipeline with a single comparator switch.
  • Pad outdated notice — author now resolved from token cookie, not session (Qodo #7804 / #7805). The 3.1.0 redesigned outdated-version gritter never fired in production. resolveRequestAuthor() looked for an authorID on req.session.user, which Etherpad does not populate for pad visitors (express-session only carries the admin-login user), so computeOutdated() always returned EMPTY. The lookup now mirrors how the socket.io handshake resolves pad-visitor identity — read the HttpOnly token (or <prefix>token) cookie and call authorManager.getAuthorId(token, user) via a dynamic import (same circular-init guard pattern the file already uses for PadManager). The admin OpenAPI document gains a description note clarifying that /api/version-status is a public pad-side endpoint that lives in the admin doc only because it shares the same internal route registration.
  • Localisation — silence spurious "could not translate element content" warning (#7797). <select data-l10n-id="…"> with <option> element children — the pattern used by ep_headings2, ep_align, ep_font_size, ep_font_family, … — used to drop into the textContent branch of html10n.translateNode, hunt for a text-node child to overwrite, find none, and emit Unexpected error: could not translate element content for key … on every pad load. The SELECT / INPUT / TEXTAREA aria-label fallback already lived inside the same else-branch after the warning, so the accessible name landed correctly but the noisy console line still fired. Form-control elements now short-circuit into the aria-label path before the text-node hunt — aria-label is the only sensible localization target for these elements (a <select>'s text is its <option> labels, not its own name). Closes the console warning reported on Etherpad 3.1.0.

Internal / contributor-facing

  • CI — swap archived ep_readonly_guest for ep_guest in the plugin matrix (#7795 / #7808). ep_readonly_guest is archived (read-only on GitHub) and its authenticate hook unconditionally swapped req.session.user with a read-only guest, even when the request carried an HTTP Authorization header. That silently demoted admin login attempts and stalled the anonymizeAuthorSocket tests for 14 min/run on every with-plugins CI matrix. The pre-fix theory from 3.1.0 (#7796) blamed ep_hash_auth.handleMessage; that was a red herring — handleMessage only fires on the /pad namespace, never on /settings. ep_guest is the maintained successor (same authors, same purpose); 1.0.72 on npm already defers to basic auth / admin paths. Swapping the matrix unblocks the anonymizeAuthorSocket suite on Linux, Windows, and the upgrade-from-latest-release workflow. The runtime probe added in #7796 stays — it still catches any other authenticate-hook plugin that rejects the test's plain-text credentials (e.g. a future hashed-only plugin).
  • Tests — admin saveSettings round-trip + cross-restart persistence (#7819 / #7820 / #7821). The admin saveSettings socket had zero direct backend coverage and the existing e2e "restart works" test only checked that the page renders after a restart, neither of which catches a deployment that resets settings.json on restart, nor the user-visible workflow that triggered #7819 (add a top-level plugin block via Raw, save, watch it disappear). Three new backend specs (adminSettingsSave.ts) verify byte-for-byte write, top-level-block augmentation round-tripping through the next load, and /* */ comments surviving the write path. A new e2e spec mirrors the #7819 user workflow — open Raw, prepend an ep_oauth-shaped top-level block, save, restartEtherpad(), re-login, confirm the block is still in Raw and surfaces as its own Form-view section (Ep oauth from humanize()). A separate docker.yml job (adminSettings_7819.ts) authenticates via POST /admin-auth/ (always-requireAdmin, regardless of settings.requireAuthentication), saves a hand-built minimal-but-viable settings document containing a marker block, docker exec test greps for it, docker restarts the container, waits for the health probe, and re-greps. Both checks must pass.
  • Bug report template now asks contributors whether the abstraction in their proposed fix matches the rest of the codebase, to head off premature-generalisation fixes earlier in review.

Dependencies

  • ueberdb2 6.0.3 → 6.1.2 (two patch releases of cleanup on top of the 6.1.0 findKeysPaged API that the 3.1.0 sessionstorage OOM fix relies on).
  • semver 7.8.0 → 7.8.1, lru-cache 11.3.6 → 11.5.0, @elastic/elasticsearch 9.4.0 → 9.4.1, pg 8.20.0 → 8.21.0, openapi-backend 5.16.1 → 5.17.0, tsx 4.22.0 → 4.22.3, @tanstack/react-query 5.100.10 → 5.100.11 + @tanstack/react-query-devtools, js-cookie 3.0.6 → 3.0.7, plus two dev-dependency group bumps.

Localisation

  • Multiple updates from translatewiki.net.

Don't miss a new etherpad release

NewReleases is sending notifications on new releases.