{"id":"CVE-2026-126063","kind":"vulnerability","aliases":["CVE-2026-126063","GHSA-4p39-36wj-23h4"],"related":[],"title":"Nokogiri: Mutation XSS through `noscript` content in Nokogiri's HTML5 serializer","description":"### Summary\n\nNokogiri's HTML5 serializer did not escape text inside `noscript` elements, or `<` and `>` inside attribute values. When a document parsed with the default options is serialized, a browser with scripting enabled can parse the output into markup that Nokogiri's parser never produced. This is a mutation XSS for applications that sanitize untrusted HTML with `Nokogiri::HTML5`, allow `noscript` elements, and send the serialized output to browsers.\n\nNokogiri 1.19.5 escapes both, as the HTML standard's serialization algorithm requires. Text inside `noscript` is still emitted unescaped for documents parsed with `parse_noscript_content_as_text: true`.\n\nOnly the C extension (CRuby and TruffleRuby) is affected. JRuby does not implement `Nokogiri::HTML5`.\n\n### Severity\n\nThe Nokogiri maintainers have evaluated this as low severity (CVSS 2.3, `CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:P/VC:N/VI:N/VA:N/SC:L/SI:L/SA:N`). Exploitation requires an application that allows `noscript` in sanitized HTML. Loofah and rails-html-sanitizer do not allow it by default, and Sanitize always removes it.\n\n### Mitigation\n\nUpgrade to Nokogiri 1.19.5 or later.\n\nApplications that sanitize HTML and allow `noscript` should parse with `parse_noscript_content_as_text: true` (available since Nokogiri 1.17.0), or remove `noscript` elements. This is the workaround for earlier versions, and it is recommended on every version: some content inside `noscript` is serialized unchanged, as the standard requires.\n\n### Credit\n\nThis issue was responsibly reported by @a-zara-n.","summary":"Nokogiri's HTML5 serializer does not escape text inside noscript elements or angle brackets in attribute values, so a browser can parse the output into markup the parser never produced. An attacker who can supply HTML that an app sanitizes and serializes with Nokogiri::HTML5, while allowing noscript, can run script in a victim's browser.","severity":"low","cvss":2.3,"epss":null,"exploited":false,"exploited_at":null,"products":[{"name":"nokogiri","fixed":["1.19.5"],"affected":">= 1.12.0, < 1.19.5","ecosystem":"rubygems"}],"ecosystems":["rubygems"],"tags":["cwe-116","cwe-79","html-sanitization","nokogiri","package","rails","ruby"],"not_affected_if":["default-sanitizer-config","jruby","no-noscript-allowed","sanitize-gem"],"how_to_check":null,"fix":"- Upgrade `nokogiri` to `1.19.5` or later.\n- Applications that allow `noscript` should parse with `parse_noscript_content_as_text: true` (available since `1.17.0`) or remove `noscript` elements, as the sources recommend on every version.","status":"open","published_at":"2026-11-10T20:47:36+00:00","first_seen_at":"2026-11-10T20:51:58+00:00","triaged_at":"2026-11-10T20:53:41+00:00","updated_at":"2026-11-10T21:46:15+00:00","category":"package","confidence":0.8,"triage":{"at":"2026-11-10T20:53:41Z","model":"claude-haiku-5-5","output":{"fix":"- Upgrade `nokogiri` to `1.19.5` or later.\n- Applications that allow `noscript` should parse with `parse_noscript_content_as_text: true` (available since `1.17.0`) or remove `noscript` elements, as the sources recommend on every version.","reach":"internet","title":"Nokogiri: HTML5 serializer lets sanitized markup become XSS in browsers","impact":"medium","summary":"Nokogiri's HTML5 serializer does not escape text inside noscript elements or angle brackets in attribute values, so a browser can parse the output into markup the parser never produced. An attacker who can supply HTML that an app sanitizes and serializes with Nokogiri::HTML5, while allowing noscript, can run script in a victim's browser.","audience":"developers","category":"package","nicknames":[],"who_cares":["ruby","rails","nokogiri","html-sanitization"],"confidence":0.8,"fix_status":"available","needs_auth":"unknown","worst_case":"script runs in a victim's browser on a sanitized page","analyst_note":"","how_to_check":"- Check the Nokogiri version in `Gemfile.lock` for a line like `nokogiri (1.19.4)`; anything from `1.12.0` up to but not including `1.19.5` is affected.\n- Search the codebase for `Nokogiri::HTML5` and for `noscript` in your sanitizer's allow-list.\n- Check whether parsing uses `parse_noscript_content_as_text: true`; the sources say that setting is the workaround on earlier versions.","plain_action":"","product_name":"","needs_analyst":false,"not_affected_if":["jruby","no-noscript-allowed","default-sanitizer-config","sanitize-gem"],"who_is_affected":"Applications that sanitize untrusted HTML with Nokogiri::HTML5, allow noscript elements, and send the serialized output to browsers. Loofah, rails-html-sanitizer and Sanitize do not allow noscript by default, and Sanitize always removes it, so apps relying only on those are less exposed. Only the C extension on CRuby and TruffleRuby is affected; JRuby does not implement Nokogiri::HTML5.","user_interaction":"yes","product_ecosystem":"","needs_special_setup":"yes","severity_if_unknown":"low"},"cost_usd":0.000916,"input_tokens":5215,"output_tokens":790,"severity_before":"low"},"analyst_status":null,"analyst_note":null,"search":"'-116':25B '-126063':14A,22A '-2026':13A,21A '-79':27B '/av':217C '1.17.0':282C '1.19.5':160C,260C '2.3':214C '23h4':19A '36wj':18A '4.0':216C '4p39':17A 'a-zara-n':319C 'affect':196C 'algorithm':169C 'allow':81B,149C,234C,247C,268C 'alway':253C 'angl':47B 'app':73B 'applic':141C,232C,263C 'attack':66B 'attribut':50B,106C 'avail':279C 'bracket':48B 'browser':54B,90B,119C,158C 'c':190C 'content':6A,184C,275C,302C 'credit':312C 'crubi':192C 'cve':12A,20A 'cvss':213C,215C 'cwe':24B,26B 'default':114C,250C 'document':110C,179C 'earlier':292C 'element':45B,102C,151C,286C 'emit':176C 'enabl':122C 'escap':41B,98C,161C 'evalu':208C 'everi':299C 'exploit':229C 'extens':191C 'ghsa':16A 'ghsa-4p39-36wj-23h4':15A 'html':29B,70B,145C,165C,238C,243C,266C 'html-sanit':28B 'html5':10A,37B,79B,94C,148C,202C 'implement':200C 'insid':43B,100C,105C,172C,303C 'issu':314C 'jrubi':197C 'l/at':219C 'l/sa':227C 'l/si':226C 'later':262C 'loofah':239C 'low':211C 'maintain':206C 'markup':60B,128C 'mitig':256C 'mutat':2A,138C 'n':228C,322C 'n/ac':218C 'n/sc':225C 'n/ui':221C 'n/va':224C 'n/vi':223C 'never':63B,133C 'nokogiri':1A,8A,23A,31B,35B,78B,92C,130C,147C,159C,201C,205C,259C,281C 'noscript':5A,44B,82B,101C,150C,173C,183C,235C,269C,274C,285C,304C 'option':115C 'output':58B,126C,156C 'p/pr':220C 'p/vc':222C 'packag':32B 'pars':56B,111C,124C,180C,182C,271C,273C 'parser':62B,132C 'produc':64B,134C 'rail':33B,242C 'rails-html-sanit':241C 'recommend':297C 'remov':254C,284C 'report':317C 'requir':170C,230C,311C 'respons':316C 'rubi':34B 'run':84B 'sanit':30B,74B,143C,237C,244C,252C,265C 'script':85B,121C 'send':153C 'serial':11A,38B,76B,95C,117C,155C,168C,306C 'sever':203C,212C 'sinc':280C 'standard':166C,310C 'still':175C 'summari':91C 'suppli':69B 'text':42B,99C,171C,186C,277C 'true':187C,278C 'trufflerubi':194C 'unchang':307C 'unescap':177C 'untrust':144C 'upgrad':257C 'valu':51B,107C 'version':293C,300C 'victim':88B 'workaround':290C 'xss':3A,139C 'zara':321C","nicknames":[],"mitigations":[],"who_affected":"Applications that sanitize untrusted HTML with Nokogiri::HTML5, allow noscript elements, and send the serialized output to browsers. Loofah, rails-html-sanitizer and Sanitize do not allow noscript by default, and Sanitize always removes it, so apps relying only on those are less exposed. Only the C extension on CRuby and TruffleRuby is affected; JRuby does not implement Nokogiri::HTML5.","tier":"C","verified":{},"analyst_revisit_at":null,"fix_status":"available","audience":"developers","plain_action":null,"pkeys":["nokogiri"],"ptokens":["nokogiri"],"labels":["cwe116","cwe79","htmlsanitization","low","nokogiri","package","rails","ruby","rubygems"],"campaign_type":null,"search_a":"'-126063':14A,22A '-2026':13A,21A '23h4':19A '36wj':18A '4p39':17A 'content':6A 'cve':12A,20A 'ghsa':16A 'ghsa-4p39-36wj-23h4':15A 'html5':10A 'mutat':2A 'nokogiri':1A,8A,23A 'noscript':5A 'serial':11A 'xss':3A","search_b":"'-116':25B '-79':27B 'allow':81B 'angl':47B 'app':73B 'attack':66B 'attribut':50B 'bracket':48B 'browser':54B,90B 'cwe':24B,26B 'element':45B 'escap':41B 'html':29B,70B 'html-sanit':28B 'html5':37B,79B 'insid':43B 'markup':60B 'never':63B 'nokogiri':31B,35B,78B 'noscript':44B,82B 'output':58B 'packag':32B 'pars':56B 'parser':62B 'produc':64B 'rail':33B 'rubi':34B 'run':84B 'sanit':30B,74B 'script':85B 'serial':38B,76B 'suppli':69B 'text':42B 'valu':51B 'victim':88B","search_c":"'/av':217C '1.17.0':282C '1.19.5':160C,260C '2.3':214C '4.0':216C 'a-zara-n':319C 'affect':196C 'algorithm':169C 'allow':149C,234C,247C,268C 'alway':253C 'applic':141C,232C,263C 'attribut':106C 'avail':279C 'browser':119C,158C 'c':190C 'content':184C,275C,302C 'credit':312C 'crubi':192C 'cvss':213C,215C 'default':114C,250C 'document':110C,179C 'earlier':292C 'element':102C,151C,286C 'emit':176C 'enabl':122C 'escap':98C,161C 'evalu':208C 'everi':299C 'exploit':229C 'extens':191C 'html':145C,165C,238C,243C,266C 'html5':94C,148C,202C 'implement':200C 'insid':100C,105C,172C,303C 'issu':314C 'jrubi':197C 'l/at':219C 'l/sa':227C 'l/si':226C 'later':262C 'loofah':239C 'low':211C 'maintain':206C 'markup':128C 'mitig':256C 'mutat':138C 'n':228C,322C 'n/ac':218C 'n/sc':225C 'n/ui':221C 'n/va':224C 'n/vi':223C 'never':133C 'nokogiri':92C,130C,147C,159C,201C,205C,259C,281C 'noscript':101C,150C,173C,183C,235C,269C,274C,285C,304C 'option':115C 'output':126C,156C 'p/pr':220C 'p/vc':222C 'pars':111C,124C,180C,182C,271C,273C 'parser':132C 'produc':134C 'rail':242C 'rails-html-sanit':241C 'recommend':297C 'remov':254C,284C 'report':317C 'requir':170C,230C,311C 'respons':316C 'sanit':143C,237C,244C,252C,265C 'script':121C 'send':153C 'serial':95C,117C,155C,168C,306C 'sever':203C,212C 'sinc':280C 'standard':166C,310C 'still':175C 'summari':91C 'text':99C,171C,186C,277C 'true':187C,278C 'trufflerubi':194C 'unchang':307C 'unescap':177C 'untrust':144C 'upgrad':257C 'valu':107C 'version':293C,300C 'workaround':290C 'xss':139C 'zara':321C","cause_entry":null,"cause_path":null,"disclosures_checked_at":null,"first_public_at":"2026-11-02T03:45:42+00:00","kev":null,"cvss_vector":"CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:P/VC:N/VI:N/VA:N/SC:L/SI:L/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X","cvss_by":"security-advisories@github.com","poc_count":0,"sources":[{"source":"ghsa","external_id":"GHSA-4p39-36wj-23h4","url":"https://github.com/advisories/GHSA-4p39-36wj-23h4","severity":"low","published_at":"2026-11-10T20:47:36+00:00","modified_at":"2026-11-10T20:47:37+00:00","first_seen_at":"2026-11-10T20:51:58+00:00","fetched_at":"2026-11-10T21:16:10+00:00","note":null},{"source":"nvd","external_id":"CVE-2026-126063","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-126063","severity":null,"published_at":"2026-11-10T21:19:23+00:00","modified_at":"2026-11-10T21:19:23+00:00","first_seen_at":"2026-11-10T21:46:15+00:00","fetched_at":"2026-11-10T21:46:16+00:00","note":null}],"documents":[{"id":118306,"url":"https://api.github.com/repos/sparklemotion/nokogiri/security-advisories/GHSA-4p39-36wj-23h4","title":"Maintainer's advisory: api.github.com","kind":"vendor","source":"ghsa","publisher":"vendor","text":null,"published_at":null,"relation":"advisory","confidence":null,"note":"Maintainer's advisory: api.github.com"},{"id":118307,"url":"https://github.com/sparklemotion/nokogiri/security/advisories/GHSA-4p39-36wj-23h4","title":"Maintainer's advisory: github.com","kind":"vendor","source":"ghsa","publisher":"vendor","text":null,"published_at":"2026-11-02T03:45:42+00:00","relation":"advisory","confidence":null,"note":"Maintainer's advisory: github.com"},{"id":118302,"url":"https://github.com/sparklemotion/nokogiri/pull/3752","title":"Patch: github.com","kind":"other","source":"ghsa","publisher":"vendor","text":null,"published_at":null,"relation":"fix","confidence":null,"note":"Patch: github.com"},{"id":118309,"url":"https://github.com/sparklemotion/nokogiri/commit/3e84df6ff7ab4d78f446e3a415430bd69a055d32","title":"Patch: github.com","kind":"other","source":"ghsa","publisher":"vendor","text":null,"published_at":null,"relation":"fix","confidence":null,"note":"Patch: github.com"},{"id":118310,"url":"https://github.com/sparklemotion/nokogiri/commit/c6c0bffbdcd194694995acafeeed27326b0805f8","title":"Patch: github.com","kind":"other","source":"ghsa","publisher":"vendor","text":null,"published_at":null,"relation":"fix","confidence":null,"note":"Patch: github.com"},{"id":118162,"url":"https://github.com/sparklemotion/nokogiri/releases/tag/v1.19.5","title":"Release notes: github.com","kind":"release","source":"nvd","publisher":"vendor","text":null,"published_at":null,"relation":"fix","confidence":null,"note":"Release: github.com"}],"disclosures":[],"exploit_sources":[],"notice":"Free to read, with fair use of about 60 pages a minute. For higher limits or API access: sales@infrastack.ai"}