GHSA-CV27-6WVH-8X7J

Vulnerability from github – Published: 2026-10-20 18:15 – Updated: 2026-10-20 18:15
VLAI
Summary
ApostropheCMS: Incomplete CVE-2026-53606: `meta http-equiv=refresh` `content` still ships `javascript:` / `data:` URLs
Details

Summary

CVE-2026-53606 expanded allowedSchemesAppliedToAttributes so named URL attributes (action, formaction, data, poster, background, later ping, xlink:href, …) go through naughtyHref(). A URL nested inside meta content (http-equiv=refresh) is not an attribute name on that list, so the embedded destination is never scheme-checked.

Default allowedTags still omit meta. Same non-default integrator config as 53606: allow meta plus http-equiv / content.

On 2.17.7:

sanitizeHtml(
  '<meta http-equiv="refresh" content="0;url=javascript:alert(1)">',
  { allowedTags: ['meta'], allowedAttributes: { meta: ['http-equiv', 'content'] } }
)
// => <meta http-equiv="refresh" content="0;url=javascript:alert(1)" />

data:text/html,… and arbitrary https: destinations also survive. formaction="javascript:…" (53606) is stripped.

Please Request CVE. Credit: Finder + Reporter (adrbogacz).

Details

naughtyHref() only runs when the attribute name is in allowedSchemesAppliedToAttributes (index.js ~385). Refresh URLs live in a content value of the form 0;url=…, so the gate never fires. Adding content to that list wholesale would break ordinary meta content (description, viewport).

Fix: when http-equiv is refresh (case-insensitive), parse the ; url= destination and run naughtyHref('meta', url). Drop content (or the tag) if the URL is rejected. Leave other meta content values alone.

PoC

Local sanitize-html@2.17.7. Output inspection only:

node -e '
const sanitize = require("sanitize-html");
const opts = { allowedTags: ["meta"], allowedAttributes: { meta: ["http-equiv", "content"] } };
for (const html of [
  "<meta http-equiv=\"refresh\" content=\"0;url=javascript:alert(1)\">",
  "<meta http-equiv=\"refresh\" content=\"0;url=data:text/html,aaa\">",
  "<form><button formaction=\"javascript:alert(1)\">x</button></form>",
]) {
  console.log(html, "=>", sanitize(html, html.includes("meta") ? opts : { allowedTags: ["form","button"], allowedAttributes: { button: ["formaction"] } }));
}
'

Expected: refresh javascript: / data: remain; 53606 formaction is removed. Do not attach a browser hang or stored-XSS page.

Impact

