Skip to content

fix(#184): escape the overlay's plain-text fallback - #185

Merged
BearToCode merged 4 commits into
BearToCode:masterfrom
itsmunzir:fix-184-overlay-escape
Oct 5, 2026
Merged

BearToCode merged 4 commits into
BearToCode:masterfrom
itsmunzir:fix-184-overlay-escape

Conversation

@itsmunzir

Copy link
Copy Markdown
Contributor

Fixes #184

speculativeHighlightUpdate returns the editor's text unchanged when the overlay has no .line spans. Two ordinary paths reach it: the overlay is empty until the highlighter's first pass, and a sanitizer that strips Shiki's <span class="line"> wrappers leaves it that way permanently. The caller renders the return value with {@html displayedOverlay.html} and never runs the configured sanitizer over this path, so markup typed into the editor was parsed and executed inside the overlay (the report's <img onerror> repro).

The fallback now escapes &, < and >. With no highlighted lines to patch, the overlay shows plain text either way, so the escape changes how the text is parsed, not what it says.

Test

The repo has no test runner, so this ships a dependency-free node:test check that runs the reported repro against the built package:

pnpm --filter carta-md build
node --test packages/carta-md/test/speculative.test.mjs

The old code fails it (the returned string still contains <img); the new code passes. Happy to move it under a runner if you add one later.

`speculativeHighlightUpdate` returns the editor's text unchanged when the overlayhas no `.line` spans (before the highlighter's first pass, or when asanitizer strips those spans). The caller parses that return value as HTML andnever runs the configured sanitizer over it, so markup typed into the editorexecuted inside the overlay.

The fallback now escapes `&`, `<` and `>`, with a regression test that runsthe reported repro: the old code fails it, the new code passes.
@BearToCode

Copy link
Copy Markdown
Owner

Thanks for your contribution. However, I think it's a better idea to just update the following function by calling the sanitizer specified as a carta option:

/**
* Returns the highlighted text using a speculative update.
* @param text The text to highlight.
*/
function speculativeHighlight(value: string) {
const timestamp = new Date().getTime();
if (!mounted) return { html: '', timestamp };
const currentOverlay = highlightElem.innerHTML;
if (highlightElem) {
try {
const html = speculativeHighlightUpdate(currentlyHighlightedValue, value, currentOverlay);
currentlyHighlightedValue = value;
return { html, timestamp };
} catch (e) {
console.error(`Error executing speculative update: ${e}.`);
// The overlay still holds the previous text, but the debounced highlight is already
// on its way and will render `value`. Keep the baseline in sync with it, otherwise it
// stays at the pre-change text and the next patch sees the whole document as added,
// replaying it into the overlay (the text is duplicated until the debounce lands).
currentlyHighlightedValue = value;
}
}
return {
html: highlightElem.innerHTML,
timestamp
};
}

Removed allowBuilds section from pnpm workspace configuration.
Add early return if no lines are found in the tree.
@itsmunzir

Copy link
Copy Markdown
Contributor Author

Thanks - I see you implemented it on the branch. The speculative path now runs the configured carta.sanitizer, the manual escape is dropped, and the early return is back, which matches the rework I was about to send, so I dropped my duplicate commit. Nothing else from me on this one; ready when you are.

@BearToCode
BearToCode merged commit cdf937b into BearToCode:master Oct 5, 2026
1 check passed
@github-actions

github-actions Bot commented Oct 5, 2026

Copy link
Copy Markdown

🎉 This PR is included in version carta-md-v4.11.4 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Report a vulnerability : Editor overlay renders raw input as HTML when the highlight overlay has no .line spans

2 participants