Security fix: keep GM-only page text out of players' journal view - #141
Merged
Merged
Conversation
Sync reads Chronicle with the owner's key, so page text arrives with its GM-only parts. They were written into Foundry journal pages as ordinary text that players could read. Pulls now put GM-only text and pictures into Foundry secret blocks, and pushes turn everything inside a secret block back into GM-only content, so an edit in Foundry keeps it hidden in Chronicle too. Headings that start GM-only no longer become page names. Field values (including GM-only fields) are no longer stored in journal flags, which every client receives; nothing read them. Fixes keyxmakerx/Cordinator#217 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Abw8XBoqPMgbdjkmkuhCN2
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Abw8XBoqPMgbdjkmkuhCN2
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.
Requested by Key Maker · project thread
Fixes keyxmakerx/Cordinator#217
Security implication: security fix. GM-only text and pictures in Chronicle pages no longer show as ordinary text in Foundry journals, and GM-only field values are no longer stored in journal flags every client receives.
Consumer-verified: Chronicle marks GM-only text as
<span data-secret>(internal/sanitize/sanitize.go:70, stripped for players byStripSecretsHTMLat:124, regex at:114); the sync API leaves it in for Owner/Scribe keys (internal/plugins/syncapi/egress_sanitize.go:107); a pushedentryis stored as HTML through the same sanitizer (internal/plugins/entities/service.go:674-684).Foundry compatibility: not run in a live Foundry. Uses Foundry's own
<section class="secret">markup (v12 to v14). Live check to be filed as a needs-operator issue.Mockup: n/a (players stop seeing text they shouldn't; no new UI)
What this changes
Before: Sync reads Chronicle with the owner's key, so a page's GM-only text reached Foundry and was written into the journal page as ordinary text, readable by every player who can see the journal.
After: GM-only text and GM-only pictures go into Foundry's own GM secret blocks, which Foundry hides from players who don't own the page. Edits made in Foundry keep it GM-only: anything inside a secret block, including one the GM made, goes back to Chronicle as GM-only content.
How: a new pure helper,
scripts/_gm-secrets.mjs.data-chronicle-partnumber, and spaces at the split edges become no-break spaces so Foundry's editor keeps them._collectTextPages,_collectPlayerNotes) every text run inside a secret block is wrapped in its own<span data-secret>, never a span inside a span, because Chronicle's stripper ends a secret at the first</span>. Pictures inside a secret block are markedce-img--gm, and split paragraphs are joined again._splitByHeadingsignores headings inside secret blocks, and a heading that starts with GM-only text keeps that start on its own page. A secret never becomes a page name, and no extra "Overview" heading is pushed.It also stops writing
flags.chronicle-sync.fields, the entity's field values including GM-only ones, and removes the copy older versions stored. Nothing read that flag.Why
Chronicle hides GM-only text from everyone below Scribe. The module has to keep it hidden on the Foundry side too, and keep it marked through a round trip, because a plain strip on pull would delete the secrets in Chronicle on the GM's next Foundry edit.
Known limits, recorded in the private issue rather than here:
Order with #138 (pictures): on push that PR's picture pass should run first and this one second. Whichever merges second has a small conflict at the same call sites.
Test plan
node --test tools/test-*.mjs: 1070 pass, 0 fail. The newtools/test-gm-secrets.mjs(33 tests) covers what players can read after a pull, round trips with no change to Chronicle, secret blocks the GM made in Foundry, a stray</span>, pictures, the heading/page-name cases, and the wiring on every pull and push path.CHRONICLE_DIR=../Chronicle bench/run.sh journals: 13 pass, 0 fail, against Chronicle main. The new scenariogm-secretschecks that GM-only text arrives only inside a secret block, that a GM edit in Foundry keeps thedata-secretspan in Chronicle, that a later Chronicle edit still arrives hidden, and that no field values are stored on the journal.Tenet self-check
.ai.mdfile index and the CLAUDE.md conventions line updatedGenerated by Claude Code