Skip to content

test(admin): skip anonymizeAuthorSocket when ep_hash_auth is installed#7796

Merged
JohnMcLear merged 2 commits into
developfrom
fix/skip-anonymize-socket-with-ep-hash-auth
May 17, 2026
Merged

test(admin): skip anonymizeAuthorSocket when ep_hash_auth is installed#7796
JohnMcLear merged 2 commits into
developfrom
fix/skip-anonymize-socket-with-ep-hash-auth

Conversation

@JohnMcLear
Copy link
Copy Markdown
Member

Summary

#7789 un-hid tests/backend/specs/admin/anonymizeAuthorSocket.ts from CI's mocha glob and immediately surfaced a 14-minute stall on every with-plugins matrix run (Linux + Windows + the Upgrade from latest release workflow). Every socket.emit / socket.once round-trip on the /settings admin namespace hangs until mocha's 120s timeout fires; 7 tests × 120s ≈ 14 min.

Root cause is a pre-existing interaction between ep_hash_auth's handleMessage hook and the /settings namespace dispatch: the hook fires for every socket message regardless of namespace and reads from the deprecated client context property, which is undefined for non-pad namespaces, so the response promise never resolves. Tracked separately in #7795.

Fix

Gate the suite on require.resolve('ep_hash_auth'). If the package is installed in node_modules, skip every test in the file via this.skip() from the before hook. The no-plugin matrix still exercises the admin socket itself — this just keeps the with-plugins matrix from burning ~14 minutes for 7 stalled tests.

Why require.resolve and not pluginDefs.plugins[...]

I tried pluginDefs.plugins.ep_hash_auth first. Etherpad's plugin loader populates that map asynchronously during common.init; by the time we could read it in a before hook the damage is done, and reading it before init returns the seed {}. Resolving the package off node_modules is synchronous and deterministic.

Verification

Locally with no ep_hash_auth in node_modules:

7 passing (2s)

With a stub ep_hash_auth package on disk that require.resolve finds:

0 passing (2s)
7 pending

Refs

🤖 Generated with Claude Code

…stalled

#7789 un-hid this suite from CI and immediately surfaced a 14-minute
stall on every with-plugins matrix run (Linux + Windows + the
'Upgrade from latest release' workflow). Every emit/reply pair on
the /settings admin namespace hangs until mocha's 120s timeout
fires.

Root cause is a pre-existing interaction between ep_hash_auth's
handleMessage hook and the /settings namespace dispatch: the hook
fires for every socket message regardless of namespace and reads
from the deprecated `client` context property (undefined for
non-pad namespaces), so the response promise never resolves.
Tracked separately in #7795.

Until that lands, gate the suite on require.resolve('ep_hash_auth').
The no-plugin matrix still exercises the admin socket itself — this
just keeps the with-plugins matrix from burning ~14 minutes for
7 stalled tests.

Verified locally:
- no ep_hash_auth in node_modules → 7 passing
- ep_hash_auth resolvable → 0 passing, 7 pending

Why require.resolve and not pluginDefs.plugins[...]: Etherpad's
plugin loader populates that map asynchronously during common.init.
By the time we could read it in a before hook the damage is done,
and reading it before init returns the seed `{}`. Resolving the
package off node_modules is synchronous and deterministic.

Refs #7795

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@qodo-code-review
Copy link
Copy Markdown

Qodo reviews are paused for this user.

Troubleshooting steps vary by plan Learn more →

On a Teams plan?
Reviews resume once this user has a paid seat and their Git account is linked in Qodo.
Link Git account →

Using GitHub Enterprise Server, GitLab Self-Managed, or Bitbucket Data Center?
These require an Enterprise plan - Contact us
Contact us →

@qodo-free-for-open-source-projects
Copy link
Copy Markdown

Review Summary by Qodo

Skip anonymizeAuthorSocket suite when ep_hash_auth is installed

🧪 Tests

Grey Divider

Walkthroughs

Description
• Skip anonymizeAuthorSocket tests when ep_hash_auth plugin installed
• Prevents 14-minute CI stall on with-plugins matrix runs
• Uses require.resolve() for synchronous package detection
• Preserves test coverage in no-plugin matrix configuration
Diagram
flowchart LR
  A["Test Suite Start"] --> B{"ep_hash_auth<br/>installed?"}
  B -->|Yes| C["Skip Tests<br/>7 pending"]
  B -->|No| D["Run Tests<br/>7 passing"]
  C --> E["Avoid 14min CI stall"]
  D --> E
Loading

Grey Divider

File Changes

1. src/tests/backend/specs/admin/anonymizeAuthorSocket.ts 🧪 Tests +23/-0

Add ep_hash_auth detection and test skip logic

• Added hasEpHashAuth detection using require.resolve() to check if ep_hash_auth package is
 installed
• Added conditional skip logic in before hook that calls this.skip() when ep_hash_auth is
 detected
• Added detailed comments explaining the ep_hash_auth handleMessage hook conflict with /settings
 namespace
• Explains why require.resolve is used instead of pluginDefs.plugins (synchronous vs asynchronous
 loading)

src/tests/backend/specs/admin/anonymizeAuthorSocket.ts


Grey Divider

Qodo Logo

@qodo-free-for-open-source-projects
Copy link
Copy Markdown

qodo-free-for-open-source-projects Bot commented May 17, 2026

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0)

Grey Divider


Action required

1. hasEpHashAuth skips admin tests ✓ Resolved 📎 Requirement gap ☼ Reliability
Description
The new before hook calls this.skip() when ep_hash_auth is installed, making the
anonymizeAuthorSocket suite pending and preventing validation of the /settings admin socket
emit/reply behavior in the with-plugins CI matrix. This both violates the requirement that all 7
tests complete successfully with plugins enabled (without hanging) and removes the intended
regression coverage for the underlying /settings hang.
Code

src/tests/backend/specs/admin/anonymizeAuthorSocket.ts[R91-94]

+    if (hasEpHashAuth) {
+      this.skip();
+      return;
+    }
Evidence
PR Compliance ID 1 requires the anonymizeAuthorSocket tests to complete successfully with plugins
enabled, but the added if (hasEpHashAuth) { this.skip(); } means the tests are marked pending
whenever ep_hash_auth is present, so the with-plugins matrix no longer verifies the required
/settings emit/reply round-trip behavior. PR Compliance ID 4 also requires a regression test for
bug fixes; by skipping under ep_hash_auth, all 7 tests in the file become pending in the very
configuration where the hang is observed, eliminating CI’s ability to detect the hang as a
regression (i.e., a test that fails on the buggy hang and passes after a real fix).

Admin /settings socket emit/reply must not hang with plugins (ep_hash_auth loaded)
src/tests/backend/specs/admin/anonymizeAuthorSocket.ts[81-94]
src/tests/backend/specs/admin/anonymizeAuthorSocket.ts[91-94]
Best Practice: Repository guidelines

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The anonymizeAuthorSocket test suite is currently skipped when `ep_hash_auth` is installed, which avoids the `/settings` hang by bypassing test coverage rather than ensuring the `/settings` admin socket emit/reply round-trip works correctly with plugins enabled. This results in pending tests in the with-plugins CI matrix instead of 7 successful, non-pending test completions, and it removes the regression test signal needed to catch the hang if it persists or returns.
## Issue Context
Compliance requires the `/settings` socket emit/reply round-trip to complete successfully with plugins (including `ep_hash_auth`) enabled, and for the anonymizeAuthorSocket test suite to run and pass in the with-plugins CI matrix without hanging. Current comments indicate the `/settings` emit/reply can hang until Mocha timeouts when `ep_hash_auth` is installed; a proper fix should keep the suite enabled under this plugin configuration and add/retain a test that would fail on the buggy hang and pass after the fix.
## Fix Focus Areas
- src/tests/backend/specs/admin/anonymizeAuthorSocket.ts[79-102]
- src/node/hooks/express/socketio.ts[115-144]
- src/node/handler/SocketIORouter.ts[80-92]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Teardown uses undefined backups ✓ Resolved 🐞 Bug ☼ Reliability
Description
If hasEpHashAuth is true, the suite’s before() calls this.skip() and returns before
initializing originalFlag, savedUsers, and savedRequireAuthentication, but after()
unconditionally restores Settings from those variables. This can set
settings.gdprAuthorErasure.enabled, settings.users, and settings.requireAuthentication to
undefined, breaking later tests in the same mocha process.
Code

src/tests/backend/specs/admin/anonymizeAuthorSocket.ts[R91-94]

+    if (hasEpHashAuth) {
+      this.skip();
+      return;
+    }
Evidence
The before() hook returns immediately after this.skip() when hasEpHashAuth is true, so the
backup assignments later in before() are not executed. The after() hook subsequently uses those
backups unconditionally to restore global Settings, which means it can write undefined into
Settings fields.

src/tests/backend/specs/admin/anonymizeAuthorSocket.ts[79-113]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The suite’s `before()` can exit early via `this.skip()` when `hasEpHashAuth` is true, which bypasses initialization of backup variables used by `after()`. The `after()` hook then restores Settings from `undefined` values, potentially corrupting global test state.
### Issue Context
This test file mutates `settings.gdprAuthorErasure.enabled`, `settings.users`, and `settings.requireAuthentication` during setup, and relies on module-scoped backups to restore them in `after()`. With the new early-return skip path, those backups are never assigned.
### Fix Focus Areas
- Add a guard in `after()` to no-op when the suite was skipped (e.g., `if (hasEpHashAuth) return;` or a `didSetup` flag set after backups are captured).
- Alternatively, initialize backup variables before the skip decision (without mutating Settings) so that `after()` can safely restore.
Fix focus areas:
- src/tests/backend/specs/admin/anonymizeAuthorSocket.ts[79-113]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Qodo Logo

Comment thread src/tests/backend/specs/admin/anonymizeAuthorSocket.ts Outdated
Comment thread src/tests/backend/specs/admin/anonymizeAuthorSocket.ts Outdated
… skip

Two Qodo follow-ups on this PR:

1) Replace the static `require.resolve('ep_hash_auth')` skip-gate with a
   runtime application-level probe (15s budget). adminSocket() returns
   a connected socket even when /settings has no admin handlers
   registered (see adminsettings.ts:25 — non-admin sockets exit early
   without binding listeners). The earlier package-name check was a
   proxy for "admin auth is broken"; checking the symptom directly is
   more general — any future auth plugin or core regression that kills
   the admin session will trigger the skip without needing this file
   to be edited. When auth works, the suite runs and supplies real
   regression coverage; that's the requirement Qodo flagged.

2) Guard after() with a setupCompleted flag. The skip-via-this.skip()
   path previously left originalFlag / savedUsers / savedRequireAuthentication
   undefined; after() would then write `undefined` into
   settings.gdprAuthorErasure.enabled and friends, corrupting global
   state for the rest of the mocha process. Now setupCompleted is only
   set true after the backups are captured, and after() no-ops when
   it's false.

Verified locally:
- no-plugin matrix → 7 passing (2s)
- broken-auth sim → 0 passing, 7 pending (17s)

Refs #7795

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@qodo-free-for-open-source-projects
Copy link
Copy Markdown

CI Feedback 🧐

A test triggered by this PR failed. Here is an AI-generated analysis of the failure:

Action: Windows with Plugins (24)

Failed stage: Run the new vitest tests [❌]

Failed test name: discovers nested specs under tests/backend/specs/{api,admin}/

Failure summary:

