Skip to content
Heist/ feed
Back to feed
MediumFix available

LiquidJS's strip_html filter bypass via newline characters in HTML tags enables XSS

Type: advisorySources: OSV

## Summary The `strip_html` filter in liquidjs is intended to remove HTML tags from a string before rendering, and is widely used as an XSS sanitizer. The implementation uses a regex whose catch-all branch (`<.*?>`) does not match line terminators, so any HTML tag containing a `\n` or `\r` character passes through unmodified. An attacker who can place a newline inside a tag (e.g. ``) bypasses sanitization entirely, since browsers treat newlines as whitespace within a tag and execute the resulting `onerror`/`onload`/etc. handler. This results in stored or reflected XSS in any application that relies on `strip_html` to neutralize untrusted HTML. ## Details The vulnerable code is in `src/filters/html.ts`: ```ts // src/filters/html.ts:45-49 export function strip_html (this: FilterImpl, v: string) { const str = stringify(v) this.context.memoryLimit.use(str.length) return str.replace(/||<.*?>|/g, '') } ``` The regex has four alternations: 1. `` — uses `[\s\S]`, matches across newlines. 2. `` — uses `[\s\S]`, matches across newlines. 3. `<.*?>` — uses `.`, which in JavaScript does **not** match `\n` or `\r` (no `s`/dotAll flag set). 4. `` — uses `[\s\S]`, matches across newlines. Branch 3 is the catch-all for "any other tag." Because `.` excludes line terminators, a tag containing a newline does not match any alternative. The literal characters of the tag are passed through to the output. Browsers, however, parse HTML tag content with whitespace tolerance: per the HTML spec, attribu

Products
liquidjs
Affected versions
Not specified
Fixed versions
10.26.0
CVEs
CVE-2026-44644
Confirmed fixed CVEs
Not specified
Updated in this feed
1 Nov 2026, 18:44 UTC · Revision 1

Source evidence

LiquidJS's strip_html filter bypass via newline characters in HTML tags enables XSS

core-osv · Original source

Published 27 May 2026, 00:09 UTC

## Summary The `strip_html` filter in liquidjs is intended to remove HTML tags from a string before rendering, and is widely used as an XSS sanitizer. The implementation uses a regex whose catch-all branch (`<.*?>`) does not match line terminators, so any HTML tag containing a `\n` or `\r` character passes through unmodified. An attacker who can place a newline inside a tag (e.g. ``) bypasses sanitization entirely, since browsers treat newlines as whitespace within a tag and execute the resulting `onerror`/`onload`/etc. handler. This results in stored or reflected XSS in any application that relies on `strip_html` to neutralize untrusted HTML. ## Details The vulnerable code is in `src/filters/html.ts`: ```ts // src/filters/html.ts:45-49 export function strip_html (this: FilterImpl, v: string) { const str = stringify(v) this.context.memoryLimit.use(str.length) return str.replace(/||<.*?>|/g, '') } ``` The regex has four alternations: 1. `` — uses `[\s\S]`, matches across newlines. 2. `` — uses `[\s\S]`, matches across newlines. 3. `<.*?>` — uses `.`, which in JavaScript does **not** match `\n` or `\r` (no `s`/dotAll flag set). 4. `` — uses `[\s\S]`, matches across newlines. Branch 3 is the catch-all for "any other tag." Because `.` excludes line terminators, a tag containing a newline does not match any alternative. The literal characters of the tag are passed through to the output. Browsers, however, parse HTML tag content with whitespace tolerance: per the HTML spec, attribu

osv:GHSA-2qv6-9wx5-cwv4

liquidjs

Fixed versions: 10.26.0

