Repository navigation
Add CI, wire-protocol fuzz suite, and prototype-key hardening - #2
Merged
Merged
Conversation
The repo had no CI. Add a GitHub Actions workflow (Node 22 + 24) that syntax-checks every shipped module with `node --check` and runs `npm test`. A new wire-level fuzz suite (test/wire-fuzz.test.mjs) feeds every client message type, every field, and every registered verb junk values in every session state, asserting that nothing throws or surfaces as a SESSION_ERROR. It found one real crash and one message leak: - Typing `__proto__` (or any Object.prototype key) as a verb resolved through `p.aliases[first]` / `DIR_ALIASES[cmd]` and threw `cmd.replace is not a function`, which reached the player as "Something went wrong" and a server-log stack trace. Lookups now use own-key checks. - `raceById` / `guildById` / `itemById` / `creatureById` / `skillById` returned Object.prototype members for `constructor`-style ids, so a `charcreate` with race `constructor` leaked a TypeError message instead of "Unknown race". The lookups now require an own string key. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Two seed-42 Circle 2 runs take ~29s on a GitHub-hosted Node 22 runner, so
the 30s outer timeout SIGKILLed the engine mid-run ("Engine exited before
response" + EPIPE). The test also fails this way on main when run on that
runner; it was only exposed now because this PR adds CI. Per-request 8s
timeouts are unchanged, so a hung engine still fails fast.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Owner
Author
|
Automated PR audit (2026-10-21): |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The repo had no CI and no test that exercises the WebSocket protocol with hostile input. This adds both, and fixes the two bugs the new fuzz suite surfaced.
What changed
.github/workflows/ci.yml): Node 22 and 24 matrix;node --checkover every shipped module (server, data, client JS, scripts, tests — there is no bundler, so a parse error is a runtime error), thennpm test.test/wire-fuzz.test.mjs): every wire message type × every field × junk values (wrong types, prototype keys, 100 KB strings, NUL, lone surrogates) in login / charselect / chargen / playing states, plus all 183 registered verbs × junk arguments. Contract:route()never throws and never emits aSESSION_ERROR.__proto__as a verb resolved throughp.aliases[first]/DIR_ALIASES[cmd]and threwcmd.replace is not a function→ "Something went wrong" to the player and a stack trace in the server log. Alias and direction lookups now use own-key checks (server/commands/index.js,world.js,player.js).raceById/guildById/itemById/creatureById/skillByIdreturnedObject.prototypemembers for ids likeconstructor;charcreatewith raceconstructorleaked a TypeError message instead of "Unknown race". All five now require an own string key (data/*.js,server/commands/join.js).How verified
npm test→ 959 tests, 956 pass. The 2 failures aretest/puffer-circle.test.mjs("seed 42 scripted rotation earns Circle 2 within 512 activities") hitting its 30 s timeout under CPU load; it fails identically on unmodifiedmainon the same machine and passes when the box is idle — pre-existing flake, not touched here.node --test test/wire-fuzz.test.mjs→ 7/7 pass (fails onmainwith thecmd.replaceTypeError).Risks / follow-ups
*ByIdhelpers now returnnullfor non-string ids. All callers pass strings today (full suite green), but any future caller passing a number will getnullrather than a hit.puffer-circlesubprocess test deserves a longer timeout or a smaller activity budget so it is stable on loaded CI runners.🤖 Generated with Claude Code