An integrator who allowed meta + content expecting allowedSchemes to apply (the 53606 configuration pattern) still emits a refresh to javascript:, data:, or an attacker https: URL. Stored XSS / open redirect for viewers of that HTML. Default config is not affected.

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "npm",
        "name": "sanitize-html"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "2.18.0"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-113661"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-79"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-10-20T18:15:33Z",
    "nvd_published_at": null,
    "severity": "MODERATE"
  },
  "details": "### Summary\n\nCVE-2026-53606 expanded `allowedSchemesAppliedToAttributes` so named URL attributes (`action`, `formaction`, `data`, `poster`, `background`, later `ping`, `xlink:href`, \u2026) go through `naughtyHref()`. A **URL nested inside `meta` `content`** (`http-equiv=refresh`) is not an attribute name on that list, so the embedded destination is never scheme-checked.\n\nDefault `allowedTags` still omit `meta`. Same non-default integrator config as 53606: allow `meta` plus `http-equiv` / `content`.\n\nOn **2.17.7**:\n\n```text\nsanitizeHtml(\n  \u0027\u003cmeta http-equiv=\"refresh\" content=\"0;url=javascript:alert(1)\"\u003e\u0027,\n  { allowedTags: [\u0027meta\u0027], allowedAttributes: { meta: [\u0027http-equiv\u0027, \u0027content\u0027] } }\n)\n// =\u003e \u003cmeta http-equiv=\"refresh\" content=\"0;url=javascript:alert(1)\" /\u003e\n```\n\n`data:text/html,\u2026` and arbitrary `https:` destinations also survive. `formaction=\"javascript:\u2026\"` (53606) is stripped.\n\nPlease **Request CVE**. Credit: Finder + Reporter (`adrbogacz`).\n\n### Details\n\n`naughtyHref()` only runs when the attribute name is in `allowedSchemesAppliedToAttributes` (`index.js` ~385). Refresh URLs live in a `content` value of the form `0;url=\u2026`, so the gate never fires. Adding `content` to that list wholesale would break ordinary meta `content` (description, viewport).\n\n**Fix:** when `http-equiv` is `refresh` (case-insensitive), parse the `; url=` destination and run `naughtyHref(\u0027meta\u0027, url)`. Drop `content` (or the tag) if the URL is rejected. Leave other `meta` `content` values alone.\n\n### PoC\n\nLocal `sanitize-html@2.17.7`. Output inspection only:\n\n```text\nnode -e \u0027\nconst sanitize = require(\"sanitize-html\");\nconst opts = { allowedTags: [\"meta\"], allowedAttributes: { meta: [\"http-equiv\", \"content\"] } };\nfor (const html of [\n  \"\u003cmeta http-equiv=\\\"refresh\\\" content=\\\"0;url=javascript:alert(1)\\\"\u003e\",\n  \"\u003cmeta http-equiv=\\\"refresh\\\" content=\\\"0;url=data:text/html,aaa\\\"\u003e\",\n  \"\u003cform\u003e\u003cbutton formaction=\\\"javascript:alert(1)\\\"\u003ex\u003c/button\u003e\u003c/form\u003e\",\n]) {\n  console.log(html, \"=\u003e\", sanitize(html, html.includes(\"meta\") ? opts : { allowedTags: [\"form\",\"button\"], allowedAttributes: { button: [\"formaction\"] } }));\n}\n\u0027\n```\n\nExpected: refresh `javascript:` / `data:` remain; 53606 `formaction` is removed. Do not attach a browser hang or stored-XSS page.\n\n### Impact\n\nAn integrator who allowed `meta` + `content` expecting `allowedSchemes` to apply (the 53606 configuration pattern) still emits a refresh to `javascript:`, `data:`, or an attacker `https:` URL. Stored XSS / open redirect for viewers of that HTML. Default config is not affected.",
  "id": "GHSA-cv27-6wvh-8x7j",
  "modified": "2026-10-20T18:15:33Z",
  "published": "2026-10-20T18:15:33Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/apostrophecms/apostrophe/security/advisories/GHSA-cv27-6wvh-8x7j"
    },
    {
      "type": "WEB",
      "url": "https://github.com/apostrophecms/apostrophe/commit/9500b8b0e1d630d5e07fbb5fd2c25ed0943845ce"
    },
    {
      "type": "WEB",
      "url": "https://github.com/apostrophecms/apostrophe/commit/f84fd41cbf6c22eb8d0008fc9c25df1f7130b350"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/apostrophecms/apostrophe"
    },
    {
      "type": "WEB",
      "url": "https://github.com/apostrophecms/apostrophe/releases/tag/sanitize-html@2.18.0"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:L/I:L/A:N",
      "type": "CVSS_V3"
    }
  ],
  "summary": "ApostropheCMS: Incomplete CVE-2026-53606: `meta http-equiv=refresh` `content` still ships `javascript:` / `data:` URLs"
}



Log in or create an account to share your comment.




Tags
Taxonomy of the tags.


Loading…

Loading…

Loading…

Drag nodes to arrange them. Double-click a related vulnerability to open it, or a source to visit its link. The Physics button on the left tunes the layout and the simulation; the legend at the bottom left hides or shows nodes by kind and by sighting type.

Forecast uses a logistic model when the trend is rising, or an exponential decay model when the trend is falling. Fitted via linearized least squares.

Sightings

Author Source Type Date Other

Nomenclature

  • Seen: The vulnerability was mentioned, discussed, or observed by the user.
  • Confirmed: The vulnerability has been validated from an analyst's perspective.
  • Published Proof of Concept: A public proof of concept is available for this vulnerability.
  • Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
  • Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
  • Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
  • Not confirmed: The user expressed doubt about the validity of the vulnerability.
  • Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.
Vulnerability timeline

Sightings and recorded dates, grouped by day · UTC

JSON
Events included in the timeline

Loading timeline…


    Loading…

    Loading…

    Loading…

    Related by attack behaviour

    Vulnerabilities whose description is nearest to this one in the vector space of the CIRCL/vulnerability-attack-technique-biencoder model. This is a similarity search over the bi-encoder space (plain cosine), not a classification, and it has no measured accuracy.