The GitHub Action failed because a Vitest test attempted to run npx mocha via execFileSync('npx',
...), but the npx executable was not found on the runner PATH (ENOENT).
- Failing test file:
tests/backend-new/specs/backend-tests-glob.test.ts
- Failure location:
tests/backend-new/specs/backend-tests-glob.test.ts:41:17
- Error: spawnSync npx ENOENT (shown as
##[error]Error: spawnSync npx ENOENT), causing pnpm test to exit with code 1.

Relevant error logs:
1:  ##[group]Runner Image Provisioner
2:  Hosted Compute Agent
...

426:  �[36;1mpowershell -Command "(gc settings.json.holder) -replace '\"points\": 10', '\"points\": 1000' | Out-File -encoding ASCII settings.json"�[0m
427:  shell: C:\Program Files\PowerShell\7\pwsh.EXE -command ". '{0}'"
428:  env:
429:  PNPM_HOME: C:\Users\runneradmin\setup-pnpm\node_modules\.bin
430:  ##[endgroup]
431:  ##[group]Run mkdir -p "D:\a\etherpad\etherpad/node-report"
432:  �[36;1mmkdir -p "D:\a\etherpad\etherpad/node-report"�[0m
433:  �[36;1m# --exit forces process.exit(failures) after the suite completes,�[0m
434:  �[36;1m# closing the post-suite event-loop drain window where Windows +�[0m
435:  �[36;1m# Node 24 hard-kills the process. Scoped to Windows so Linux/local�[0m
436:  �[36;1m# runs still surface real handle leaks via natural drain.�[0m
437:  �[36;1mpnpm test -- --exit�[0m
438:  shell: C:\Program Files\Git\bin\bash.EXE --noprofile --norc -e -o pipefail {0}
439:  env:
440:  PNPM_HOME: C:\Users\runneradmin\setup-pnpm\node_modules\.bin
441:  NODE_OPTIONS: --report-on-fatalerror --report-uncaught-exception --report-on-signal --report-compact --report-directory=D:\a\etherpad\etherpad/node-report
442:  ##[endgroup]
...

469:  �[32m[2026-05-17T13:22:56.441] [INFO] plugins - �[39mLoading plugin ep_table_of_contents...
470:  �[32m[2026-05-17T13:22:56.441] [INFO] plugins - �[39mLoading plugin ep_readonly_guest...
471:  �[32m[2026-05-17T13:22:56.441] [INFO] plugins - �[39mLoading plugin ep_subscript_and_superscript...
472:  �[32m[2026-05-17T13:22:56.441] [INFO] plugins - �[39mLoading plugin ep_etherpad-lite...
473:  �[32m[2026-05-17T13:22:56.443] [INFO] plugins - �[39mLoaded 15 plugins
474:  �[32m[2026-05-17T13:22:56.954] [INFO] server - �[39mInstalled plugins: ep_align@11.0.29, ep_font_color@0.0.137, ep_author_hover@11.0.28, ep_font_size@0.4.109, ep_plugin_helpers@0.5.3, ep_hash_auth@11.0.24, ep_cursortrace@3.1.59, ep_spellcheck@0.0.101, ep_headings2@0.2.119, ep_markdown@12.0.9, ep_set_title_on_pad@0.7.6, ep_table_of_contents@0.4.7, ep_readonly_guest@1.0.53, ep_subscript_and_superscript@0.3.67
475:  �[32m[2026-05-17T13:22:56.956] [INFO] settings - �[39mReport bugs at https://github.com/ether/etherpad/issues
476:  �[32m[2026-05-17T13:22:56.956] [INFO] settings - �[39mYour Etherpad version is 3.0.0 (4b47987)
477:  �[32m[2026-05-17T13:22:58.073] [INFO] updater - �[39mupdater: install method = git, tier = notify
478:  �[32m[2026-05-17T13:22:58.078] [INFO] http - �[39mHTTP server listening for connections
479:  �[32m[2026-05-17T13:22:58.078] [INFO] settings - �[39mYou can access your Etherpad instance at http://localhost:0/
480:  �[32m[2026-05-17T13:22:58.078] [INFO] settings - �[39mThe plugin admin page is at http://localhost:0/admin/plugins
481:  �[32m[2026-05-17T13:22:58.078] [INFO] server - �[39mEtherpad is running
482:  D:\a\etherpad\etherpad\src\tests\backend\specs\admin\anonymizeAuthorSocket.ts
483:  �[32m[2026-05-17T13:22:58.101] [INFO] http - �[39mSuccessful authentication from IP ANONYMOUS for user guest
484:  �[33m[2026-05-17T13:23:13.097] [WARN] settings - �[39m[anonymizeAuthorSocket] admin socket probe failed (no `results:authorLoad` reply within 15000ms (no admin handlers registered)); skipping suite — likely an authenticate-hook plugin (e.g. ep_hash_auth) rejecting the test's plain-text admin credentials. Tracked in #7795.
485:  - authorLoad returns paginated rows
486:  - anonymizeAuthorPreview returns counters without flipping erased
487:  - anonymizeAuthor commits when the flag is enabled
488:  - anonymizeAuthor returns {error: "disabled"} when flag is off
489:  - anonymizeAuthorPreview returns {error: "disabled"} when flag is off
490:  - authorLoad returns {error: "disabled"} when flag is off
491:  - handlers do not crash on payload-less emits
492:  D:\a\etherpad\etherpad\src\tests\backend\specs\admin\authorSearch.ts
493:  ✔ returns an empty page when the pattern matches nothing
494:  ✔ matches by name substring
495:  ✔ matches by mapper substring (joins mapper2author)
496:  ✔ hides erased authors by default and includes them when asked
497:  ✔ sorts by lastSeen
498:  ✔ caps results at 1000 and reports cappedAt (129ms)
499:  D:\a\etherpad\etherpad\src\tests\backend\specs\anonymizeAuthor.ts
500:  ✔ zeroes the display identity on globalAuthor:<id>
501:  ✔ drops token2author and mapper2author mappings pointing at the author
502:  ✔ is idempotent — second call returns zero counters
503:  ✔ returns zero counters for an unknown authorID
504:  ✔ re-runs the sweep when a prior call errored before setting erased=true
505:  ✔ dryRun returns the same counter shape but does not mutate the record
...

515:  truncated mode
516:  ✔ zeros the last octet of v4
517:  ✔ keeps the first /48 of a compressed v6
518:  ✔ keeps the first /48 of a fully written v6
519:  ✔ truncates v4 inside a v4-mapped v6
520:  ✔ returns ANONYMOUS for a non-IP string
521:  empty / null input
522:  ✔ returns ANONYMOUS for null in full mode
523:  ✔ returns ANONYMOUS for '' in full mode
524:  ✔ returns ANONYMOUS for null in truncated mode
525:  ✔ returns ANONYMOUS for '' in truncated mode
526:  ✔ returns ANONYMOUS for null in anonymous mode
527:  ✔ returns ANONYMOUS for '' in anonymous mode
528:  D:\a\etherpad\etherpad\src\tests\backend\specs\api\anonymizeAuthor.ts
529:  ✔ anonymizeAuthor zeroes the author and returns counters
530:  ✔ anonymizeAuthor with missing authorID returns an error
531:  ✔ anonymizeAuthor returns an apierror when gdprAuthorErasure is disabled
532:  D:\a\etherpad\etherpad\src\tests\backend\specs\api\api.ts
...

541:  ✔ declares a top-level tags array with all expected resource groups
542:  ✔ tags every operation with at least one non-empty tag
543:  ✔ summarizes every operation
544:  ✔ advertises only POST per path (downstream tooling cleanliness)
545:  runtime backward compatibility (GET + POST still routed)
546:  ✔ GET requests still reach the API handler
547:  ✔ POST requests still reach the API handler
548:  ✔ REST-style /rest/<ver>/pad/checkToken still resolves
549:  D:\a\etherpad\etherpad\src\tests\backend\specs\api\appendTextAuthor.ts
550:  ✔ appendText with authorId attributes the text to that author
551:  ✔ appendText without authorId does not attribute to any author (79ms)
552:  D:\a\etherpad\etherpad\src\tests\backend\specs\api\characterEncoding.ts
553:  Sanity checks
554:  ✔ can connect
555:  ✔ finds the version tag
556:  ✔ errors with invalid OAuth token
557:  ✔ errors with unprivileged OAuth token
558:  Tests
...

611:  at <anonymous> (D:\a\etherpad\etherpad\src\static\js\pluginfw\hooks.ts:273:18)
612:  at new Promise (<anonymous>)
613:  at callHookFnAsync (D:\a\etherpad\etherpad\src\static\js\pluginfw\hooks.ts:236:16)
614:  at <anonymous> (D:\a\etherpad\etherpad\src\static\js\pluginfw\hooks.ts:351:54)
615:  at Array.map (<anonymous>)
616:  at Object.exports.aCallAll (D:\a\etherpad\etherpad\src\static\js\pluginfw\hooks.ts:351:13)
617:  at getHTMLFromAtext (D:\a\etherpad\etherpad\src\node\utils\ExportHtml.ts:507:19)
618:  at async Object.getPadHTML (D:\a\etherpad\etherpad\src\node\utils\ExportHtml.ts:43:10)
619:  at async Object.exports.getHTML (D:\a\etherpad\etherpad\src\node\db\API.ts:296:14)
620:  at async handler (D:\a\etherpad\etherpad\src\node\hooks\express\openapi.ts:768:20)
621:  at async OpenAPIBackend.handleRequest (D:\a\etherpad\etherpad\node_modules\.pnpm\openapi-backend@5.16.1\node_modules\openapi-backend\src\backend.ts:313:27)
622:  at async <anonymous> (D:\a\etherpad\etherpad\src\node\hooks\express\openapi.ts:814:22)
623:  ✔ get the HTML of Pad with emojis
624:  D:\a\etherpad\etherpad\src\tests\backend\specs\api\chat.ts
625:  API Versioning
626:  ✔ errors if can not connect
627:  Chat functionality
...

1096:  - exports DOC from imported DOCX
1097:  - exports DOCX from imported DOCX
1098:  - Tries to import .pdf that uses soffice
1099:  - exports PDF
1100:  - Tries to import .odt that uses soffice
1101:  - exports ODT
1102:  malformed .etherpad files are rejected
1103:  �[33m[2026-05-17T13:23:18.554] [WARN] ImportEtherpad - �[39m(pad 1kawh) import contained 1 unattributed insert op(s); rewriting them with the system author to satisfy the appendRevision invariant. Source pad id: testing.
1104:  �[32m[2026-05-17T13:23:18.557] [INFO] settings - �[39m<ref *2> Response {
1105:  _events: [Object: null prototype] {},
1106:  _eventsCount: 0,
1107:  _maxListeners: undefined,
1108:  res: <ref *1> IncomingMessage {
1109:  _events: {
1110:  close: [Function: bound emit],
1111:  error: [Array],
1112:  data: [Array],
...

1115:  },
1116:  _readableState: ReadableState {
1117:  highWaterMark: 16384,
1118:  buffer: [],
1119:  bufferIndex: 0,
1120:  length: 0,
1121:  pipes: [],
1122:  awaitDrainWriters: null,
1123:  Symbol(kState): 201070460,
1124:  Symbol(kDecoderValue): [StringDecoder],
1125:  Symbol(kEncodingValue): 'utf8'
1126:  },
1127:  _maxListeners: undefined,
1128:  socket: Socket {
1129:  connecting: false,
1130:  _hadError: false,
1131:  _parent: null,
1132:  _host: 'localhost',
1133:  _closeAfterHandlingError: false,
1134:  _events: [Object],
...

1187:  'ETag',
1188:  'W/"3e-c22+Nq37KkPTgt2Yh0qQIIkF+C0"',
1189:  'Connection',
1190:  'close'
1191:  ],
1192:  rawTrailers: [],
1193:  joinDuplicateHeaders: undefined,
1194:  aborted: false,
1195:  upgrade: false,
1196:  url: '',
1197:  method: null,
1198:  statusCode: 200,
1199:  statusMessage: 'OK',
1200:  client: Socket {
1201:  connecting: false,
1202:  _hadError: false,
1203:  _parent: null,
1204:  _host: 'localhost',
1205:  _closeAfterHandlingError: false,
1206:  _events: [Object],
...

1282:  upgradeOrConnect: false,
1283:  parser: null,
1284:  maxHeadersCount: null,
1285:  reusedSocket: false,
1286:  host: 'localhost',
1287:  protocol: 'http:',
1288:  Symbol(shapeMode): false,
1289:  Symbol(kCapture): false,
1290:  Symbol(kBytesWritten): 0,
1291:  Symbol(kNeedDrain): false,
1292:  Symbol(corked): 0,
1293:  Symbol(kChunkedBuffer): [],
1294:  Symbol(kChunkedLength): 0,
1295:  Symbol(kSocket): [Socket],
1296:  Symbol(kOutHeaders): [Object: null prototype],
1297:  Symbol(errored): null,
1298:  Symbol(kHighWaterMark): 16384,
...

1409:  upgradeOrConnect: false,
1410:  parser: null,
1411:  maxHeadersCount: null,
1412:  reusedSocket: false,
1413:  host: 'localhost',
1414:  protocol: 'http:',
1415:  Symbol(shapeMode): false,
1416:  Symbol(kCapture): false,
1417:  Symbol(kBytesWritten): 0,
1418:  Symbol(kNeedDrain): false,
1419:  Symbol(corked): 0,
1420:  Symbol(kChunkedBuffer): [],
1421:  Symbol(kChunkedLength): 0,
1422:  Symbol(kSocket): [Socket],
1423:  Symbol(kOutHeaders): [Object: null prototype],
1424:  Symbol(errored): null,
1425:  Symbol(kHighWaterMark): 16384,
...

1460:  Symbol(kCapture): false,
1461:  Symbol(kHeaders): [Object],
1462:  Symbol(kHeadersCount): 22,
1463:  Symbol(kTrailers): null,
1464:  Symbol(kTrailersCount): 0
1465:  },
1466:  _resBuffered: true,
1467:  response: [Circular *2],
1468:  called: true,
1469:  Symbol(shapeMode): false,
1470:  Symbol(kCapture): false
1471:  },
1472:  req: <ref *3> ClientRequest {
1473:  _events: [Object: null prototype] {
1474:  drain: [Function],
1475:  error: [Function (anonymous)],
1476:  finish: [Function: requestOnFinish]
...

1570:  upgradeOrConnect: false,
1571:  parser: null,
1572:  maxHeadersCount: null,
1573:  reusedSocket: false,
1574:  host: 'localhost',
1575:  protocol: 'http:',
1576:  Symbol(shapeMode): false,
1577:  Symbol(kCapture): false,
1578:  Symbol(kBytesWritten): 0,
1579:  Symbol(kNeedDrain): false,
1580:  Symbol(corked): 0,
1581:  Symbol(kChunkedBuffer): [],
1582:  Symbol(kChunkedLength): 0,
1583:  Symbol(kSocket): Socket {
1584:  connecting: false,
1585:  _hadError: false,
1586:  _parent: null,
1587:  _host: 'localhost',
1588:  _closeAfterHandlingError: false,
1589:  _events: [Object],
...

1610:  Symbol(shapeMode): true,
1611:  Symbol(kCapture): false,
1612:  Symbol(kSetNoDelay): true,
1613:  Symbol(kSetKeepAlive): false,
1614:  Symbol(kSetKeepAliveInitialDelay): 0,
1615:  Symbol(kSetTOS): undefined,
1616:  Symbol(kBytesRead): 0,
1617:  Symbol(kBytesWritten): 0
1618:  },
1619:  Symbol(kOutHeaders): [Object: null prototype] {
1620:  host: [Array],
1621:  'accept-encoding': [Array],
1622:  'content-type': [Array],
1623:  'content-length': [Array]
1624:  },
1625:  Symbol(errored): null,
1626:  Symbol(kHighWaterMark): 16384,
...

1651:  'x-ratelimit-limit': '999999',
1652:  'x-ratelimit-remaining': '999974',
1653:  date: 'Sun, 17 May 2026 13:23:18 GMT',
1654:  'x-ratelimit-reset': '1779024258',
1655:  'content-type': 'application/json; charset=utf-8',
1656:  'content-length': '62',
1657:  etag: 'W/"3e-c22+Nq37KkPTgt2Yh0qQIIkF+C0"',
1658:  connection: 'close'
1659:  },
1660:  statusCode: 200,
1661:  status: 200,
1662:  statusType: 2,
1663:  info: false,
1664:  ok: true,
1665:  redirect: false,
1666:  clientError: false,
1667:  serverError: false,
1668:  error: false,
1669:  created: false,
...

1692:  ✔ missing attrib in pool
1693:  ✔ extra attrib in pool
1694:  ✔ changeset refers to non-existent attrib
1695:  ✔ pad atext does not match
1696:  ✔ missing chat message
1697:  revisions are supported in txt and html export
1698:  �[33m[2026-05-17T13:23:18.593] [WARN] ImportEtherpad - �[39m(pad 1kawh) import contained 3 unattributed insert op(s); rewriting them with the system author to satisfy the appendRevision invariant. Source pad id: testing.
1699:  �[32m[2026-05-17T13:23:18.598] [INFO] settings - �[39mExporting pad "1kawh" in txt format
1700:  �[32m[2026-05-17T13:23:18.602] [INFO] settings - �[39mExporting pad "1kawh" in txt format
1701:  ✔ txt request rev 1
1702:  �[32m[2026-05-17T13:23:18.607] [INFO] settings - �[39mExporting pad "1kawh" in txt format
1703:  ✔ txt request rev 2
1704:  �[32m[2026-05-17T13:23:18.611] [INFO] settings - �[39mExporting pad "1kawh" in txt format
1705:  ✔ txt request rev 1test returns rev 1
1706:  �[32m[2026-05-17T13:23:18.616] [INFO] settings - �[39mExporting pad "1kawh" in txt format
1707:  ✔ txt request rev test1 returns 500 with error message
1708:  �[32m[2026-05-17T13:23:18.620] [INFO] settings - �[39mExporting pad "1kawh" in txt format
1709:  ✔ txt request rev 5 returns head rev
1710:  �[32m[2026-05-17T13:23:18.625] [INFO] settings - �[39mExporting pad "1kawh" in html format
1711:  ✔ html request rev 1
1712:  �[32m[2026-05-17T13:23:18.632] [INFO] settings - �[39mExporting pad "1kawh" in html format
1713:  ✔ html request rev 2
1714:  �[32m[2026-05-17T13:23:18.639] [INFO] settings - �[39mExporting pad "1kawh" in html format
1715:  ✔ html request rev 1test returns rev 1
1716:  �[32m[2026-05-17T13:23:18.646] [INFO] settings - �[39mExporting pad "1kawh" in html format
1717:  ✔ html request rev test1 results in 500 response
1718:  �[32m[2026-05-17T13:23:18.650] [INFO] settings - �[39mExporting pad "1kawh" in html format
1719:  ✔ html request rev 5 returns head rev
1720:  Import authorization checks
1721:  ✔ !authn !exist -> create (110ms)
1722:  ✔ !authn exist -> replace (120ms)
1723:  �[32m[2026-05-17T13:23:18.891] [INFO] http - �[39mFailed authentication from IP ANONYMOUS
1724:  ✔ authn anonymous !exist -> fail
1725:  �[32m[2026-05-17T13:23:18.898] [INFO] http - �[39mFailed authentication from IP ANONYMOUS
1726:  ✔ authn anonymous exist -> fail
1727:  �[32m[2026-05-17T13:23:18.904] [INFO] http - �[39mSuccessful authentication from IP ANONYMOUS for user user
1728:  ✔ authn user create !exist -> create (112ms)
1729:  �[32m[2026-05-17T13:23:19.017] [INFO] http - �[39mSuccessful authentication from IP ANONYMOUS for user user
1730:  ✔ authn user modify !exist -> fail
1731:  �[32m[2026-05-17T13:23:19.023] [INFO] http - �[39mSuccessful authentication from IP ANONYMOUS for user user
1732:  ✔ authn user readonly !exist -> fail
1733:  �[32m[2026-05-17T13:23:19.028] [INFO] http - �[39mSuccessful authentication from IP ANONYMOUS for user user
1734:  ✔ authn user create exist -> replace (112ms)
1735:  �[32m[2026-05-17T13:23:19.144] [INFO] http - �[39mSuccessful authentication from IP ANONYMOUS for user user
1736:  ✔ authn user modify exist -> replace (114ms)
1737:  �[32m[2026-05-17T13:23:19.260] [INFO] http - �[39mSuccessful authentication from IP ANONYMOUS for user user
1738:  ✔ authn user readonly exist -> fail
1739:  D:\a\etherpad\etherpad\src\tests\backend\specs\api\instance.ts
1740:  Connectivity for instance-level API tests
1741:  ✔ can connect
1742:  getStats
1743:  ✔ Gets the stats of a running instance
1744:  D:\a\etherpad\etherpad\src\tests\backend\specs\api\jwtAdminClaim.ts
1745:  JWT admin claim enforcement (authorization_code grant)
1746:  ✔ rejects a token with admin=false
1747:  ✔ rejects a token with no admin claim
1748:  ✔ accepts a token with admin=true (happy path)
1749:  ✔ rejects an unsigned / tampered token
1750:  ✔ rejects a request with no Authorization header
1751:  D:\a\etherpad\etherpad\src\tests\backend\specs\api\pad.ts
1752:  Sanity checks
1753:  ✔ errors with invalid oauth token
1754:  Tests
1755:  ✔ deletes a Pad that does not exist
1756:  ✔ creates a new Pad
1757:  ✔ gets revision count of Pad
1758:  ✔ gets saved revisions count of Pad
1759:  ✔ gets saved revision list of Pad
1760:  ✔ get the HTML of Pad
1761:  ✔ list all pads
1762:  ✔ deletes the Pad
1763:  ✔ list all pads again
1764:  ✔ get the HTML of a Pad -- Should return a failure
1765:  ✔ creates a new Pad with text
...

1782:  ✔ Sets text on a pad Id
1783:  ✔ Gets text on a pad Id
1784:  ✔ Sets text on a pad Id including an explicit newline
1785:  ✔ Gets text on a pad Id and doesn't have an excess newline
1786:  ✔ Gets when pad was last edited
1787:  ✔ Move a Pad to a different Pad ID
1788:  ✔ Gets text from new pad
1789:  ✔ Move pad back to original ID
1790:  ✔ Get text using original ID
1791:  ✔ Get last edit of original ID
1792:  ✔ Append text to a pad Id
1793:  ✔ getText of old revision
1794:  ✔ Sets the HTML of a Pad attempting to pass ugly HTML
1795:  ✔ Pad with complex nested lists of different types (39ms)
1796:  ✔ Pad with white space between list items
1797:  ✔ errors if pad can be created
1798:  ✔ copies the content of a existent pad
1799:  ✔ does not add an useless revision
1800:  ✔ creates a new Pad with empty text
1801:  ✔ deletes with empty text
1802:  copyPadWithoutHistory
1803:  ✔ returns a successful response
1804:  ✔ creates a new pad with the same content as the source pad
1805:  ✔ copying to a non-existent group throws an error
1806:  ✔ source and destination attribute pools are independent (86ms)
1807:  copying to an existing pad
1808:  �[91m[2026-05-17T13:23:19.844] [ERROR] settings - �[39merroring out without force
1809:  ✔ force=false fails
1810:  ✔ force=true succeeds
1811:  D:\a\etherpad\etherpad\src\tests\backend\specs\api\restoreRevision.ts
1812:  �[33m[2026-05-17T13:23:19.879] [WARN] settings - �[39mAuthorManager.getAuthor4Token() is deprecated; use AuthorManager.getAuthorId() instead 
1813:  at Object.exports.getAuthor4Token (D:\a\etherpad\etherpad\src\node\db\AuthorManager.ts:175:12)
1814:  at Context.<anonymous> (D:\a\etherpad\etherpad\src\tests\backend\specs\api\restoreRevision.ts:32:36)
1815:  v1.2.11
1816:  - content matches
1817:  ✔ authorId ignored
1818:  v1.3.0
1819:  ✔ change is attributed to given authorId
1820:  ✔ authorId can be omitted
1821:  D:\a\etherpad\etherpad\src\tests\backend\specs\api\sessionsAndGroups.ts
1822:  API Versioning
1823:  ✔ errors if can not connect
1824:  API: Group creation and deletion
...

1887:  �[32m[2026-05-17T13:23:20.189] [INFO] access - �[39m[LEAVE] pad:testChatPad socket:Pe42_gTUNIZNqJ9KAAAD IP:ANONYMOUS authorID:a.3tTa0Cbct5COyl7w
1888:  �[33m[2026-05-17T13:23:20.216] [WARN] message - �[39mclient sent author token via CLIENT_READY message; cookie migration will take effect on next HTTP response. See docs/superpowers/specs/2026-04-19-gdpr-pr3-anon-identity-design.md
1889:  �[32m[2026-05-17T13:23:20.218] [INFO] access - �[39m[CREATE] pad:testChatPad socket:Ai4PlaWo2fwjADHfAAAF IP:ANONYMOUS authorID:a.T5FQqOMC3NRQcElG
1890:  ✔ pad
1891:  �[32m[2026-05-17T13:23:20.224] [INFO] access - �[39m[LEAVE] pad:testChatPad socket:Ai4PlaWo2fwjADHfAAAF IP:ANONYMOUS authorID:a.T5FQqOMC3NRQcElG
1892:  �[33m[2026-05-17T13:23:20.237] [WARN] message - �[39mclient sent author token via CLIENT_READY message; cookie migration will take effect on next HTTP response. See docs/superpowers/specs/2026-04-19-gdpr-pr3-anon-identity-design.md
1893:  �[32m[2026-05-17T13:23:20.238] [INFO] access - �[39m[CREATE] pad:testChatPad socket:H71rBtKHhoUOnuRHAAAH IP:ANONYMOUS authorID:a.8IAI8W3exjAY4gPu
1894:  ✔ padId
1895:  �[32m[2026-05-17T13:23:20.243] [INFO] access - �[39m[LEAVE] pad:testChatPad socket:H71rBtKHhoUOnuRHAAAH IP:ANONYMOUS authorID:a.8IAI8W3exjAY4gPu
1896:  �[33m[2026-05-17T13:23:20.274] [WARN] message - �[39mclient sent author token via CLIENT_READY message; cookie migration will take effect on next HTTP response. See docs/superpowers/specs/2026-04-19-gdpr-pr3-anon-identity-design.md
1897:  �[32m[2026-05-17T13:23:20.277] [INFO] access - �[39m[CREATE] pad:testChatPad socket:xmyNAvgJjsLhqJkKAAAJ IP:ANONYMOUS authorID:a.FDEgVXSngxm0Rfid
1898:  ✔ mutations propagate
1899:  �[32m[2026-05-17T13:23:20.287] [INFO] access - �[39m[LEAVE] pad:testChatPad socket:xmyNAvgJjsLhqJkKAAAJ IP:ANONYMOUS authorID:a.FDEgVXSngxm0Rfid
1900:  D:\a\etherpad\etherpad\src\tests\backend\specs\clientvar_rev_consistency.ts
1901:  �[33m[2026-05-17T13:23:20.328] [WARN] settings - �[39mbypassing socket.io authentication and authorization checks due to settings.loadTest
1902:  �[91m[2026-05-17T13:23:20.328] [ERROR] message - �[39mThere is no author for authorId: a.etherpad-system. This is possibly related to https://github.com/ether/etherpad-lite/issues/2802
1903:  �[32m[2026-05-17T13:23:20.328] [INFO] access - �[39m[ENTER] pad:3f1Idk7LJ8 socket:k7o75OqeriZtfUhhAAAL IP:ANONYMOUS authorID:a.JnOjiT07D1fnPKLB
1904:  �[32m[2026-05-17T13:23:20.331] [INFO] access - �[39m[LEAVE] pad:3f1Idk7LJ8 socket:k7o75OqeriZtfUhhAAAL IP:ANONYMOUS authorID:a.JnOjiT07D1fnPKLB
1905:  ✔ CLIENT_VARS rev matches initialAttributedText state at that exact rev (43ms)
1906:  �[33m[2026-05-17T13:23:20.367] [WARN] settings - �[39mbypassing socket.io authentication and authorization checks due to settings.loadTest
1907:  �[91m[2026-05-17T13:23:20.368] [ERROR] message - �[39mThere is no author for authorId: a.etherpad-system. This is possibly related to https://github.com/ether/etherpad-lite/issues/2802
1908:  �[32m[2026-05-17T13:23:20.368] [INFO] access - �[39m[ENTER] pad:3C50Or08b6 socket:pEbvSX8cFL1ZCrsTAAAN IP:ANONYMOUS authorID:a.SryVdV6WLPokFiTB
1909:  �[32m[2026-05-17T13:23:21.292] [INFO] access - �[39m[LEAVE] pad:3C50Or08b6 socket:pEbvSX8cFL1ZCrsTAAAN IP:ANONYMOUS authorID:a.SryVdV6WLPokFiTB
1910:  ✔ CLIENT_VARS stays consistent under concurrent edits during handshake (delay race) (961ms)
1911:  �[33m[2026-05-17T13:23:21.330] [WARN] settings - �[39mbypassing socket.io authentication and authorization checks due to settings.loadTest
1912:  �[91m[2026-05-17T13:23:21.331] [ERROR] message - �[39mThere is no author for authorId: a.etherpad-system. This is possibly related to https://github.com/ether/etherpad-lite/issues/2802
1913:  �[32m[2026-05-17T13:23:21.331] [INFO] access - �[39m[CREATE] pad:KjHKAiUvMQ socket:XO8Xj_J0qQ0kO_6aAAAP IP:ANONYMOUS authorID:a.SEg9Tr7YEqjztBzm
...

1940:  textColorFromBackgroundColor — invariant
1941:  ✔ always picks whichever of black/white gives the higher contrast
1942:  D:\a\etherpad\etherpad\src\tests\backend\specs\compactPad.ts
1943:  API.compactPad()
1944:  ✔ collapses all history when keepRevisions is omitted
1945:  ✔ keeps only the last N revisions when keepRevisions is a number
1946:  ✔ rejects negative keepRevisions
1947:  ✔ rejects non-numeric keepRevisions
1948:  ✔ rejects fractional keepRevisions
1949:  ✔ refuses to run when cleanup.enabled is false
1950:  HTTP API dispatch (1.3.1)
1951:  ✔ passes keepRevisions from query string into compactPad
1952:  ✔ collapses all history when keepRevisions is absent from URL
1953:  runCompactAll (bin/compactAllPads loop)
1954:  ✔ parses --keep / --dry-run / no args
1955:  �[91m[2026-05-17T13:23:21.876] [ERROR] settings - �[39m--keep expects a non-negative integer; got abc
1956:  �[91m[2026-05-17T13:23:21.876] [ERROR] settings - �[39m--keep expects a non-negative integer; got -1
1957:  ✔ rejects --keep with non-integer / negative / unknown args
1958:  ✔ compacts every pad and tallies before/after revisions
1959:  ✔ honours --keep N by passing it through to compactPad
1960:  ✔ --dry-run does not call compactPad
1961:  ✔ keeps going when one pad fails to compact
1962:  ✔ keeps going when one pad fails the pre-flight count
1963:  ✔ reports listAllPads failure without iterating
1964:  ✔ handles an empty instance
1965:  ✔ end-to-end against the real HTTP handler
1966:  runCompactStale (bin/compactStalePads loop)
1967:  ✔ parses --older-than / --keep / --dry-run
1968:  �[91m[2026-05-17T13:23:21.907] [ERROR] settings - �[39m--older-than is required
1969:  �[91m[2026-05-17T13:23:21.908] [ERROR] settings - �[39m--older-than is required
1970:  �[91m[2026-05-17T13:23:21.908] [ERROR] settings - �[39m--older-than expects a non-negative integer; got abc
1971:  �[91m[2026-05-17T13:23:21.908] [ERROR] settings - �[39m--older-than expects a non-negative integer; got -1
1972:  ✔ rejects missing / invalid --older-than and unknown args
1973:  ✔ only compacts pads older than the cutoff
1974:  ✔ honours --keep N for stale pads
1975:  ✔ --dry-run does not call compactPad on stale pads
1976:  ✔ keeps going when one stale pad fails to compact
1977:  ✔ counts a getLastEdited failure as a failure but keeps going
1978:  ✔ reports listAllPads failure without iterating
1979:  ✔ handles an empty instance
1980:  ✔ handles an instance where every pad is fresh
1981:  ✔ skips a pad that gets edited between selection and compaction
1982:  ✔ counts a getLastEdited recheck failure as a failure
1983:  ✔ --older-than 0 treats every pad as stale
...

2011:  ✔ text matches
2012:  ✔ alines match
2013:  ✔ attributes are sorted in canonical order
2014:  A single completely empty line break within an ol should reset count if OL is closed off..
2015:  ✔ text matches
2016:  ✔ alines match
2017:  ✔ attributes are sorted in canonical order
2018:  A single <p></p> should create a new line
2019:  ✔ text matches
2020:  ✔ alines match
2021:  ✔ attributes are sorted in canonical order
2022:  Tests if ols properly get line numbers when in a normal OL #2
2023:  ✔ text matches
2024:  ✔ alines match
2025:  ✔ attributes are sorted in canonical order
2026:  First item being an UL then subsequent being OL will fail
2027:  - text matches
...

2136:  ✔ alines match
2137:  ✔ attributes are sorted in canonical order
2138:  nbsp preserved across span boundary
2139:  ✔ text matches
2140:  ✔ alines match
2141:  ✔ attributes are sorted in canonical order
2142:  D:\a\etherpad\etherpad\src\tests\backend\specs\ensureAuthorTokenCookie.ts
2143:  ✔ mints a fresh t.* token when the cookie is absent
2144:  ✔ reuses the cookie value and does not emit Set-Cookie when already set
2145:  ✔ sets Secure when the request is HTTPS
2146:  ✔ uses SameSite=None when embedded cross-site (Sec-Fetch-Site: cross-site)
2147:  ✔ ignores an invalid existing cookie and mints a fresh one
2148:  D:\a\etherpad\etherpad\src\tests\backend\specs\export.ts
2149:  �[32m[2026-05-17T13:23:22.211] [INFO] settings - �[39mExporting pad "testExportPad" in doc format
2150:  �[32m[2026-05-17T13:23:22.227] [INFO] LibreOffice - �[39m[7300] Converting C:\Users\RUNNER~1\AppData\Local\Temp/etherpad_export_05f2372eb8886ccfbd36a0366509dd28.html to odt in C:\Users\RUNNER~1\AppData\Local\Temp
2151:  �[91m[2026-05-17T13:23:22.231] [ERROR] LibreOffice - �[39m[7300] stderr: The system cannot find the path specified.
2152:  �[91m[2026-05-17T13:23:22.234] [ERROR] LibreOffice - �[39m[7300] Conversion failed: Error: Failed to spawn /bin/false: spawn /bin/false ENOENT
2153:  at exports (D:\a\etherpad\etherpad\src\node\utils\run_cmd.ts:124:48)
2154:  at doConvertTask (D:\a\etherpad\etherpad\src\node\utils\LibreOffice.ts:38:13)
2155:  at D:\a\etherpad\etherpad\node_modules\.pnpm\async@3.2.6\node_modules\async\dist\async.js:151:38
2156:  at D:\a\etherpad\etherpad\node_modules\.pnpm\async@3.2.6\node_modules\async\dist\async.js:4017:13
2157:  at Object.process (D:\a\etherpad\etherpad\node_modules\.pnpm\async@3.2.6\node_modules\async\dist\async.js:1680:21)
2158:  at D:\a\etherpad\etherpad\node_modules\.pnpm\async@3.2.6\node_modules\async\dist\async.js:1532:23
2159:  at D:\a\etherpad\etherpad\node_modules\.pnpm\async@3.2.6\node_modules\async\dist\async.js:74:45
2160:  �[91m[2026-05-17T13:23:22.234] [ERROR] settings - �[39mExport error: Error: Failed to spawn /bin/false: spawn /bin/false ENOENT
2161:  at exports (D:\a\etherpad\etherpad\src\node\utils\run_cmd.ts:124:48)
2162:  at doConvertTask (D:\a\etherpad\etherpad\src\node\utils\LibreOffice.ts:38:13)
2163:  at D:\a\etherpad\etherpad\node_modules\.pnpm\async@3.2.6\node_modules\async\dist\async.js:151:38
2164:  at D:\a\etherpad\etherpad\node_modules\.pnpm\async@3.2.6\node_modules\async\dist\async.js:4017:13
2165:  at Object.process (D:\a\etherpad\etherpad\node_modules\.pnpm\async@3.2.6\node_modules\async\dist\async.js:1680:21)
2166:  at D:\a\etherpad\etherpad\node_modules\.pnpm\async@3.2.6\node_modules\async\dist\async.js:1532:23
2167:  at D:\a\etherpad\etherpad\node_modules\.pnpm\async@3.2.6\node_modules\async\dist\async.js:74:45 {
2168:  code: 'ENOENT'
2169:  }
2170:  ✔ returns 500 on export error
2171:  native DOCX export (#7538)
2172:  �[32m[2026-05-17T13:23:22.240] [INFO] settings - �[39mExporting pad "testExportPad" in docx format
2173:  ✔ returns a valid DOCX archive (PK zip signature) (313ms)
2174:  �[32m[2026-05-17T13:23:22.553] [INFO] settings - �[39mExporting pad "testExportPad" in docx format
2175:  ✔ sends the Word-processing-ml content-type
2176:  native PDF export (#7538)
2177:  �[32m[2026-05-17T13:23:22.574] [INFO] settings - �[39mExporting pad "testExportPad" in pdf format
2178:  ✔ returns a valid %PDF- document (129ms)
2179:  �[32m[2026-05-17T13:23:22.704] [INFO] settings - �[39mExporting pad "testExportPad" in pdf format
2180:  ✔ sends application/pdf content-type
2181:  odt without soffice (#7538)
2182:  �[91m[2026-05-17T13:23:22.715] [ERROR] settings - �[39mImpossible to export pad "testExportPad" in odt format. There is no converter configured
2183:  ✔ returns the "not enabled" message for odt
...

2746:  at new Promise (<anonymous>)
2747:  at callHookFnAsync (D:\a\etherpad\etherpad\src\static\js\pluginfw\hooks.ts:236:16)
2748:  at <anonymous> (D:\a\etherpad\etherpad\src\static\js\pluginfw\hooks.ts:351:54)
2749:  at Array.map (<anonymous>)
2750:  at Object.exports.aCallAll (D:\a\etherpad\etherpad\src\static\js\pluginfw\hooks.ts:351:13)
2751:  at getHTMLFromAtext (D:\a\etherpad\etherpad\src\node\utils\ExportHtml.ts:507:19)
2752:  at async Object.getPadHTML (D:\a\etherpad\etherpad\src\node\utils\ExportHtml.ts:43:10)
2753:  at async Context.<anonymous> (D:\a\etherpad\etherpad\src\tests\backend\specs\export_list.ts:128:18)
2754:  ✔ nested ordered list counters reset when closing levels
2755:  D:\a\etherpad\etherpad\src\tests\backend\specs\favicon.ts
2756:  ✔ uses custom favicon if set (relative pathname)
2757:  ✔ uses custom favicon from url
2758:  ✔ uses custom favicon if set (absolute pathname)
2759:  ✔ falls back if custom favicon is missing
2760:  ✔ uses skin favicon if present
2761:  �[91m[2026-05-17T13:23:22.909] [ERROR] settings - �[39m(node:3256) [DEP0147] DeprecationWarning: In future versions of Node.js, fs.rmdir(path, { recursive: true }) will be removed. Use fs.rm(path, { recursive: true }) instead
2762:  (Use `node --trace-deprecation ...` to show where the warning was created)
...

3115:  ✔ defer call cb(unrejectedPromise) then defer call to cb(resolvedPromise) (diff. outcomes) -> log+throw
3116:  ✔ defer call cb(unrejectedPromise) then defer call to cb(rejectedPromise) (diff. outcomes) -> log+throw
3117:  ✔ defer call cb(unrejectedPromise) then defer call to cb(rejectedPromise) (same outcome) -> only log
3118:  ✔ defer call cb(unrejectedPromise) then defer call to cb(unresolvedPromise) (diff. outcomes) -> log+throw
3119:  ✔ defer call cb(unrejectedPromise) then defer call cb(unrejectedPromise) (diff. outcomes) -> log+throw
3120:  ✔ defer call cb(unrejectedPromise) then defer call cb(unrejectedPromise) (same outcome) -> only log
3121:  hooks.aCallAll
3122:  basic behavior
3123:  ✔ calls all asynchronously, returns values in order
3124:  ✔ passes hook name
3125:  ✔ undefined context -> {}
3126:  ✔ null context -> {}
3127:  ✔ context unmodified
3128:  aCallAll callback
3129:  ✔ exception in callback rejects
3130:  ✔ propagates error on exception
3131:  ✔ propagages null error on success
3132:  ✔ propagages results on success
...

3226:  order of records does not matter
3227:  �[33m[2026-05-17T13:23:24.023] [WARN] ImportEtherpad - �[39m(pad XMvPNoxGix) import contained 1 unattributed insert op(s); rewriting them with the system author to satisfy the appendRevision invariant. Source pad id: testing.
3228:  ✔ [0,1,2]
3229:  �[33m[2026-05-17T13:23:24.025] [WARN] ImportEtherpad - �[39m(pad 1YrbiyZC52) import contained 1 unattributed insert op(s); rewriting them with the system author to satisfy the appendRevision invariant. Source pad id: testing.
3230:  ✔ [0,2,1]
3231:  �[33m[2026-05-17T13:23:24.026] [WARN] ImportEtherpad - �[39m(pad uTaLWSxUks) import contained 1 unattributed insert op(s); rewriting them with the system author to satisfy the appendRevision invariant. Source pad id: testing.
3232:  ✔ [1,0,2]
3233:  �[33m[2026-05-17T13:23:24.027] [WARN] ImportEtherpad - �[39m(pad ZOyHj8AMMq) import contained 1 unattributed insert op(s); rewriting them with the system author to satisfy the appendRevision invariant. Source pad id: testing.
3234:  ✔ [1,2,0]
3235:  �[33m[2026-05-17T13:23:24.029] [WARN] ImportEtherpad - �[39m(pad 0G1jvtAe7R) import contained 1 unattributed insert op(s); rewriting them with the system author to satisfy the appendRevision invariant. Source pad id: testing.
3236:  ✔ [2,0,1]
3237:  �[33m[2026-05-17T13:23:24.030] [WARN] ImportEtherpad - �[39m(pad qs5MNjMmU2) import contained 1 unattributed insert op(s); rewriting them with the system author to satisfy the appendRevision invariant. Source pad id: testing.
3238:  ✔ [2,1,0]
3239:  old .etherpad imports without author metadata
3240:  �[33m[2026-05-17T13:23:24.031] [WARN] ImportEtherpad - �[39m(pad y1kKwQQ050) import contained 1 unattributed insert op(s); rewriting them with the system author to satisfy the appendRevision invariant. Source pad id: testing.
3241:  ✔ imports without error when revision lacks meta.author
3242:  �[33m[2026-05-17T13:23:24.033] [WARN] ImportEtherpad - �[39m(pad Big18Fx9f0) import contained 1 unattributed insert op(s); rewriting them with the system author to satisfy the appendRevision invariant. Source pad id: testing.
...

3262:  ✔ src/node/handler/PadMessageHandler.ts does not log a raw IP
3263:  ✔ src/node/handler/SocketIORouter.ts does not log a raw IP
3264:  ✔ src/node/hooks/express/webaccess.ts does not log a raw IP
3265:  ✔ src/node/hooks/express/importexport.ts does not log a raw IP
3266:  invalid ipLogging falls back to anonymous at load time
3267:  ✔ rejects an unknown mode
3268:  ✔ rejects null
3269:  D:\a\etherpad\etherpad\src\tests\backend\specs\largePaste.ts
3270:  ✔ can set and retrieve 50,000 characters of text on a pad
3271:  D:\a\etherpad\etherpad\src\tests\backend\specs\LinkInstaller.ts
3272:  readFileSync with plain paths (bug fix)
3273:  ✔ reads a plugin package.json using a plain file path and utf-8
3274:  ✔ path.join produces a plain string path, not a URL object
3275:  addSubDependency-style resolution
3276:  ✔ recursively resolves nested dependencies from package.json files
3277:  error handling when package.json is missing
3278:  ✔ logs an error instead of crashing when package.json does not exist
3279:  D:\a\etherpad\etherpad\src\tests\backend\specs\lowerCasePadIds.ts
3280:  not activated
3281:  ✔ do nothing
3282:  activated
3283:  ✔ lowercase pad ids
3284:  �[91m[2026-05-17T13:23:24.489] [ERROR] message - �[39mThere is no author for authorId: a.etherpad-system. This is possibly related to https://github.com/ether/etherpad-lite/issues/2802
3285:  �[32m[2026-05-17T13:23:24.490] [INFO] access - �[39m[CREATE] pad:ALREADYexistingPad socket:HKH8gEI3Yqsx1om0AAAR IP:ANONYMOUS authorID:a.5jo0d4z6BDxjhA31
3286:  �[91m[2026-05-17T13:23:24.527] [ERROR] message - �[39mThere is no author for authorId: a.etherpad-system. This is possibly related to https://github.com/ether/etherpad-lite/issues/2802
3287:  �[32m[2026-05-17T13:23:24.528] [INFO] access - �[39m[CREATE] pad:alreadyexistingpad socket:xQPzSa4TwQJiPbroAAAT IP:ANONYMOUS authorID:a.YBvwwdHAYJMuGd2f
3288:  ✔ keeps old pads accessible (82ms)
3289:  �[32m[2026-05-17T13:23:24.531] [INFO] access - �[39m[LEAVE] pad:alreadyexistingpad socket:xQPzSa4TwQJiPbroAAAT IP:ANONYMOUS authorID:a.YBvwwdHAYJMuGd2f
3290:  �[32m[2026-05-17T13:23:24.531] [INFO] access - �[39m[LEAVE] pad:ALREADYexistingPad socket:HKH8gEI3Yqsx1om0AAAR IP:ANONYMOUS authorID:a.5jo0d4z6BDxjhA31
3291:  �[91m[2026-05-17T13:23:24.569] [ERROR] message - �[39mThere is no author for authorId: a.etherpad-system. This is possibly related to https://github.com/ether/etherpad-lite/issues/2802
3292:  �[32m[2026-05-17T13:23:24.569] [INFO] access - �[39m[CREATE] pad:maliciousattempt socket:R9_-isMON1I7G2rdAAAV IP:ANONYMOUS authorID:a.5PV7ADxijUs1Wgvh
3293:  ✔ disallow creation of different case pad-name via socket connection
3294:  �[32m[2026-05-17T13:23:24.571] [INFO] access - �[39m[LEAVE] pad:maliciousattempt socket:R9_-isMON1I7G2rdAAAV IP:ANONYMOUS authorID:a.5PV7ADxijUs1Wgvh
3295:  D:\a\etherpad\etherpad\src\tests\backend\specs\messages.ts
3296:  CHANGESET_REQ
3297:  �[91m[2026-05-17T13:23:24.616] [ERROR] message - �[39mThere is no author for authorId: a.etherpad-system. This is possibly related to https://github.com/ether/etherpad-lite/issues/2802
3298:  �[32m[2026-05-17T13:23:24.616] [INFO] access - �[39m[ENTER] pad:2kVcP8UgWN socket:514jMWAHwabnS0MAAAAX IP:ANONYMOUS authorID:a.a9ICDNfBEq8MX9oa
3299:  �[91m[2026-05-17T13:23:24.638] [ERROR] message - �[39mThere is no author for authorId: a.etherpad-system. This is possibly related to https://github.com/ether/etherpad-lite/issues/2802
3300:  �[32m[2026-05-17T13:23:24.638] [INFO] access - �[39m[ENTER] pad:2kVcP8UgWN socket:96eq4T5zVPdj8bQhAAAZ IP:ANONYMOUS authorID:a.qm5yFMyQeMzr2dFc
3301:  ✔ users are unable to read changesets from other pads
3302:  �[32m[2026-05-17T13:23:25.666] [INFO] access - �[39m[LEAVE] pad:2kVcP8UgWN socket:514jMWAHwabnS0MAAAAX IP:ANONYMOUS authorID:a.a9ICDNfBEq8MX9oa
3303:  �[32m[2026-05-17T13:23:25.667] [INFO] access - �[39m[LEAVE] pad:2kVcP8UgWN socket:96eq4T5zVPdj8bQhAAAZ IP:ANONYMOUS authorID:a.qm5yFMyQeMzr2dFc
3304:  �[91m[2026-05-17T13:23:25.709] [ERROR] message - �[39mThere is no author for authorId: a.etherpad-system. This is possibly related to https://github.com/ether/etherpad-lite/issues/2802
3305:  �[32m[2026-05-17T13:23:25.709] [INFO] access - �[39m[ENTER] pad:gyB1HtlBQD socket:bamhdO4zkR5peQtIAAAb IP:ANONYMOUS authorID:a.TUTfDeyit6q7cE5Y
3306:  �[91m[2026-05-17T13:23:25.740] [ERROR] message - �[39mThere is no author for authorId: a.etherpad-system. This is possibly related to https://github.com/ether/etherpad-lite/issues/2802
3307:  �[32m[2026-05-17T13:23:25.740] [INFO] access - �[39m[ENTER] pad:gyB1HtlBQD socket:0LeSESGThiaUzaVjAAAd IP:ANONYMOUS authorID:a.7u5kBkoVBfbQMvUz
3308:  �[91m[2026-05-17T13:23:26.752] [ERROR] socket.io - �[39mError handling pad message from 0LeSESGThiaUzaVjAAAd: Error: CHANGESET_REQ: rev is not a number
3309:  at Object.exports.handleMessage (D:\a\etherpad\etherpad\src\node\handler\PadMessageHandler.ts:612:11)
3310:  at async <anonymous> (D:\a\etherpad\etherpad\src\node\handler\SocketIORouter.ts:85:14)
3311:  ✔ CHANGESET_REQ: verify revNum is a number (regression)
3312:  �[32m[2026-05-17T13:23:26.753] [INFO] access - �[39m[LEAVE] pad:gyB1HtlBQD socket:bamhdO4zkR5peQtIAAAb IP:ANONYMOUS authorID:a.TUTfDeyit6q7cE5Y
3313:  �[32m[2026-05-17T13:23:26.754] [INFO] access - �[39m[LEAVE] pad:gyB1HtlBQD socket:0LeSESGThiaUzaVjAAAd IP:ANONYMOUS authorID:a.7u5kBkoVBfbQMvUz
3314:  �[91m[2026-05-17T13:23:26.799] [ERROR] message - �[39mThere is no author for authorId: a.etherpad-system. This is possibly related to https://github.com/ether/etherpad-lite/issues/2802
3315:  �[32m[2026-05-17T13:23:26.799] [INFO] access - �[39m[ENTER] pad:XLB5Wdxd3W socket:q4ne26U0fgxzZAGAAAAf IP:ANONYMOUS authorID:a.V3tjTfMcaURrPidj
3316:  �[91m[2026-05-17T13:23:26.830] [ERROR] message - �[39mThere is no author for authorId: a.etherpad-system. This is possibly related to https://github.com/ether/etherpad-lite/issues/2802
3317:  �[32m[2026-05-17T13:23:26.830] [INFO] access - �[39m[ENTER] pad:XLB5Wdxd3W socket:ZWm45sLw7M_SGM-aAAAh IP:ANONYMOUS authorID:a.tZC48krcGCCV05wE
3318:  ✔ CHANGESET_REQ: revNum is converted to number if possible (regression)
3319:  �[32m[2026-05-17T13:23:27.836] [INFO] access - �[39m[LEAVE] pad:XLB5Wdxd3W socket:q4ne26U0fgxzZAGAAAAf IP:ANONYMOUS authorID:a.V3tjTfMcaURrPidj
3320:  �[32m[2026-05-17T13:23:27.837] [INFO] access - �[39m[LEAVE] pad:XLB5Wdxd3W socket:ZWm45sLw7M_SGM-aAAAh IP:ANONYMOUS authorID:a.tZC48krcGCCV05wE
3321:  �[91m[2026-05-17T13:23:27.859] [ERROR] message - �[39mThere is no author for authorId: a.etherpad-system. This is possibly related to https://github.com/ether/etherpad-lite/issues/2802
3322:  �[32m[2026-05-17T13:23:27.859] [INFO] access - �[39m[ENTER] pad:2pLL1K9LiZ socket:CtoE6g93Vqc2Mh7jAAAj IP:ANONYMOUS authorID:a.bjvREwyq9oS7n59C
3323:  �[91m[2026-05-17T13:23:27.890] [ERROR] message - �[39mThere is no author for authorId: a.etherpad-system. This is possibly related to https://github.com/ether/etherpad-lite/issues/2802
3324:  �[32m[2026-05-17T13:23:27.890] [INFO] access - �[39m[ENTER] pad:2pLL1K9LiZ socket:A1cA0s5cN6gJIbuGAAAl IP:ANONYMOUS authorID:a.m0lWe6g9npyNfkx9
3325:  ✔ CHANGESET_REQ: revNum 2 is converted to head rev 1 (regression)
3326:  �[32m[2026-05-17T13:23:28.905] [INFO] access - �[39m[LEAVE] pad:2pLL1K9LiZ socket:CtoE6g93Vqc2Mh7jAAAj IP:ANONYMOUS authorID:a.bjvREwyq9oS7n59C
3327:  �[32m[2026-05-17T13:23:28.905] [INFO] access - �[39m[LEAVE] pad:2pLL1K9LiZ socket:A1cA0s5cN6gJIbuGAAAl IP:ANONYMOUS authorID:a.m0lWe6g9npyNfkx9
3328:  USER_CHANGES
3329:  �[91m[2026-05-17T13:23:28.928] [ERROR] message - �[39mThere is no author for authorId: a.etherpad-system. This is possibly related to https://github.com/ether/etherpad-lite/issues/2802
3330:  �[32m[2026-05-17T13:23:28.928] [INFO] access - �[39m[ENTER] pad:b6H2IX52bC socket:az--dK6jvTSFnmlEAAAn IP:ANONYMOUS authorID:a.8P5anptzBukOJmAm
3331:  �[91m[2026-05-17T13:23:28.966] [ERROR] message - �[39mThere is no author for authorId: a.etherpad-system. This is possibly related to https://github.com/ether/etherpad-lite/issues/2802
3332:  �[32m[2026-05-17T13:23:28.966] [INFO] access - �[39m[ENTER] pad:b6H2IX52bC socket:vqMeaVPHBiRkX3xtAAAp IP:ANONYMOUS authorID:a.CD5DJPuY2MStQFac
3333:  ✔ changes are applied
3334:  �[32m[2026-05-17T13:23:29.974] [INFO] access - �[39m[LEAVE] pad:b6H2IX52bC socket:az--dK6jvTSFnmlEAAAn IP:ANONYMOUS authorID:a.8P5anptzBukOJmAm
3335:  �[32m[2026-05-17T13:23:29.974] [INFO] access - �[39m[LEAVE] pad:b6H2IX52bC socket:vqMeaVPHBiRkX3xtAAAp IP:ANONYMOUS authorID:a.CD5DJPuY2MStQFac
3336:  �[91m[2026-05-17T13:23:30.008] [ERROR] message - �[39mThere is no author for authorId: a.etherpad-system. This is possibly related to https://github.com/ether/etherpad-lite/issues/2802
3337:  �[32m[2026-05-17T13:23:30.008] [INFO] access - �[39m[ENTER] pad:OQfA5EIgg7 socket:7d4eDBDqop-k4_TmAAAr IP:ANONYMOUS authorID:a.pGjP99tZauqUOKhA
3338:  �[91m[2026-05-17T13:23:30.040] [ERROR] message - �[39mThere is no author for authorId: a.etherpad-system. This is possibly related to https://github.com/ether/etherpad-lite/issues/2802
3339:  �[32m[2026-05-17T13:23:30.040] [INFO] access - �[39m[ENTER] pad:OQfA5EIgg7 socket:L6MKoAUi0a5eAvCfAAAt IP:ANONYMOUS authorID:a.Glaen1B47tTjCeTz
3340:  �[33m[2026-05-17T13:23:31.052] [WARN] message - �[39mFailed to apply USER_CHANGES from author a.pGjP99tZauqUOKhA (socket 7d4eDBDqop-k4_TmAAAr) on pad OQfA5EIgg7: Error: Not a changeset: this is not a valid changeset
3341:  at error (D:\a\etherpad\etherpad\src\static\js\Changeset.ts:64:13)
3342:  at unpack (D:\a\etherpad\etherpad\src\static\js\Changeset.ts:363:44)
3343:  at checkRep (D:\a\etherpad\etherpad\src\static\js\Changeset.ts:246:20)
3344:  at handleUserChanges (D:\a\etherpad\etherpad\src\node\handler\PadMessageHandler.ts:840:5)
3345:  ✔ bad changeset is rejected
3346:  �[32m[2026-05-17T13:23:31.053] [INFO] access - �[39m[LEAVE] pad:OQfA5EIgg7 socket:7d4eDBDqop-k4_TmAAAr IP:ANONYMOUS authorID:a.pGjP99tZauqUOKhA
3347:  �[32m[2026-05-17T13:23:31.054] [INFO] access - �[39m[LEAVE] pad:OQfA5EIgg7 socket:L6MKoAUi0a5eAvCfAAAt IP:ANONYMOUS authorID:a.Glaen1B47tTjCeTz
3348:  �[91m[2026-05-17T13:23:31.083] [ERROR] message - �[39mThere is no author for authorId: a.etherpad-system. This is possibly related to https://github.com/ether/etherpad-lite/issues/2802
3349:  �[32m[2026-05-17T13:23:31.083] [INFO] access - �[39m[ENTER] pad:tykWJpdBSY socket:ufWN61lyw_aEhzVBAAAv IP:ANONYMOUS authorID:a.Rjk1lyexiaWxI5Zf
3350:  �[91m[2026-05-17T13:23:31.105] [ERROR] message - �[39mThere is no author for authorId: a.etherpad-system. This is possibly related to https://github.com/ether/etherpad-lite/issues/2802
3351:  �[32m[2026-05-17T13:23:31.105] [INFO] access - �[39m[ENTER] pad:tykWJpdBSY socket:K2vcnE-L-JY9Pv3AAAAx IP:ANONYMOUS authorID:a.DUHVsqwg3bwPN8Ne
3352:  �[33m[2026-05-17T13:23:32.111] [WARN] message - �[39mFailed to apply USER_CHANGES from author a.Rjk1lyexiaWxI5Zf (socket ufWN61lyw_aEhzVBAAAv) on pad tykWJpdBSY: Error: Author a.Rjk1lyexiaWxI5Zf submitted an insert without an author attribute in changeset Z:1>5+5$hello
3353:  at handleUserChanges (D:\a\etherpad\etherpad\src\node\handler\PadMessageHandler.ts:887:15)
3354:  ✔ insert without author attribute is rejected
3355:  �[32m[2026-05-17T13:23:32.112] [INFO] access - �[39m[LEAVE] pad:tykWJpdBSY socket:ufWN61lyw_aEhzVBAAAv IP:ANONYMOUS authorID:a.Rjk1lyexiaWxI5Zf
3356:  �[32m[2026-05-17T13:23:32.112] [INFO] access - �[39m[LEAVE] pad:tykWJpdBSY socket:K2vcnE-L-JY9Pv3AAAAx IP:ANONYMOUS authorID:a.DUHVsqwg3bwPN8Ne
3357:  �[91m[2026-05-17T13:23:32.134] [ERROR] message - �[39mThere is no author for authorId: a.etherpad-system. This is possibly related to https://github.com/ether/etherpad-lite/issues/2802
3358:  �[32m[2026-05-17T13:23:32.134] [INFO] access - �[39m[ENTER] pad:20NkWWTLbO socket:84jJtx410pRw3MxPAAAz IP:ANONYMOUS authorID:a.MLU2fEvZwFbPRKLN
3359:  �[91m[2026-05-17T13:23:32.174] [ERROR] message - �[39mThere is no author for authorId: a.etherpad-system. This is possibly related to https://github.com/ether/etherpad-lite/issues/2802
3360:  �[32m[2026-05-17T13:23:32.174] [INFO] access - �[39m[ENTER] pad:20NkWWTLbO socket:fIywKq_LVxrQRw3sAAA1 IP:ANONYMOUS authorID:a.D1yV6QfJi5u3MrSp
3361:  �[33m[2026-05-17T13:23:33.186] [WARN] message - �[39mFailed to apply USER_CHANGES from author a.MLU2fEvZwFbPRKLN (socket 84jJtx410pRw3MxPAAAz) on pad 20NkWWTLbO: Error: Author a.MLU2fEvZwFbPRKLN tried to submit changes as author a.etherpad-system in changeset Z:1>5*0+5$hello
3362:  at handleUserChanges (D:\a\etherpad\etherpad\src\node\handler\PadMessageHandler.ts:874:17)
3363:  ✔ insert claiming the reserved system author is rejected
3364:  �[32m[2026-05-17T13:23:33.188] [INFO] access - �[39m[LEAVE] pad:20NkWWTLbO socket:84jJtx410pRw3MxPAAAz IP:ANONYMOUS authorID:a.MLU2fEvZwFbPRKLN
3365:  �[32m[2026-05-17T13:23:33.188] [INFO] access - �[39m[LEAVE] pad:20NkWWTLbO socket:fIywKq_LVxrQRw3sAAA1 IP:ANONYMOUS authorID:a.D1yV6QfJi5u3MrSp
3366:  �[91m[2026-05-17T13:23:33.208] [ERROR] message - �[39mThere is no author for authorId: a.etherpad-system. This is possibly related to https://github.com/ether/etherpad-lite/issues/2802
3367:  �[32m[2026-05-17T13:23:33.208] [INFO] access - �[39m[ENTER] pad:rAC5pI4jJI socket:jjmgOoNlK-7n4I7gAAA3 IP:ANONYMOUS authorID:a.z78YxIb6fhqLZH8P
3368:  �[91m[2026-05-17T13:23:33.233] [ERROR] message - �[39mThere is no author for authorId: a.etherpad-system. This is possibly related to https://github.com/ether/etherpad-lite/issues/2802
3369:  �[32m[2026-05-17T13:23:33.233] [INFO] access - �[39m[ENTER] pad:rAC5pI4jJI socket:rRR6H_XN17cQw0o3AAA5 IP:ANONYMOUS authorID:a.JD6rLP6YOkx1eCnN
3370:  �[33m[2026-05-17T13:23:34.240] [WARN] message - �[39mFailed to apply USER_CHANGES from author a.z78YxIb6fhqLZH8P (socket jjmgOoNlK-7n4I7gAAA3) on pad rAC5pI4jJI: Error: Rejected USER_CHANGES whose application would leave the pad without a trailing '\n' (length 7). Every USER_CHANGES must preserve the "doc ends with \n" invariant.
3371:  at handleUserChanges (D:\a\etherpad\etherpad\src\node\handler\PadMessageHandler.ts:960:13)
3372:  ✔ changeset that would strand the trailing \n is rejected
3373:  �[32m[2026-05-17T13:23:34.242] [INFO] access - �[39m[LEAVE] pad:rAC5pI4jJI socket:jjmgOoNlK-7n4I7gAAA3 IP:ANONYMOUS authorID:a.z78YxIb6fhqLZH8P
3374:  �[32m[2026-05-17T13:23:34.242] [INFO] access - �[39m[LEAVE] pad:rAC5pI4jJI socket:rRR6H_XN17cQw0o3AAA5 IP:ANONYMOUS authorID:a.JD6rLP6YOkx1eCnN
3375:  �[91m[2026-05-17T13:23:34.278] [ERROR] message - �[39mThere is no author for authorId: a.etherpad-system. This is possibly related to https://github.com/ether/etherpad-lite/issues/2802
3376:  �[32m[2026-05-17T13:23:34.278] [INFO] access - �[39m[ENTER] pad:OtTCcK32Pq socket:kqOkpeSQjIS3HMebAAA7 IP:ANONYMOUS authorID:a.URXc1F1n7DzQFnXX
3377:  �[91m[2026-05-17T13:23:34.316] [ERROR] message - �[39mThere is no author for authorId: a.etherpad-system. This is possibly related to https://github.com/ether/etherpad-lite/issues/2802
3378:  �[32m[2026-05-17T13:23:34.317] [INFO] access - �[39m[ENTER] pad:OtTCcK32Pq socket:HrO-5DFh4tqvxSZaAAA9 IP:ANONYMOUS authorID:a.Gj25PIwB5PxueV5w
3379:  ✔ retransmission is accepted, has no effect
3380:  �[32m[2026-05-17T13:23:35.341] [INFO] access - �[39m[LEAVE] pad:OtTCcK32Pq socket:kqOkpeSQjIS3HMebAAA7 IP:ANONYMOUS authorID:a.URXc1F1n7DzQFnXX
3381:  �[32m[2026-05-17T13:23:35.342] [INFO] access - �[39m[LEAVE] pad:OtTCcK32Pq socket:HrO-5DFh4tqvxSZaAAA9 IP:ANONYMOUS authorID:a.Gj25PIwB5PxueV5w
3382:  �[91m[2026-05-17T13:23:35.384] [ERROR] message - �[39mThere is no author for authorId: a.etherpad-system. This is possibly related to https://github.com/ether/etherpad-lite/issues/2802
3383:  �[32m[2026-05-17T13:23:35.384] [INFO] access - �[39m[ENTER] pad:jposQsoBfa socket:-53I6Rtvj-dQryeHAAA_ IP:ANONYMOUS authorID:a.p7TemVQz4bmkMyfP
3384:  �[91m[2026-05-17T13:23:35.431] [ERROR] message - �[39mThere is no author for authorId: a.etherpad-system. This is possibly related to https://github.com/ether/etherpad-lite/issues/2802
3385:  �[32m[2026-05-17T13:23:35.431] [INFO] access - �[39m[ENTER] pad:jposQsoBfa socket:gampeL2Q9A5du5VAAABB IP:ANONYMOUS authorID:a.DEX9X3z7EDDUuzO0
3386:  ✔ identity changeset is accepted, has no effect
3387:  �[32m[2026-05-17T13:23:36.439] [INFO] access - �[39m[LEAVE] pad:jposQsoBfa socket:-53I6Rtvj-dQryeHAAA_ IP:ANONYMOUS authorID:a.p7TemVQz4bmkMyfP
3388:  �[32m[2026-05-17T13:23:36.439] [INFO] access - �[39m[LEAVE] pad:jposQsoBfa socket:gampeL2Q9A5du5VAAABB IP:ANONYMOUS authorID:a.DEX9X3z7EDDUuzO0
3389:  �[91m[2026-05-17T13:23:36.465] [ERROR] message - �[39mThere is no author for authorId: a.etherpad-system. This is possibly related to https://github.com/ether/etherpad-lite/issues/2802
3390:  �[32m[2026-05-17T13:23:36.465] [INFO] access - �[39m[ENTER] pad:7f3BJOyQlX socket:oumYQS7JcvuhuD0iAABD IP:ANONYMOUS authorID:a.ZdKOr2LOmG0ynyoN
3391:  �[91m[2026-05-17T13:23:36.507] [ERROR] message - �[39mThere is no author for authorId: a.etherpad-system. This is possibly related to https://github.com/ether/etherpad-lite/issues/2802
3392:  �[32m[2026-05-17T13:23:36.508] [INFO] access - �[39m[ENTER] pad:7f3BJOyQlX socket:7qglswo3yrqGB0GxAABF IP:ANONYMOUS authorID:a.acPMLDiBYFszOXb0
3393:  ✔ non-identity changeset with no net change is accepted, has no effect
3394:  �[32m[2026-05-17T13:23:37.514] [INFO] access - �[39m[LEAVE] pad:7f3BJOyQlX socket:oumYQS7JcvuhuD0iAABD IP:ANONYMOUS authorID:a.ZdKOr2LOmG0ynyoN
3395:  �[32m[2026-05-17T13:23:37.514] [INFO] access - �[39m[LEAVE] pad:7f3BJOyQlX socket:7qglswo3yrqGB0GxAABF IP:ANONYMOUS authorID:a.acPMLDiBYFszOXb0
3396:  �[91m[2026-05-17T13:23:37.551] [ERROR] message - �[39mThere is no author for authorId: a.etherpad-system. This is possibly related to https://github.com/ether/etherpad-lite/issues/2802
3397:  �[32m[2026-05-17T13:23:37.551] [INFO] access - �[39m[ENTER] pad:RVPBGNwHjQ socket:0pxNoZcPsCj66CmqAABH IP:ANONYMOUS authorID:a.hD0yhyJA6sBHkCO9
3398:  �[91m[2026-05-17T13:23:37.583] [ERROR] message - �[39mThere is no author for authorId: a.etherpad-system. This is possibly related to https://github.com/ether/etherpad-lite/issues/2802
3399:  �[32m[2026-05-17T13:23:37.583] [INFO] access - �[39m[ENTER] pad:RVPBGNwHjQ socket:qDS48mK0s__GQs7IAABJ IP:ANONYMOUS authorID:a.MDbihYGWVBn03CR7
3400:  �[91m[2026-05-17T13:23:38.595] [ERROR] socket.io - �[39mError handling pad message from qDS48mK0s__GQs7IAABJ: Error: COLLABROOM: write attempt on read-only pad
3401:  at Object.exports.handleMessage (D:\a\etherpad\etherpad\src\node\handler\PadMessageHandler.ts:612:11)
3402:  at async <anonymous> (D:\a\etherpad\etherpad\src\node\handler\SocketIORouter.ts:85:14)
3403:  �[91m[2026-05-17T13:23:38.599] [ERROR] socket.io - �[39mError handling pad message from qDS48mK0s__GQs7IAABJ: Error: COLLABROOM: write attempt on read-only pad
3404:  at Object.exports.handleMessage (D:\a\etherpad\etherpad\src\node\handler\PadMessageHandler.ts:612:11)
...

3801:  �[32m[2026-05-17T13:23:44.835] [INFO] access - �[39m[CREATE] pad:pad socket:CPeWOSPXKLxsuCl1AABj IP:ANONYMOUS authorID:a.QyW6IBb1lpbWmvj3 username:user
3802:  �[32m[2026-05-17T13:23:44.837] [INFO] access - �[39m[LEAVE] pad:pad socket:CPeWOSPXKLxsuCl1AABj IP:ANONYMOUS authorID:a.QyW6IBb1lpbWmvj3 username:user
3803:  �[32m[2026-05-17T13:23:44.838] [INFO] http - �[39mSuccessful authentication from IP ANONYMOUS for user user
3804:  �[32m[2026-05-17T13:23:44.866] [INFO] access - �[39m[CREATE] pad:pad socket:0cr1EKkoBMFL3jJgAABl IP:ANONYMOUS authorID:a.MCujmtybIYnTpnlL username:user
3805:  ✔ authn user read-only /p/pad -> 200, ok (62ms)
3806:  �[32m[2026-05-17T13:23:44.868] [INFO] access - �[39m[LEAVE] pad:pad socket:0cr1EKkoBMFL3jJgAABl IP:ANONYMOUS authorID:a.MCujmtybIYnTpnlL username:user
3807:  �[32m[2026-05-17T13:23:44.870] [INFO] http - �[39mSuccessful authentication from IP ANONYMOUS for user user
3808:  �[32m[2026-05-17T13:23:44.898] [INFO] access - �[39m[CREATE] pad:pad socket:mOM0Ga-sknjWVZx1AABn IP:ANONYMOUS authorID:a.eLGqFG0A4kfBSxp8 username:user
3809:  ✔ authz user /p/pad -> 200, ok
3810:  �[32m[2026-05-17T13:23:44.900] [INFO] access - �[39m[LEAVE] pad:pad socket:mOM0Ga-sknjWVZx1AABn IP:ANONYMOUS authorID:a.eLGqFG0A4kfBSxp8 username:user
3811:  �[32m[2026-05-17T13:23:44.902] [INFO] http - �[39mSuccessful authentication from IP ANONYMOUS for user user
3812:  �[32m[2026-05-17T13:23:44.936] [INFO] access - �[39m[CREATE] pad:päd socket:GdKsHV6mM4MhrBKaAABp IP:ANONYMOUS authorID:a.Flgz7XgCOcZrki3o username:user
3813:  ✔ supports pad names with characters that must be percent-encoded
3814:  �[32m[2026-05-17T13:23:44.938] [INFO] access - �[39m[LEAVE] pad:päd socket:GdKsHV6mM4MhrBKaAABp IP:ANONYMOUS authorID:a.Flgz7XgCOcZrki3o username:user
3815:  Abnormal access attempts
3816:  �[32m[2026-05-17T13:23:44.941] [INFO] http - �[39mFailed authentication from IP ANONYMOUS
3817:  �[33m[2026-05-17T13:23:44.976] [WARN] message - �[39mclient sent author token via CLIENT_READY message; cookie migration will take effect on next HTTP response. See docs/superpowers/specs/2026-04-19-gdpr-pr3-anon-identity-design.md
3818:  �[91m[2026-05-17T13:23:44.977] [ERROR] socket.io - �[39mError handling pad message from GXU3C01Ca7-kxHUbAABr: Error: access denied
3819:  at Object.exports.handleMessage (D:\a\etherpad\etherpad\src\node\handler\PadMessageHandler.ts:508:11)
3820:  at async <anonymous> (D:\a\etherpad\etherpad\src\node\handler\SocketIORouter.ts:85:14)
3821:  ✔ authn anonymous /p/pad -> 401, error (38ms)
3822:  �[32m[2026-05-17T13:23:44.980] [INFO] http - �[39mSuccessful authentication from IP ANONYMOUS for user user
3823:  �[32m[2026-05-17T13:23:44.997] [INFO] access - �[39m[CREATE] pad:pad socket:zQl6t-O5BzHP0FT7AABt IP:ANONYMOUS authorID:a.EpH46HeQ89pHQGrD username:user
3824:  �[32m[2026-05-17T13:23:45.009] [INFO] access - �[39m[LEAVE] pad:pad socket:zQl6t-O5BzHP0FT7AABt IP:ANONYMOUS authorID:a.EpH46HeQ89pHQGrD username:user
3825:  �[32m[2026-05-17T13:23:45.010] [INFO] http - �[39mFailed authentication from IP ANONYMOUS
3826:  �[33m[2026-05-17T13:23:45.055] [WARN] message - �[39mclient sent author token via CLIENT_READY message; cookie migration will take effect on next HTTP response. See docs/superpowers/specs/2026-04-19-gdpr-pr3-anon-identity-design.md
3827:  �[91m[2026-05-17T13:23:45.056] [ERROR] socket.io - �[39mError handling pad message from xWsu1cK_fUR-jqqcAABv: Error: access denied
3828:  at Object.exports.handleMessage (D:\a\etherpad\etherpad\src\node\handler\PadMessageHandler.ts:508:11)
3829:  at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
3830:  at async <anonymous> (D:\a\etherpad\etherpad\src\node\handler\SocketIORouter.ts:85:14)
3831:  ✔ authn anonymous read-only /p/pad -> 401, error (78ms)
3832:  �[33m[2026-05-17T13:23:45.087] [WARN] message - �[39mclient sent author token via CLIENT_READY message; cookie migration will take effect on next HTTP response. See docs/superpowers/specs/2026-04-19-gdpr-pr3-anon-identity-design.md
3833:  �[91m[2026-05-17T13:23:45.087] [ERROR] socket.io - �[39mError handling pad message from cjbnL4gY-6dV_xrzAABx: Error: access denied
3834:  at Object.exports.handleMessage (D:\a\etherpad\etherpad\src\node\handler\PadMessageHandler.ts:508:11)
3835:  at async <anonymous> (D:\a\etherpad\etherpad\src\node\handler\SocketIORouter.ts:85:14)
3836:  ✔ authn !cookie -> error
3837:  �[32m[2026-05-17T13:23:45.091] [INFO] http - �[39mSuccessful authentication from IP ANONYMOUS for user user
3838:  �[91m[2026-05-17T13:23:45.119] [ERROR] socket.io - �[39mError handling pad message from qQu7wH95-srDBddIAABz: Error: access denied
3839:  at Object.exports.handleMessage (D:\a\etherpad\etherpad\src\node\handler\PadMessageHandler.ts:508:11)
3840:  at async <anonymous> (D:\a\etherpad\etherpad\src\node\handler\SocketIORouter.ts:85:14)
3841:  ✔ authorization bypass attempt -> error
3842:  Authorization levels via authorize hook
3843:  �[32m[2026-05-17T13:23:45.122] [INFO] http - �[39mSuccessful authentication from IP ANONYMOUS for user user
3844:  �[32m[2026-05-17T13:23:45.151] [INFO] access - �[39m[CREATE] pad:pad socket:d9vANLDrDcELQa8WAAB1 IP:ANONYMOUS authorID:a.RzGYAXno2V2Exs85 username:user
3845:  ✔ level='create' -> can create
3846:  �[32m[2026-05-17T13:23:45.153] [INFO] access - �[39m[LEAVE] pad:pad socket:d9vANLDrDcELQa8WAAB1 IP:ANONYMOUS authorID:a.RzGYAXno2V2Exs85 username:user
3847:  �[32m[2026-05-17T13:23:45.156] [INFO] http - �[39mSuccessful authentication from IP ANONYMOUS for user user
3848:  �[32m[2026-05-17T13:23:45.175] [INFO] access - �[39m[CREATE] pad:pad socket:Q8myrNl1lyWnWUEpAAB3 IP:ANONYMOUS authorID:a.cfhcDRo7Wtdo2Oxn username:user
3849:  ✔ level=true -> can create
3850:  �[32m[2026-05-17T13:23:45.183] [INFO] access - �[39m[LEAVE] pad:pad socket:Q8myrNl1lyWnWUEpAAB3 IP:ANONYMOUS authorID:a.cfhcDRo7Wtdo2Oxn username:user
3851:  �[32m[2026-05-17T13:23:45.186] [INFO] http - �[39mSuccessful authentication from IP ANONYMOUS for user user
3852:  �[91m[2026-05-17T13:23:45.215] [ERROR] message - �[39mThere is no author for authorId: a.etherpad-system. This is possibly related to https://github.com/ether/etherpad-lite/issues/2802
3853:  �[32m[2026-05-17T13:23:45.215] [INFO] access - �[39m[CREATE] pad:pad socket:XZ1METoobpUxCKHLAAB5 IP:ANONYMOUS authorID:a.BRWCeK2xANkR65M8 username:user
3854:  ✔ level='modify' -> can modify
3855:  �[32m[2026-05-17T13:23:45.217] [INFO] access - �[39m[LEAVE] pad:pad socket:XZ1METoobpUxCKHLAAB5 IP:ANONYMOUS authorID:a.BRWCeK2xANkR65M8 username:user
3856:  �[32m[2026-05-17T13:23:45.219] [INFO] http - �[39mSuccessful authentication from IP ANONYMOUS for user user
3857:  �[91m[2026-05-17T13:23:45.253] [ERROR] socket.io - �[39mError handling pad message from 2K3S7HG1kI_LTocjAAB7: Error: access denied
3858:  at Object.exports.handleMessage (D:\a\etherpad\etherpad\src\node\handler\PadMessageHandler.ts:508:11)
3859:  at async <anonymous> (D:\a\etherpad\etherpad\src\node\handler\SocketIORouter.ts:85:14)
3860:  ✔ level='create' settings.editOnly=true -> unable to create (43ms)
3861:  �[32m[2026-05-17T13:23:45.264] [INFO] http - �[39mSuccessful authentication from IP ANONYMOUS for user user
3862:  �[91m[2026-05-17T13:23:45.293] [ERROR] socket.io - �[39mError handling pad message from -favDyQIPjBRPdSaAAB9: Error: access denied
3863:  at Object.exports.handleMessage (D:\a\etherpad\etherpad\src\node\handler\PadMessageHandler.ts:508:11)
3864:  at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
3865:  at async <anonymous> (D:\a\etherpad\etherpad\src\node\handler\SocketIORouter.ts:85:14)
3866:  ✔ level='modify' settings.editOnly=false -> unable to create
3867:  �[32m[2026-05-17T13:23:45.296] [INFO] http - �[39mSuccessful authentication from IP ANONYMOUS for user user
3868:  �[91m[2026-05-17T13:23:45.326] [ERROR] socket.io - �[39mError handling pad message from 82__lQ5zfamy3MvZAAB_: Error: access denied
3869:  at Object.exports.handleMessage (D:\a\etherpad\etherpad\src\node\handler\PadMessageHandler.ts:508:11)
3870:  at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
3871:  at async <anonymous> (D:\a\etherpad\etherpad\src\node\handler\SocketIORouter.ts:85:14)
3872:  ✔ level='readOnly' -> unable to create
3873:  �[32m[2026-05-17T13:23:45.329] [INFO] http - �[39mSuccessful authentication from IP ANONYMOUS for user user
3874:  �[91m[2026-05-17T13:23:45.357] [ERROR] message - �[39mThere is no author for authorId: a.etherpad-system. This is possibly related to https://github.com/ether/etherpad-lite/issues/2802
3875:  �[32m[2026-05-17T13:23:45.357] [INFO] access - �[39m[CREATE] pad:pad socket:vw-m0IO5nh1WXm_1AACB IP:ANONYMOUS authorID:a.fNLrXkUxPQXBpFD0 username:user
3876:  ✔ level='readOnly' -> unable to modify
3877:  �[32m[2026-05-17T13:23:45.359] [INFO] access - �[39m[LEAVE] pad:pad socket:vw-m0IO5nh1WXm_1AACB IP:ANONYMOUS authorID:a.fNLrXkUxPQXBpFD0 username:user
3878:  Authorization levels via user settings
3879:  �[32m[2026-05-17T13:23:45.363] [INFO] http - �[39mSuccessful authentication from IP ANONYMOUS fo...

@JohnMcLear JohnMcLear merged commit b195b13 into develop May 17, 2026
29 of 31 checks passed
@JohnMcLear JohnMcLear deleted the fix/skip-anonymize-socket-with-ep-hash-auth branch May 17, 2026 13:26
JohnMcLear added a commit that referenced this pull request May 18, 2026
…7808)

ep_readonly_guest is archived (read-only on GitHub) and its
authenticate hook unconditionally swaps req.session.user with a
read-only guest, even when the request carries 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 (#7795). The pre-fix theory blamed ep_hash_auth.handleMessage;
the actual hook trace is a red herring — handleMessage only fires on
the /pad namespace and never on /settings.

ep_guest is the maintained successor (same authors, same purpose).
1.0.72 on npm already includes the "defer to basic auth / admin
paths" fix backported to ep_readonly_guest by intent here. 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 ep_hash_auth-style hashed-only plugin).
Reattribute its comment so future readers don't chase ep_hash_auth.

Closes #7795.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant