Repository navigation
chore(deps): update dependency sanitize-html to v2.18.0 [security] - #193
Merged
Merged
Conversation
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.
This PR contains the following updates:
2.17.7→2.18.0ApostropheCMS sanitize-html: SMIL animation namespace-prefix bypass allows javascript: URI injection (XSS)
CVE-2026-113657 / GHSA-374f-7chj-9948
More information
Details
Summary
sanitize-html 2.17.7 introduced
animatesUrlAttribute()to block SVG animation elements (animate,animateColor,animateMotion,animateTransform,set) when theirattributeNametargets a URL sink likehref. However, this check is bypassed when the animation element uses a namespace prefix (e.g.,svg:animateinstead ofanimate).Root Cause
The
animatesUrlAttributefunction (index.js, line ~832) checkssvgAnimationTags.indexOf(name.toLowerCase()), wheresvgAnimationTagscontains bare local names:["animate", "animatecolor", "animatemotion", "animatetransform", "set"]. htmlparser2 passes namespace-prefixed tag names verbatim, sosvg:animatedoes not matchanimatein the array, causing the function to returnfalseand skip the SMIL XSS protection.Ironically, the function already handles namespace prefixes correctly for the
attributeNametarget value (line ~843):const localName = target.slice(target.lastIndexOf(":") + 1)— but this same stripping was not applied to the tag name itself.Proof of Concept
All 5 SMIL animation elements are bypassable with any namespace prefix (
svg:,xlink:,xml:, uppercase variants). Tested and confirmed on sanitize-html 2.17.7 from the apostrophe monorepo.Impact
Cross-site scripting (XSS) via SMIL animation retargeting. When an application uses
allowedTags: false(common in rich-text editors that want to preserve SVG), an attacker can inject<svg:animate attributeName="href" values="#safe;javascript:..." .../>to override thehrefattribute of a parent<a>element after the page loads, causing the link to execute JavaScript when clicked.Suggested Fix
Strip the namespace prefix from the tag name before the lookup, matching the pattern already used for the attribute name target:
Affected Versions
sanitize-html >= 2.17.7 (where the SMIL animation protection was introduced). Earlier versions lack the SMIL protection entirely.
Credit
Kai Aizen (SnailSploit) — Adversarial AI & Security Research
Severity
CVSS:3.1/AV:N/AC:H/PR:L/UI:R/S:U/C:L/I:L/A:NReferences
This data is provided by the GitHub Advisory Database (CC-BY 4.0).
Release Notes
apostrophecms/apostrophe (sanitize-html)
v2.18.0Compare Source
Adds
loggeroption: pass any console-shaped object, withdebug,info,warnanderrormethods, and sanitize-html's own diagnostics are delivered to it rather than to the console, so an application with a logging pipeline of its own can route them. Missing methods, and no option at all, fall back to the console. Those messages also lost their decorative line breaks and warning icon, so each is now a single line of text; their wording is otherwise unchanged.Fixes
allowedSchemesByTagis now applied tosrcsetandimagesrcsetURLs. Previously the per-tag lookup used the attribute name instead of the tag name, so these attributes always fell back to the globalallowedSchemesand ignored a tag-specific scheme allowlist. Thanks tospokodev for the fix.
sanitize-htmlbegan escaping any markup preserved inside a disallowed iframe tag, which was a changein behavior due to an upstream change in
htmlparser2. This fix ensures such "fallback markup" is preserved without escaping, but alsofully sanitized according to the same rules as the original input. Thanks to sumitjhacodes for
the fix.
Security
When
metawas allowed together with itshttp-equivandcontentattributes, the destination URL of a<meta http-equiv="refresh" content="0;url=...">was never checked againstallowedSchemes, because it is embedded incontentrather than being an attribute of its own. Sojavascript:,data:and other disallowed destinations passed through. The refresh URL is now extracted the way browsers do it, allowing for the different spellings, separators, quoting and letter case ofurl=, and checked againstallowedSchemes(orallowedSchemesByTag.meta). If it is rejected, or the content cannot be parsed as a refresh, thecontentattribute is removed.contenton othermetaelements is unchanged. The default configuration does not allowmetaand was not affected (CWE-79, CWE-601, GHSA-cv27-6wvh-8x7j).Thanks to adrbogacz for reporting the vulnerability.
When
noscriptis listed innonTextTags, the discarded region could end too early. Browsers with scripting enabled treat<noscript>content as raw text up to the first</noscript>, but the underlying parser treats it as markup, so an end tag for an enclosing element inside<noscript>closed it implicitly and the rest of its content was emitted as ordinary sanitized markup. The discard region now continues until the point where a browser would end the<noscript>element, while implied closes of othernonTextTagssuch as<option>behave as before (CWE-79, CWE-436, GHSA-x3q4-9hxx-gx8m).Thanks to joaquiniglesiaslug for reporting the vulnerability.
The check that drops SVG animation elements (
animate,animateColor,animateMotion,animateTransform,set) when they retarget a URL attribute such ashrefcompared the full tag name, so a namespace-prefixed spelling likesvg:animatewas not recognized when such tags were allowed (for example withallowedTags: false). In XML serializations such as XHTML or standalone SVG, the prefixed element is a real animation element and could retarget a link to ajavascript:URL after sanitization. The element andattributeNameare now matched by their local names, ignoring any prefix (CWE-79, CWE-184, GHSA-374f-7chj-9948).Thanks to Kai Aizen (SnailSploit) for reporting the vulnerability.
Configuration
📅 Schedule: (in timezone Asia/Kolkata)
🚦 Automerge: Enabled.
♻ Rebasing: Whenever PR is behind base branch, or you tick the rebase/retry checkbox.
🔕 Ignore: Close this PR and you won't be reminded about this update again.
This PR was generated by Mend Renovate. View the repository job log.