Skip to content

fix(frontend): sanitize markdown HTML rendering in Text widget#52

Open
Micsi wants to merge 1 commit into
mainfrom
codex/fix-stored-xss-in-text-widget
Open

fix(frontend): sanitize markdown HTML rendering in Text widget#52
Micsi wants to merge 1 commit into
mainfrom
codex/fix-stored-xss-in-text-widget

Conversation

@Micsi
Copy link
Copy Markdown
Owner

@Micsi Micsi commented May 18, 2026

Upstream Tracking

Motivation

  • The Text widget parsed stored Markdown with marked.parse() and injected the result via v-html, which allowed stored XSS when Markdown contains raw HTML and event handlers.
  • Because page configs are persisted and viewers import the widget and JWTs are kept in localStorage, an attacker who can save a page could execute scripts in other users' sessions.

Description

  • Added an escapeHtml() helper and registered a marked renderer html hook to escape raw HTML tokens before they are returned by the parser in frontend/src/widgets/Text/Widget.vue.
  • Kept the existing Markdown parsing and rendering flow (marked.parse + v-html) to preserve formatting while neutralizing embedded HTML payloads.
  • The change is minimal and targeted to the markdown-to-HTML boundary so widget behavior for normal Markdown remains unchanged.

Testing

  • Ran the frontend type checker with cd frontend && npm run typecheck, which completed successfully.

Codex Task

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f9c16d1a90

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +16 to +20
marked.use({
renderer: {
html({ text }) {
return escapeHtml(text)
},
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Register marked renderer only once

Because this is a <script setup> component, marked.use(...) executes every time a Text widget instance is created, but marked is a shared singleton. That means each mount appends another renderer override to global state; on dashboards with many widgets (or repeated mount/unmount cycles), this accumulates unnecessary wrapper chains and can eventually trigger the recursion/performance failure mode documented by Marked for repeated use() calls in component code. Move this to a one-time module initialization (outside per-instance setup) or use an isolated Marked instance.

Useful? React with 👍 / 👎.

@Micsi Micsi added the promoted upstream Fork PR has an associated upstream PR label May 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

aardvark codex promoted upstream Fork PR has an associated upstream PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant