CVE Patch Review

GHSA-97R8-RF7Q-WMJW: Root-Cause Fix for Stored XSS in Sveltia CMS Summary Sanitization

GHSA-97R8-RF7Q-WMJW · Updated 2026-05-18 Root-cause

Summary

The patch addresses a stored XSS condition in Sveltia CMS caused by decoding HTML entities after sanitization in the entry summary path. The change reorders entity decoding relative to sanitization based on the Markdown mode and adds regression tests for entity-encoded tags and event handlers. Based on the provided diff and tests, this is a root-cause fix for the reported vulnerability in the summary rendering flow.

Analysis

Vulnerability

GHSA-97R8-RF7Q-WMJW describes a stored cross-site scripting issue in Sveltia CMS caused by a sanitize-then-decode flaw in entry summary generation. The vulnerable behavior shown in the commit diff decodes HTML entities with parseEntities(str) in a way that allowed attacker-controlled entity-encoded markup to survive sanitization and later become active HTML when administrators viewed entry summaries. This is a classic order-of-operations bug: sanitization was applied to encoded text, then decoding reintroduced dangerous markup such as tags and inline event handlers.

The impact is consistent with stored XSS in an administrative rendering context. An attacker can persist payloads like entity-encoded <img ... onerror=...> or encoded attributes on otherwise allowed tags, bypass filtering, and trigger script execution when the summary is rendered. The advisory and commit are aligned on this root cause and affected path.

Relevant sources: GitHub Security Advisory, fix commit, third-party report.

// vulnerable behavior from summary.js snippet
str = parseEntities(str);

Patch

The patch in commit 43a6ac5d0182a503400d8ce1ac156e08f537b1b2 changes entity decoding behavior in src/lib/services/contents/entry/summary.js so that decoding occurs conditionally around the Markdown/non-Markdown flow instead of as a single unconditional step. The important security effect, as evidenced by the new tests, is that entity-encoded HTML is decoded before the relevant sanitization logic in the Markdown-enabled path, allowing the sanitizer to inspect the real markup rather than inert-looking encoded text.

The added regression tests in src/lib/services/contents/entry/summary.test.js are security-relevant and directly target the exploit primitive:

  • Entity-encoded dangerous tags are stripped from Markdown summaries.
  • Entity-encoded event handlers on allowed tags are removed.
  • Template-driven summary generation with allowMarkdown: true returns sanitized HTML, e.g. <strong>Safe</strong> instead of preserving attacker-controlled attributes or disallowed tags.
// patched tests from summary.test.js
const input = '&lt;strong onmouseover=&quot;alert(1)&quot;&gt;Safe&lt;/strong&gt;';
const result = sanitizeEntrySummary(input, { allowMarkdown: true });

expect(result).toBe('<strong>Safe</strong>');

Although the snippet alone does not show the full surrounding sanitizer pipeline, the tests demonstrate the intended invariant: after patching, encoded HTML no longer bypasses sanitization in Markdown summary rendering.

Review

Pros

  • The fix targets the actual vulnerability class: incorrect decode/sanitize ordering. That is the core issue identified by the advisory.
  • The regression coverage is strong for the reported exploit path. Tests explicitly cover encoded disallowed tags, encoded event-handler attributes, and the higher-level getEntrySummary template flow.
  • The expected outputs are security-meaningful, not merely non-crashing behavior. The tests verify both stripping of dangerous elements and preservation of safe formatting tags.
  • The patch appears narrowly scoped to the summary rendering path implicated by the advisory, reducing risk of unrelated behavior changes.

Cons

  • The diff excerpt is minimal and does not expose the full control flow, so reviewers should still inspect whether all summary entry points consistently pass through the corrected decode-before-sanitize sequence.
  • The conditional structure shown in the snippet is slightly opaque in isolation because both branches call parseEntities(str); correctness depends on where sanitization occurs relative to each branch in the full function.
  • The tests focus on Markdown-enabled summaries. If there are other rendering modes or downstream consumers of summary output, they should be audited for similar decode-after-sanitize patterns.

Verdict

Root-cause.

Based on the provided commit and tests, the patch fixes the underlying sanitize-then-decode design flaw rather than merely blocking one payload shape. The new assertions show that entity-encoded HTML is normalized early enough for sanitization to remove dangerous tags and attributes before rendering. For this specific summary-rendering vulnerability in Sveltia CMS, the remediation is technically sound and source-supported.

Sources