{"id":"CVE-2026-84371","aliases":["CLSA-2026-1788517172","EUVD-2026-69654","GHSA-g8qq-57p8-ggw5"],"status":"published","title":"ApostropheCMS: Stored XSS via SVG SMIL URI-list scheme-policy bypass","description":"ApostropheCMS is an open-source Node.js content management system, and sanitize-html provides a simple HTML sanitizer with a clear API. From version 1.9.0 until version 2.17.7, packages/sanitize-html/index.js validates an animation value attribute as one flat URL and does not recognize that attributeName selecting href or xlink:href gives the sibling values, from, to, or by attribute SVG SMIL URL semantics. In configurations that allow the animate, animateColor, animateMotion, animateTransform, or set elements, a values list can begin with a safe fragment and contain a later executable destination that survives allowedSchemesAppliedToAttributes checking. When the sanitized SVG is rendered, the browser can copy that later destination into the live link, and a victim who activates the link can execute script in the application's origin. This issue is fixed in version 2.17.7.","severity":{"version":"3.1","score":5.4,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:L/I:L/A:N","level":"medium","assessor":"cna","from":"cve"},"epss":{"day":"2026-11-10","score":0.00298,"percentile":0.2271,"model":"v2026.06.15"},"facts":{"in_kev":false,"known_exploited":false,"public_exploit":false,"fix_available":true},"cwes":["CWE-79"],"published":"2026-09-01T20:51:10Z","modified":"2026-10-28T02:30:33Z","fixes":[{"ecosystem":"npm","package":"sanitize-html","fixed_in":["2.17.7"],"sources":["ghsa","osv"]},{"ecosystem":"TuxCare:npm","package":"sanitize-html","fixed_in":["1.27.5-tuxcare.5"],"sources":["osv"]}],"affected":[{"source":"ghsa","ecosystem":"npm","package":"sanitize-html","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"1.9.0"},{"last_affected":"2.17.6"},{"fixed":"2.17.7"}]}]},{"source":"osv","ecosystem":"TuxCare:npm","package":"sanitize-html","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"1.27.5-tuxcare.0"},{"fixed":"1.27.5-tuxcare.5"}]}],"versions":["1.27.5","1.27.5-tuxcare.1","1.27.5-tuxcare.2","1.27.5-tuxcare.3","1.27.5-tuxcare.4"]},{"source":"osv","ecosystem":"npm","package":"sanitize-html","ranges":[{"type":"SEMVER","events":[{"introduced":"1.9.0"},{"fixed":"2.17.7"}]}]}],"products":[{"vendor":"apostrophecms","product":"apostrophe"}],"references":[{"url":"https://github.com/apostrophecms/apostrophe/security/advisories/GHSA-g8qq-57p8-ggw5","tags":["x_refsource_CONFIRM","advisory","web"]},{"url":"https://github.com/apostrophecms/apostrophe/pull/5552","tags":["x_refsource_MISC","fix","web"]},{"url":"https://github.com/apostrophecms/apostrophe/commit/1135516a1a4a8f9638641c460488a43d8af20081","tags":["x_refsource_MISC","fix","web"]},{"url":"https://github.com/apostrophecms/apostrophe/commit/38ff1106c8176b16c2da9872acd9b449adcbb949","tags":["x_refsource_MISC","fix","web"]},{"url":"https://github.com/apostrophecms/apostrophe/blob/main/packages/sanitize-html/CHANGELOG.md","tags":["x_refsource_MISC","web"]},{"url":"https://github.com/advisories/GHSA-g8qq-57p8-ggw5"},{"url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/84xxx/CVE-2026-84371.json","tags":["advisory"]},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-84371","tags":["advisory"]},{"url":"https://github.com/apostrophecms/apostrophe","tags":["package"]}],"sources":{"cve":[{"id":"CVE-2026-84371","title":"ApostropheCMS: Stored XSS via SVG SMIL URI-list scheme-policy bypass","description":"ApostropheCMS is an open-source Node.js content management system, and sanitize-html provides a simple HTML sanitizer with a clear API. From version 1.9.0 until version 2.17.7, packages/sanitize-html/index.js validates an animation value attribute as one flat URL and does not recognize that attributeName selecting href or xlink:href gives the sibling values, from, to, or by attribute SVG SMIL URL semantics. In configurations that allow the animate, animateColor, animateMotion, animateTransform, or set elements, a values list can begin with a safe fragment and contain a later executable destination that survives allowedSchemesAppliedToAttributes checking. When the sanitized SVG is rendered, the browser can copy that later destination into the live link, and a victim who activates the link can execute script in the application's origin. This issue is fixed in version 2.17.7.","status":"published","published":"2026-09-01T20:51:10.441Z","modified":"2026-09-02T16:02:13.385Z","cvss":[{"version":"3.1","score":5.4,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:L/I:L/A:N","level":"medium","assessor":"cna"}],"cwes":["CWE-79"],"updated_at":"2026-11-02T14:20:08.734143Z"}],"euvd":[{"id":"EUVD-2026-69654","description":"ApostropheCMS is an open-source Node.js content management system, and sanitize-html provides a simple HTML sanitizer with a clear API. From version 1.9.0 until version 2.17.7, packages/sanitize-html/index.js validates an animation value attribute as one flat URL and does not recognize that attributeName selecting href or xlink:href gives the sibling values, from, to, or by attribute SVG SMIL URL semantics. In configurations that allow the animate, animateColor, animateMotion, animateTransform, or set elements, a values list can begin with a safe fragment and contain a later executable destination that survives allowedSchemesAppliedToAttributes checking. When the sanitized SVG is rendered, the browser can copy that later destination into the live link, and a victim who activates the link can execute script in the application's origin. This issue is fixed in version 2.17.7.","status":"published","published":"2026-09-01T20:51:10Z","modified":"2026-09-02T16:02:13Z","cvss":[{"version":"3.1","score":5.4,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:L/I:L/A:N","level":"medium"}],"updated_at":"2026-11-02T18:01:15.092584Z"}],"ghsa":[{"id":"GHSA-g8qq-57p8-ggw5","title":"ApostropheCMS: Stored XSS via SVG SMIL URI-list scheme-policy bypass","description":"### Summary\nWhen SVG animation is allowed, `attributeName=\"href\"` makes `values` a list of URL destinations. `sanitize-html` accepts a list that starts with a safe fragment even when `values` is explicitly scheme-checked, allowing a later `javascript:` destination to execute when the sanitized link is activated.\n\n### Details\n`index.js:371-383` validates each attribute as one flat URL. It does not recognize that `attributeName=\"href\"` gives the sibling `values` attribute SMIL URI-list semantics. For `values=\"#safe;javascript:...\"`, the leading fragment passes the flat check and the complete list is retained.\n\n### PoC\nThis was reproduced with `sanitize-html@2.17.6` and Chromium 150.0.7871.124. The configuration adds SVG animation to the defaults and applies the existing scheme policy to `values`; it does not allow `javascript:`. Save this as `poc.js`:\n\n```js\nconst sanitize = require('sanitize-html');\n\nconst input = `\u003csvg\u003e\u003ca\u003e\u003canimate attributeName=\"href\" values=\"#safe;javascript:alert('XSS')\" dur=\".01s\" fill=\"freeze\"\u003e\u003c/animate\u003e\u003ctext y=\"30\"\u003eClick me\u003c/text\u003e\u003c/a\u003e\u003c/svg\u003e`;\nconst output = sanitize(input, {\n  allowedTags: sanitize.defaults.allowedTags.concat(['svg', 'animate', 'text']),\n  allowedAttributes: {\n    ...sanitize.defaults.allowedAttributes,\n    animate: ['attributename', 'values', 'dur', 'fill'],\n    text: ['y']\n  },\n  allowedSchemesAppliedToAttributes:\n    sanitize.defaults.allowedSchemesAppliedToAttributes.concat(['values'])\n});\nconsole.log(output);\n```\n\nInstall and run it, then open `poc.html` and click `Click me`:\n\n```sh\nnpm install sanitize-html@2.17.6\nnode poc.js \u003e poc.html\n```\n\nThe output retains the `javascript:` entry, and clicking the sanitized SVG displays `XSS`. With `input` changed to `\u003ca href=\"javascript:alert(1)\"\u003econtrol\u003c/a\u003e`, the same configuration removes `href`.\n\n### Impact\nIn an application that accepts attacker-authored SVG animation, the attacker can store this payload without scripts or event handlers. A victim who activates the sanitized link executes JavaScript in the application's origin despite the configured scheme policy.\n\n### Suggested fix\nReject `attributeName` values selecting `href` or `xlink:href` on SVG `animate` and `set`, while retaining safe targets such as `fill`. Add `values`, `from`, and `to` regression cases.","status":"published","published":"2026-09-01T21:20:01Z","modified":"2026-09-01T21:21:20Z","cvss":[{"version":"3.1","score":5.4,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:L/I:L/A:N","level":"medium"}],"cwes":["CWE-79"],"updated_at":"2026-11-02T14:41:10.121131Z"}],"nvd":[{"id":"CVE-2026-84371","description":"ApostropheCMS is an open-source Node.js content management system, and sanitize-html provides a simple HTML sanitizer with a clear API. From version 1.9.0 until version 2.17.7, packages/sanitize-html/index.js validates an animation value attribute as one flat URL and does not recognize that attributeName selecting href or xlink:href gives the sibling values, from, to, or by attribute SVG SMIL URL semantics. In configurations that allow the animate, animateColor, animateMotion, animateTransform, or set elements, a values list can begin with a safe fragment and contain a later executable destination that survives allowedSchemesAppliedToAttributes checking. When the sanitized SVG is rendered, the browser can copy that later destination into the live link, and a victim who activates the link can execute script in the application's origin. This issue is fixed in version 2.17.7.","status":"published","published":"2026-09-01T21:18:48.357Z","modified":"2026-09-09T21:09:13.08Z","cvss":[{"version":"3.1","score":5.4,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:L/I:L/A:N","level":"medium","assessor":"secondary"}],"cwes":["CWE-79"],"updated_at":"2026-11-02T14:12:43.04945Z"}],"osv":[{"id":"CVE-2026-84371","title":"ApostropheCMS: Stored XSS via SVG SMIL URI-list scheme-policy bypass","description":"ApostropheCMS is an open-source Node.js content management system, and sanitize-html provides a simple HTML sanitizer with a clear API. From version 1.9.0 until version 2.17.7, packages/sanitize-html/index.js validates an animation value attribute as one flat URL and does not recognize that attributeName selecting href or xlink:href gives the sibling values, from, to, or by attribute SVG SMIL URL semantics. In configurations that allow the animate, animateColor, animateMotion, animateTransform, or set elements, a values list can begin with a safe fragment and contain a later executable destination that survives allowedSchemesAppliedToAttributes checking. When the sanitized SVG is rendered, the browser can copy that later destination into the live link, and a victim who activates the link can execute script in the application's origin. This issue is fixed in version 2.17.7.","status":"published","published":"2026-09-01T20:51:10.441Z","modified":"2026-10-28T02:30:33.633981534Z","cvss":[{"version":"3.1","score":5.4,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:L/I:L/A:N","level":"medium"}],"cwes":["CWE-79"],"updated_at":"2026-11-02T15:45:18.482949Z"},{"id":"GHSA-g8qq-57p8-ggw5","title":"ApostropheCMS: Stored XSS via SVG SMIL URI-list scheme-policy bypass","description":"### Summary\nWhen SVG animation is allowed, `attributeName=\"href\"` makes `values` a list of URL destinations. `sanitize-html` accepts a list that starts with a safe fragment even when `values` is explicitly scheme-checked, allowing a later `javascript:` destination to execute when the sanitized link is activated.\n\n### Details\n`index.js:371-383` validates each attribute as one flat URL. It does not recognize that `attributeName=\"href\"` gives the sibling `values` attribute SMIL URI-list semantics. For `values=\"#safe;javascript:...\"`, the leading fragment passes the flat check and the complete list is retained.\n\n### PoC\nThis was reproduced with `sanitize-html@2.17.6` and Chromium 150.0.7871.124. The configuration adds SVG animation to the defaults and applies the existing scheme policy to `values`; it does not allow `javascript:`. Save this as `poc.js`:\n\n```js\nconst sanitize = require('sanitize-html');\n\nconst input = `\u003csvg\u003e\u003ca\u003e\u003canimate attributeName=\"href\" values=\"#safe;javascript:alert('XSS')\" dur=\".01s\" fill=\"freeze\"\u003e\u003c/animate\u003e\u003ctext y=\"30\"\u003eClick me\u003c/text\u003e\u003c/a\u003e\u003c/svg\u003e`;\nconst output = sanitize(input, {\n  allowedTags: sanitize.defaults.allowedTags.concat(['svg', 'animate', 'text']),\n  allowedAttributes: {\n    ...sanitize.defaults.allowedAttributes,\n    animate: ['attributename', 'values', 'dur', 'fill'],\n    text: ['y']\n  },\n  allowedSchemesAppliedToAttributes:\n    sanitize.defaults.allowedSchemesAppliedToAttributes.concat(['values'])\n});\nconsole.log(output);\n```\n\nInstall and run it, then open `poc.html` and click `Click me`:\n\n```sh\nnpm install sanitize-html@2.17.6\nnode poc.js \u003e poc.html\n```\n\nThe output retains the `javascript:` entry, and clicking the sanitized SVG displays `XSS`. With `input` changed to `\u003ca href=\"javascript:alert(1)\"\u003econtrol\u003c/a\u003e`, the same configuration removes `href`.\n\n### Impact\nIn an application that accepts attacker-authored SVG animation, the attacker can store this payload without scripts or event handlers. A victim who activates the sanitized link executes JavaScript in the application's origin despite the configured scheme policy.\n\n### Suggested fix\nReject `attributeName` values selecting `href` or `xlink:href` on SVG `animate` and `set`, while retaining safe targets such as `fill`. Add `values`, `from`, and `to` regression cases.","status":"published","published":"2026-09-01T21:20:01Z","modified":"2026-09-10T03:50:54.46318441Z","cvss":[{"version":"3.1","score":5.4,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:L/I:L/A:N","level":"medium"}],"cwes":["CWE-79"],"updated_at":"2026-11-02T15:46:47.472172Z"},{"id":"CLSA-2026-1788517172","title":"TuxCare security update for sanitize-html (1 CVE)","description":"TuxCare rebuilt sanitize-html to address 1 CVE. Fixed in 1.27.5-tuxcare.5.","status":"published","published":"2026-09-04T10:19:32Z","modified":"2026-10-15T09:48:03.133276877Z","updated_at":"2026-11-02T15:43:47.591214Z"}]}}