Complete source record
{
  "id": "6c4930477003fb142c053306f8c35034169134f43a374ca342e65e3b60d8e18b",
  "url": "https://osv.dev/vulnerability/GHSA-2qv6-9wx5-cwv4",
  "method": "structured",
  "payload": {
    "kind": "advisory",
    "title": "LiquidJS's strip_html filter bypass via newline characters in HTML tags enables XSS",
    "summary": "## Summary The `strip_html` filter in liquidjs is intended to remove HTML tags from a string before rendering, and is widely used as an XSS sanitizer. The implementation uses a regex whose catch-all branch (`<.*?>`) does not match line terminators, so any HTML tag containing a `\\n` or `\\r` character passes through unmodified. An attacker who can place a newline inside a tag (e.g. ``) bypasses sanitization entirely, since browsers treat newlines as whitespace within a tag and execute the resulting `onerror`/`onload`/etc. handler. This results in stored or reflected XSS in any application that relies on `strip_html` to neutralize untrusted HTML. ## Details The vulnerable code is in `src/filters/html.ts`: ```ts // src/filters/html.ts:45-49 export function strip_html (this: FilterImpl, v: string) { const str = stringify(v) this.context.memoryLimit.use(str.length) return str.replace(/||<.*?>|/g, '') } ``` The regex has four alternations: 1. `` — uses `[\\s\\S]`, matches across newlines. 2. `` — uses `[\\s\\S]`, matches across newlines. 3. `<.*?>` — uses `.`, which in JavaScript does **not** match `\\n` or `\\r` (no `s`/dotAll flag set). 4. `` — uses `[\\s\\S]`, matches across newlines. Branch 3 is the catch-all for \"any other tag.\" Because `.` excludes line terminators, a tag containing a newline does not match any alternative. The literal characters of the tag are passed through to the output. Browsers, however, parse HTML tag content with whitespace tolerance: per the HTML spec, attribu",
    "evidence": "osv:GHSA-2qv6-9wx5-cwv4",
    "products": [
      {
        "name": {
          "value": "liquidjs",
          "evidence": "/affected/0/package/name"
        },
        "prerequisites": [],
        "fixed_versions": [
          {
            "value": "10.26.0",
            "evidence": "/affected/0/ranges"
          }
        ],
        "affected_versions": []
      }
    ],
    "severity": "medium",
    "structured": {
      "id": "GHSA-2qv6-9wx5-cwv4",
      "aliases": [
        "CVE-2026-44644",
        "GHSA-2qv6-9wx5-cwv4"
      ],
      "metrics": [
        {
          "type": "CVSS_V3",
          "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N"
        }
      ],
      "affected": [
        {
          "ranges": [
            {
              "type": "SEMVER",
              "events": [
                {
                  "introduced": "0"
                },
                {
                  "fixed": "10.26.0"
                }
              ]
            }
          ],
          "package": {
            "name": "liquidjs",
            "purl": "pkg:npm/liquidjs",
            "ecosystem": "npm"
          },
          "database_specific": {
            "source": "https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/05/GHSA-2qv6-9wx5-cwv4/GHSA-2qv6-9wx5-cwv4.json"
          }
        }
      ],
      "modified": "2026-10-27T15:15:08.036573629Z",
      "provider": "osv",
      "references": [
        "https://github.com/harttle/liquidjs/security/advisories/GHSA-2qv6-9wx5-cwv4",
        "https://nvd.nist.gov/vuln/detail/CVE-2026-44644",
        "https://github.com/harttle/liquidjs/commit/26ea2856c7a90aec892b98d94a9b7a3e18539045",
        "https://github.com/harttle/liquidjs",
        "https://github.com/harttle/liquidjs/blob/v10.26.0/CHANGELOG.md",
        "https://github.com/harttle/liquidjs/releases/tag/v10.26.0"
      ],
      "provider_metadata": {
        "cwe_ids": [
          "CWE-79"
        ],
        "severity": "MODERATE",
        "github_reviewed": true,
        "nvd_published_at": "2026-06-17T23:17:03Z",
        "github_reviewed_at": "2026-05-27T00:09:12Z"
      }
    },
    "related_cves": [
      {
        "value": "CVE-2026-44644",
        "evidence": "/aliases"
      }
    ],
    "security_relevant": true,
    "severity_evidence": "/severity/0/score"
  },
  "source_id": "core-osv",
  "document_id": 462826,
  "published_at": "2026-05-27T00:09:12+00:00"
}