fix: read block.padding and sanitizeText config dynamically instead of at import time#7734
fix: read block.padding and sanitizeText config dynamically instead of at import time#7734OfirHaf wants to merge 8 commits into
Conversation
🦋 Changeset detectedLatest commit: 35feaf6 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
✅ Deploy Preview for mermaid-js ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
@mermaid-js/examples
mermaid
@mermaid-js/layout-elk
@mermaid-js/layout-tidy-tree
@mermaid-js/mermaid-zenuml
@mermaid-js/parser
@mermaid-js/tiny
commit: |
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## develop #7734 +/- ##
==========================================
- Coverage 3.27% 3.27% -0.01%
==========================================
Files 600 600
Lines 60657 60660 +3
Branches 916 916
==========================================
Hits 1985 1985
- Misses 58672 58675 +3
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
|
The latest updates on your projects. Learn more about Argos notifications ↗︎
|
knsv-bot
left a comment
There was a problem hiding this comment.
[sisyphus-bot]
Thanks for picking this up @OfirHaf — closing out three stale-config bugs in one focused PR is a really nice batch. The diagnosis is on the money and the fix is exactly the right shape. A few observations below, only one of which is worth acting on before merge.
What's working well
- 🎉 [praise] This addresses the long-standing TODO at
block/layout.ts:5— that comment had been sitting there flagging the exact bug. Good to see it finally cleared. - 🎉 [praise] Bundling #7621 / #7622 / #7623 together is the right call. They share a single root cause (module-scope config snapshots), so reviewing and landing them as one cohesive change is much cleaner than three separate PRs.
- 🎉 [praise] The threading of
paddingthroughsetBlockSizesandlayoutBlocks(including their recursive calls) preserves the existing default of8exactly, so behavior is unchanged for users who don't override the config — only those who do override it see the fix. That's the ideal shape for this kind of fix. - 🎉 [praise] Security side-effect: the
blockDB.tsandquadrantDb.tschanges meandompurifyConfigoverrides (e.g.,FORBID_TAGS) now actually apply to those diagrams' label sanitization at runtime. That was a quiet sanitization bypass before, so this strengthens defense-in-depth meaningfully.
Things to address
-
🟡 [important] Missing regression tests. The PR description says "Existing unit tests continue to pass" — but those tests passed before this fix too, which means they don't actually exercise the bug. Without a regression test, it would be easy for someone to accidentally reintroduce a module-scope
const config = getConfig()in a future refactor and not notice. All three modified files already have spec files (blockDB.spec.ts,layout.spec.ts,quadrantDb.spec.ts), so adding test cases is low-friction. Something like:- In
layout.spec.ts: set a customblock.paddingviasetSiteConfig/initializeafter the module is loaded, runlayout()on a small block tree, and assert the spacing reflects the new padding. - In
blockDB.spec.tsandquadrantDb.spec.ts: setdompurifyConfig: { FORBID_TAGS: ['b'] }(matching the issue repro snippets), feed in a<b>Bold</b>label, and assert the sanitized output reflects the runtime config.
These would directly mirror the repro steps in the linked issues, which is the cleanest way to lock the fix in place.
- In
Nits and suggestions
- 🟢 [nit] The default value
8for padding is now duplicated in three spots:setBlockSizessignature,layoutBlockssignature, and thegetConfig()?.block?.padding ?? 8call insidelayout(). Not a problem today, but aconst DEFAULT_BLOCK_PADDING = 8at the top of the file would make future updates one-place-only. Totally optional. - 💡 [suggestion] Heads up that
block/renderHelpers.ts:114has a related-but-different pattern:padding ?? getConfig()?.block?.padding ?? 0. It already reads config dynamically (good), but its fallback is0whilelayout.tsfalls back to8. That inconsistency is pre-existing and out of scope for this PR — flagging only so it doesn't get lost. Could be worth a follow-up.
Self-check
| Severity | Count |
|---|---|
| 🔴 blocking | 0 |
| 🟡 important | 1 |
| 🟢 nit | 1 |
| 💡 suggestion | 1 |
| 🎉 praise | 4 |
Verdict: COMMENT — the fix is correct and well-targeted; the only meaningful ask is regression tests so the bug can't quietly come back. Let's get this across the finish line. 🙌
|
Added regression tests to all three files as requested:
All 11 tests pass. Let me know if you'd prefer a different style or more granular coverage. |
|
Hi @OfirHaf, |
|
Thanks for the heads up! The CI was failing because I used |
|
Apologies, missed two more |
…odule load block/layout.ts captured padding via getConfig() at import time, so changes to block.padding config had no effect during rendering. Move the read into layout() and thread it into setBlockSizes/layoutBlocks as a parameter. blockDB.ts and quadrantDb.ts had the same problem with sanitization config: both captured config at module scope, so dompurifyConfig updates applied after import were silently ignored. Fixes mermaid-js#7621, mermaid-js#7622, mermaid-js#7623
…adrant diagrams Verify that getConfig() is invoked during function execution (not cached at module load time) for blockDB sanitizeText, quadrantDb textSanitizer, and layout() block.padding reads.
8688086 to
3d0011b
Compare
|
Done, rebased onto develop just now. Thanks for guiding this through! |
ashishjain0512
left a comment
There was a problem hiding this comment.
Thanks for picking this up @OfirHaf — and especially for going after the dangling TODO at layout.ts:5. This is a small change with surprisingly broad reach (any user who reconfigured block.padding or dompurifyConfig after the initial import was being silently ignored), and it's good to see it land.
What's working well
🎉 Cohesive root-cause fix. All three diagrams suffered from the same module-load-time snapshot anti-pattern; addressing them together rather than one-off is exactly the right call.
🎉 layout.spec.ts:21-45 is the kind of test I want to see. It mocks getConfig to return different padding values, calls layout() twice, and asserts the output width changes (212 vs 260 — math checks out). That's a behavior assertion, not a call-count assertion, and it catches the bug at its real consequence.
🎉 Security-positive side-effect. Inlining getConfig() into the sanitizeText helpers means any post-initialize() dompurifyConfig or securityLevel override now actually applies. Previously those overrides were silently ignored — a real (if quiet) hardening for downstream embedders.
🎉 Changeset is present, correctly typed patch, and the prefix is fix:. Resolves #7621 — the linked issue is captured in the description; would be lovely to also add Resolves #7621 at the bottom of the PR body so GitHub auto-closes the issue on squash-merge.
Things to consider (non-blocking)
🟢 [nit] setBlockSizes and layoutBlocks have padding = 8 as their function-level default (layout.ts:75, 209). Since the only caller, layout() at line 357, always reads the value from config and passes it down, those defaults are now dead code. Either drop them (and require padding as a positional arg, which removes the chance of an internal caller forgetting it) or add a one-line comment that they're a safety net. Tiny — pick whichever you prefer.
🟢 [nit] The other two specs could be a touch stronger. blockDB.spec.ts:38-47 and quadrantDb.spec.ts:59-68 both assert spy.toHaveBeenCalled(), which would also pass for a "fix" that called getConfig() once and cached it across calls. Following layout.spec.ts's pattern — set two different mocked configs, invoke the sanitizer twice, assert the output differs — would lock in the regression more tightly. Not blocking; the current tests do distinguish "fixed" from the original module-load-time bug.
💡 [suggestion] Other diagrams. A quick grep for ^const config = getConfig\(\) across packages/mermaid/src/diagrams/ only surfaces these two (block, quadrant-chart), so there's nothing else to chase with this exact shape. But broader patterns like getConfig()?.X?.Y evaluated at module scope might exist elsewhere — happy follow-up if you (or anyone) wants to sweep, definitely out of scope for this PR.
Verification I ran
- Read all six source/test diffs end-to-end (the changeset too).
- Ran a focused XSS audit on the changed code paths:
common.sanitizeTextstill always invokes DOMPurify regardless of config shape;paddingis only used in arithmetic andlog.debugcalls (no DOM-sink reach); no memoization or new timing windows reintroduce the captured-snapshot bug. - Confirmed via grep that no other diagram in the tree shares this exact
const config = getConfig()module-scope pattern.
Severity tally: 🔴 0 / 🟡 0 / 🟢 2 / 💡 1 / 🎉 4
Approving — let's get this across the finish line. Thanks again!
|
Hey @pbrolin47 — just checking in. The branch is rebased on develop and all CI is green. Is there anything else needed from my side before this can be merged? |
|
Hi @OfirHaf, I think the PR is good to go, but I called out for some help of other maintainers to get it merged |
What
Three diagrams were capturing config at module scope, so any config updates applied after
import/initialize()were silently ignored:block/layout.ts—paddingwas computed once at the top of the file. The existing TODO comment (// TODO: This means the number we provide in diagram's config will never be used. Should fix.) called this out but it was never addressed.block/blockDB.ts—configsnapshot was used forsanitizeText, sodompurifyConfigchanges had no effect on block label sanitization.quadrant-chart/quadrantDb.ts— same stale-config pattern intextSanitizer.How
layout.ts: Removed the module-levelconst padding. Instead,layout()readsgetConfig()?.block?.padding ?? 8and passes the value intosetBlockSizesandlayoutBlocks(and their recursive calls) via a newpaddingparameter with the same default.blockDB.ts: Replacedcommon.sanitizeText(txt, config)withcommon.sanitizeText(txt, getConfig())so every sanitization call reflects the live config.quadrantDb.ts: Same — removed the module-scopeconfigvariable and callgetConfig()directly insidetextSanitizer.Testing
Existing unit tests for
layout.spec.ts,blockDB.spec.ts, andquadrantDb.spec.tscontinue to pass. TypeScript compilation is clean.Closes #7621
Closes #7622
Closes #7623