XXE in Nokogiri
Description
Nokogiri is a Rubygem providing HTML, XML, SAX, and Reader parsers with XPath and CSS selector support. In Nokogiri before version 1.11.0.rc4 there is an XXE vulnerability. XML Schemas parsed by Nokogiri::XML::Schema are trusted by default, allowing external resources to be accessed over the network, potentially enabling XXE or SSRF attacks. This behavior is counter to the security policy followed by Nokogiri maintainers, which is to treat all input as untrusted by default whenever possible. This is fixed in Nokogiri version 1.11.0.rc4.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
Nokogiri before 1.11.0.rc4 has an XXE vulnerability where XML Schemas parse external resources by default, enabling data theft or SSRF.
Vulnerability
Overview
CVE-2020-26247 is an XML External Entity (XXE) vulnerability in Nokogiri, a popular Ruby gem for parsing HTML and XML. The flaw exists in versions prior to 1.11.0.rc4 and specifically affects XML Schema parsing via Nokogiri::XML::Schema. By default, the parser trusts input and allows the resolution of external entities, which contradicts Nokogiri's security posture of treating all input as untrusted [1].
Exploitation
An attacker can exploit this vulnerability by crafting a malicious XML Schema file that references external entities or resources. If a Nokogiri application processes such a schema, the parser will fetch the specified external resource over the network. No special authentication is required; the vulnerability is triggered simply by parsing an untrusted XML Schema. This can be achieved through user-supplied files, API endpoints, or any other vector that introduces attacker-controlled XSD content into a Nokogiri::XML::Schema call [2].
Impact
Successful exploitation enables an XXE attack, which may allow data exfiltration of local files, server-side request forgery (SSRF), denial of service, or internal network scanning. Since external resources are fetched with the privileges of the Ruby application, an attacker could read sensitive files (e.g., /etc/passwd, configuration files) or probe internal services that are otherwise unreachable from the public internet [3].
Mitigation
The vulnerability is fixed in Nokogiri version 1.11.0.rc4. Users should upgrade to this version or later. The fix changes the default behavior so that XML::Schema parsing treats all input as untrusted, disabling external entity resolution by default. There is no known workaround other than upgrading [4].
AI Insight generated on May 21, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
nokogiriRubyGems | < 1.11.0 | 1.11.0 |
Affected products
116- ghsa-coords115 versionspkg:gem/nokogiripkg:rpm/opensuse/rubygem-nokogiri&distro=openSUSE%20Leap%2015.2pkg:rpm/suse/ardana-cobbler&distro=HPE%20Helion%20OpenStack%208pkg:rpm/suse/ardana-cobbler&distro=SUSE%20OpenStack%20Cloud%208pkg:rpm/suse/cassandra&distro=HPE%20Helion%20OpenStack%208pkg:rpm/suse/cassandra&distro=SUSE%20OpenStack%20Cloud%208pkg:rpm/suse/cassandra&distro=SUSE%20OpenStack%20Cloud%20Crowbar%208pkg:rpm/suse/crowbar-core&distro=SUSE%20OpenStack%20Cloud%20Crowbar%208pkg:rpm/suse/crowbar-openstack&distro=SUSE%20OpenStack%20Cloud%20Crowbar%208pkg:rpm/suse/documentation-hpe-helion-openstack-installation&distro=HPE%20Helion%20OpenStack%208pkg:rpm/suse/documentation-hpe-helion-openstack-operations&distro=HPE%20Helion%20OpenStack%208pkg:rpm/suse/documentation-hpe-helion-openstack-opsconsole&distro=HPE%20Helion%20OpenStack%208pkg:rpm/suse/documentation-hpe-helion-openstack-planning&distro=HPE%20Helion%20OpenStack%208pkg:rpm/suse/documentation-hpe-helion-openstack-security&distro=HPE%20Helion%20OpenStack%208pkg:rpm/suse/documentation-hpe-helion-openstack-user&distro=HPE%20Helion%20OpenStack%208pkg:rpm/suse/documentation-suse-openstack-cloud-deployment&distro=SUSE%20OpenStack%20Cloud%20Crowbar%208pkg:rpm/suse/documentation-suse-openstack-cloud-installation&distro=SUSE%20OpenStack%20Cloud%208pkg:rpm/suse/documentation-suse-openstack-cloud-operations&distro=SUSE%20OpenStack%20Cloud%208pkg:rpm/suse/documentation-suse-openstack-cloud-opsconsole&distro=SUSE%20OpenStack%20Cloud%208pkg:rpm/suse/documentation-suse-openstack-cloud-planning&distro=SUSE%20OpenStack%20Cloud%208pkg:rpm/suse/documentation-suse-openstack-cloud-security&distro=SUSE%20OpenStack%20Cloud%208pkg:rpm/suse/documentation-suse-openstack-cloud-supplement&distro=SUSE%20OpenStack%20Cloud%208pkg:rpm/suse/documentation-suse-openstack-cloud-supplement&distro=SUSE%20OpenStack%20Cloud%20Crowbar%208pkg:rpm/suse/documentation-suse-openstack-cloud-upstream-admin&distro=SUSE%20OpenStack%20Cloud%208pkg:rpm/suse/documentation-suse-openstack-cloud-upstream-admin&distro=SUSE%20OpenStack%20Cloud%20Crowbar%208pkg:rpm/suse/documentation-suse-openstack-cloud-upstream-user&distro=SUSE%20OpenStack%20Cloud%208pkg:rpm/suse/documentation-suse-openstack-cloud-upstream-user&distro=SUSE%20OpenStack%20Cloud%20Crowbar%208pkg:rpm/suse/documentation-suse-openstack-cloud-user&distro=SUSE%20OpenStack%20Cloud%208pkg:rpm/suse/grafana&distro=HPE%20Helion%20OpenStack%208pkg:rpm/suse/grafana&distro=SUSE%20OpenStack%20Cloud%208pkg:rpm/suse/grafana&distro=SUSE%20OpenStack%20Cloud%20Crowbar%208pkg:rpm/suse/kibana&distro=HPE%20Helion%20OpenStack%208pkg:rpm/suse/kibana&distro=SUSE%20OpenStack%20Cloud%208pkg:rpm/suse/kibana&distro=SUSE%20OpenStack%20Cloud%20Crowbar%208pkg:rpm/suse/openstack-heat-templates&distro=HPE%20Helion%20OpenStack%208pkg:rpm/suse/openstack-heat-templates&distro=SUSE%20OpenStack%20Cloud%208pkg:rpm/suse/openstack-heat-templates&distro=SUSE%20OpenStack%20Cloud%20Crowbar%208pkg:rpm/suse/openstack-monasca-installer&distro=HPE%20Helion%20OpenStack%208pkg:rpm/suse/openstack-monasca-installer&distro=SUSE%20OpenStack%20Cloud%208pkg:rpm/suse/openstack-monasca-installer&distro=SUSE%20OpenStack%20Cloud%20Crowbar%208pkg:rpm/suse/openstack-nova&distro=HPE%20Helion%20OpenStack%208pkg:rpm/suse/openstack-nova&distro=SUSE%20OpenStack%20Cloud%208pkg:rpm/suse/openstack-nova&distro=SUSE%20OpenStack%20Cloud%20Crowbar%208pkg:rpm/suse/openstack-nova-doc&distro=HPE%20Helion%20OpenStack%208pkg:rpm/suse/openstack-nova-doc&distro=SUSE%20OpenStack%20Cloud%208pkg:rpm/suse/openstack-nova-doc&distro=SUSE%20OpenStack%20Cloud%20Crowbar%208pkg:rpm/suse/python-Django&distro=HPE%20Helion%20OpenStack%208pkg:rpm/suse/python-Django&distro=SUSE%20OpenStack%20Cloud%208pkg:rpm/suse/python-Django&distro=SUSE%20OpenStack%20Cloud%20Crowbar%208pkg:rpm/suse/python-elementpath&distro=HPE%20Helion%20OpenStack%208pkg:rpm/suse/python-elementpath&distro=SUSE%20OpenStack%20Cloud%208pkg:rpm/suse/python-elementpath&distro=SUSE%20OpenStack%20Cloud%20Crowbar%208pkg:rpm/suse/python-eventlet&distro=HPE%20Helion%20OpenStack%208pkg:rpm/suse/python-eventlet&distro=SUSE%20OpenStack%20Cloud%208pkg:rpm/suse/python-eventlet&distro=SUSE%20OpenStack%20Cloud%20Crowbar%208pkg:rpm/suse/python-py&distro=HPE%20Helion%20OpenStack%208pkg:rpm/suse/python-py&distro=SUSE%20OpenStack%20Cloud%208pkg:rpm/suse/python-py&distro=SUSE%20OpenStack%20Cloud%20Crowbar%208pkg:rpm/suse/python-pysaml2&distro=HPE%20Helion%20OpenStack%208pkg:rpm/suse/python-pysaml2&distro=SUSE%20OpenStack%20Cloud%208pkg:rpm/suse/python-pysaml2&distro=SUSE%20OpenStack%20Cloud%20Crowbar%208pkg:rpm/suse/python-xmlschema&distro=HPE%20Helion%20OpenStack%208pkg:rpm/suse/python-xmlschema&distro=SUSE%20OpenStack%20Cloud%208pkg:rpm/suse/python-xmlschema&distro=SUSE%20OpenStack%20Cloud%20Crowbar%208pkg:rpm/suse/rubygem-activerecord-session_store&distro=SUSE%20OpenStack%20Cloud%20Crowbar%208pkg:rpm/suse/rubygem-nokogiri&distro=SUSE%20Linux%20Enterprise%20High%20Availability%20Extension%2015pkg:rpm/suse/rubygem-nokogiri&distro=SUSE%20Linux%20Enterprise%20High%20Availability%20Extension%2015%20SP1pkg:rpm/suse/rubygem-nokogiri&distro=SUSE%20Linux%20Enterprise%20High%20Availability%20Extension%2015%20SP2pkg:rpm/suse/rubygem-nokogiri&distro=SUSE%20OpenStack%20Cloud%207pkg:rpm/suse/rubygem-nokogiri&distro=SUSE%20OpenStack%20Cloud%20Crowbar%208pkg:rpm/suse/rubygem-nokogiri&distro=SUSE%20OpenStack%20Cloud%20Crowbar%209pkg:rpm/suse/venv-openstack-aodh&distro=HPE%20Helion%20OpenStack%208pkg:rpm/suse/venv-openstack-aodh&distro=SUSE%20OpenStack%20Cloud%208pkg:rpm/suse/venv-openstack-barbican&distro=HPE%20Helion%20OpenStack%208pkg:rpm/suse/venv-openstack-barbican&distro=SUSE%20OpenStack%20Cloud%208pkg:rpm/suse/venv-openstack-ceilometer&distro=HPE%20Helion%20OpenStack%208pkg:rpm/suse/venv-openstack-ceilometer&distro=SUSE%20OpenStack%20Cloud%208pkg:rpm/suse/venv-openstack-cinder&distro=HPE%20Helion%20OpenStack%208pkg:rpm/suse/venv-openstack-cinder&distro=SUSE%20OpenStack%20Cloud%208pkg:rpm/suse/venv-openstack-designate&distro=HPE%20Helion%20OpenStack%208pkg:rpm/suse/venv-openstack-designate&distro=SUSE%20OpenStack%20Cloud%208pkg:rpm/suse/venv-openstack-freezer&distro=HPE%20Helion%20OpenStack%208pkg:rpm/suse/venv-openstack-freezer&distro=SUSE%20OpenStack%20Cloud%208pkg:rpm/suse/venv-openstack-glance&distro=HPE%20Helion%20OpenStack%208pkg:rpm/suse/venv-openstack-glance&distro=SUSE%20OpenStack%20Cloud%208pkg:rpm/suse/venv-openstack-heat&distro=HPE%20Helion%20OpenStack%208pkg:rpm/suse/venv-openstack-heat&distro=SUSE%20OpenStack%20Cloud%208pkg:rpm/suse/venv-openstack-horizon&distro=SUSE%20OpenStack%20Cloud%208pkg:rpm/suse/venv-openstack-horizon-hpe&distro=HPE%20Helion%20OpenStack%208pkg:rpm/suse/venv-openstack-ironic&distro=HPE%20Helion%20OpenStack%208pkg:rpm/suse/venv-openstack-ironic&distro=SUSE%20OpenStack%20Cloud%208pkg:rpm/suse/venv-openstack-keystone&distro=HPE%20Helion%20OpenStack%208pkg:rpm/suse/venv-openstack-keystone&distro=SUSE%20OpenStack%20Cloud%208pkg:rpm/suse/venv-openstack-magnum&distro=HPE%20Helion%20OpenStack%208pkg:rpm/suse/venv-openstack-magnum&distro=SUSE%20OpenStack%20Cloud%208pkg:rpm/suse/venv-openstack-manila&distro=HPE%20Helion%20OpenStack%208pkg:rpm/suse/venv-openstack-manila&distro=SUSE%20OpenStack%20Cloud%208pkg:rpm/suse/venv-openstack-monasca-ceilometer&distro=HPE%20Helion%20OpenStack%208pkg:rpm/suse/venv-openstack-monasca-ceilometer&distro=SUSE%20OpenStack%20Cloud%208pkg:rpm/suse/venv-openstack-monasca&distro=HPE%20Helion%20OpenStack%208pkg:rpm/suse/venv-openstack-monasca&distro=SUSE%20OpenStack%20Cloud%208pkg:rpm/suse/venv-openstack-murano&distro=HPE%20Helion%20OpenStack%208pkg:rpm/suse/venv-openstack-murano&distro=SUSE%20OpenStack%20Cloud%208pkg:rpm/suse/venv-openstack-neutron&distro=HPE%20Helion%20OpenStack%208pkg:rpm/suse/venv-openstack-neutron&distro=SUSE%20OpenStack%20Cloud%208pkg:rpm/suse/venv-openstack-nova&distro=HPE%20Helion%20OpenStack%208pkg:rpm/suse/venv-openstack-nova&distro=SUSE%20OpenStack%20Cloud%208pkg:rpm/suse/venv-openstack-octavia&distro=HPE%20Helion%20OpenStack%208pkg:rpm/suse/venv-openstack-octavia&distro=SUSE%20OpenStack%20Cloud%208pkg:rpm/suse/venv-openstack-sahara&distro=HPE%20Helion%20OpenStack%208pkg:rpm/suse/venv-openstack-sahara&distro=SUSE%20OpenStack%20Cloud%208pkg:rpm/suse/venv-openstack-swift&distro=HPE%20Helion%20OpenStack%208pkg:rpm/suse/venv-openstack-swift&distro=SUSE%20OpenStack%20Cloud%208pkg:rpm/suse/venv-openstack-trove&distro=HPE%20Helion%20OpenStack%208pkg:rpm/suse/venv-openstack-trove&distro=SUSE%20OpenStack%20Cloud%208
< 1.11.0+ 114 more
- (no CPE)range: < 1.11.0
- (no CPE)range: < 1.8.5-lp152.4.3.1
- (no CPE)range: < 8.0+git.1614096566.e8c2b27-3.44.3
- (no CPE)range: < 8.0+git.1614096566.e8c2b27-3.44.3
- (no CPE)range: < 3.11.10-5.3.5
- (no CPE)range: < 3.11.10-5.3.5
- (no CPE)range: < 3.11.10-5.3.5
- (no CPE)range: < 5.0+git.1622489449.a8e60e238-3.50.4
- (no CPE)range: < 5.0+git.1616001417.67fd9c2a1-4.52.5
- (no CPE)range: < 8.20210512-1.32.5
- (no CPE)range: < 8.20210512-1.32.5
- (no CPE)range: < 8.20210512-1.32.5
- (no CPE)range: < 8.20210512-1.32.5
- (no CPE)range: < 8.20210512-1.32.5
- (no CPE)range: < 8.20210512-1.32.5
- (no CPE)range: < 8.20210512-1.32.5
- (no CPE)range: < 8.20210512-1.32.5
- (no CPE)range: < 8.20210512-1.32.5
- (no CPE)range: < 8.20210512-1.32.5
- (no CPE)range: < 8.20210512-1.32.5
- (no CPE)range: < 8.20210512-1.32.5
- (no CPE)range: < 8.20210512-1.32.5
- (no CPE)range: < 8.20210512-1.32.5
- (no CPE)range: < 8.20210512-1.32.5
- (no CPE)range: < 8.20210512-1.32.5
- (no CPE)range: < 8.20210512-1.32.5
- (no CPE)range: < 8.20210512-1.32.5
- (no CPE)range: < 8.20210512-1.32.5
- (no CPE)range: < 6.7.4-4.18.2
- (no CPE)range: < 6.7.4-4.18.2
- (no CPE)range: < 6.7.4-4.18.2
- (no CPE)range: < 4.6.6-3.9.2
- (no CPE)range: < 4.6.6-3.9.2
- (no CPE)range: < 4.6.6-3.9.2
- (no CPE)range: < 0.0.0+git.1623056900.7917e18-3.21.3
- (no CPE)range: < 0.0.0+git.1623056900.7917e18-3.21.3
- (no CPE)range: < 0.0.0+git.1623056900.7917e18-3.21.3
- (no CPE)range: < 20190923_16.32-3.18.2
- (no CPE)range: < 20190923_16.32-3.18.2
- (no CPE)range: < 20190923_16.32-3.18.2
- (no CPE)range: < 16.1.9~dev92-3.48.5
- (no CPE)range: < 16.1.9~dev92-3.48.5
- (no CPE)range: < 16.1.9~dev92-3.48.5
- (no CPE)range: < 16.1.9~dev92-3.48.5
- (no CPE)range: < 16.1.9~dev92-3.48.5
- (no CPE)range: < 16.1.9~dev92-3.48.5
- (no CPE)range: < 1.11.29-3.25.3
- (no CPE)range: < 1.11.29-3.25.3
- (no CPE)range: < 1.11.29-3.25.3
- (no CPE)range: < 1.3.1-1.3.2
- (no CPE)range: < 1.3.1-1.3.2
- (no CPE)range: < 1.3.1-1.3.2
- (no CPE)range: < 0.20.0-6.3.3
- (no CPE)range: < 0.20.0-6.3.3
- (no CPE)range: < 0.20.0-6.3.3
- (no CPE)range: < 1.4.34-3.3.3
- (no CPE)range: < 1.4.34-3.3.3
- (no CPE)range: < 1.4.34-3.3.3
- (no CPE)range: < 4.0.2-5.9.2
- (no CPE)range: < 4.0.2-5.9.2
- (no CPE)range: < 4.0.2-5.9.2
- (no CPE)range: < 1.0.18-1.3.3
- (no CPE)range: < 1.0.18-1.3.3
- (no CPE)range: < 1.0.18-1.3.3
- (no CPE)range: < 0.1.2-3.3.2
- (no CPE)range: < 1.8.5-3.6.1
- (no CPE)range: < 1.8.5-3.6.1
- (no CPE)range: < 1.8.5-3.6.1
- (no CPE)range: < 1.6.1-5.3.1
- (no CPE)range: < 1.6.1-5.3.1
- (no CPE)range: < 1.6.1-5.3.1
- (no CPE)range: < 5.1.1~dev7-12.32.3
- (no CPE)range: < 5.1.1~dev7-12.32.3
- (no CPE)range: < 5.0.2~dev3-12.33.3
- (no CPE)range: < 5.0.2~dev3-12.33.3
- (no CPE)range: < 9.0.8~dev7-12.30.3
- (no CPE)range: < 9.0.8~dev7-12.30.3
- (no CPE)range: < 11.2.3~dev29-14.34.2
- (no CPE)range: < 11.2.3~dev29-14.34.2
- (no CPE)range: < 5.0.3~dev7-12.31.3
- (no CPE)range: < 5.0.3~dev7-12.31.3
- (no CPE)range: < 5.0.0.0~xrc2~dev2-10.28.3
- (no CPE)range: < 5.0.0.0~xrc2~dev2-10.28.3
- (no CPE)range: < 15.0.3~dev3-12.31.3
- (no CPE)range: < 15.0.3~dev3-12.31.3
- (no CPE)range: < 9.0.8~dev22-12.33.2
- (no CPE)range: < 9.0.8~dev22-12.33.2
- (no CPE)range: < 12.0.5~dev6-14.36.6
- (no CPE)range: < 12.0.5~dev6-14.36.3
- (no CPE)range: < 9.1.8~dev8-12.33.3
- (no CPE)range: < 9.1.8~dev8-12.33.3
- (no CPE)range: < 12.0.4~dev11-11.35.3
- (no CPE)range: < 12.0.4~dev11-11.35.3
- (no CPE)range: < 5.0.2_5.0.2_5.0.2~dev31-11.32.2
- (no CPE)range: < 5.0.2_5.0.2_5.0.2~dev31-11.32.2
- (no CPE)range: < 5.1.1~dev5-12.37.3
- (no CPE)range: < 5.1.1~dev5-12.37.3
- (no CPE)range: < 1.5.1_1.5.1_1.5.1~dev3-8.28.3
- (no CPE)range: < 1.5.1_1.5.1_1.5.1~dev3-8.28.3
- (no CPE)range: < 2.2.2~dev1-11.28.3
- (no CPE)range: < 2.2.2~dev1-11.28.3
- (no CPE)range: < 4.0.2~dev2-12.28.3
- (no CPE)range: < 4.0.2~dev2-12.28.3
- (no CPE)range: < 11.0.9~dev69-13.38.3
- (no CPE)range: < 11.0.9~dev69-13.38.3
- (no CPE)range: < 16.1.9~dev92-11.36.3
- (no CPE)range: < 16.1.9~dev92-11.36.3
- (no CPE)range: < 1.0.6~dev3-12.33.3
- (no CPE)range: < 1.0.6~dev3-12.33.3
- (no CPE)range: < 7.0.5~dev4-11.32.3
- (no CPE)range: < 7.0.5~dev4-11.32.3
- (no CPE)range: < 2.15.2_2.15.2_2.15.2~dev32-11.23.3
- (no CPE)range: < 2.15.2_2.15.2_2.15.2~dev32-11.23.3
- (no CPE)range: < 8.0.2~dev2-11.32.3
- (no CPE)range: < 8.0.2~dev2-11.32.3
- sparklemotion/nokogiriv5Range: < 1.11.0.rc4
Patches
19c87439d9afafeat: XML::Schema and RelaxNG creation accept optional ParseOptions
9 files changed · +182 −44
ext/java/nokogiri/XmlRelaxng.java+9 −2 modified@@ -56,6 +56,7 @@ import org.jruby.RubyClass; import org.jruby.anno.JRubyClass; import org.jruby.runtime.ThreadContext; +import org.jruby.runtime.builtin.IRubyObject; import org.w3c.dom.Document; import org.xml.sax.ErrorHandler; import org.xml.sax.SAXException; @@ -78,11 +79,17 @@ private void setVerifier(Verifier verifier) { this.verifier = verifier; } - static XmlSchema createSchemaInstance(ThreadContext context, RubyClass klazz, Source source) { + static XmlSchema createSchemaInstance(ThreadContext context, RubyClass klazz, Source source, IRubyObject parseOptions) { Ruby runtime = context.getRuntime(); XmlRelaxng xmlRelaxng = (XmlRelaxng) NokogiriService.XML_RELAXNG_ALLOCATOR.allocate(runtime, klazz); + + if (parseOptions == null) { + parseOptions = defaultParseOptions(context.getRuntime()); + } + xmlRelaxng.setInstanceVariable("@errors", runtime.newEmptyArray()); - + xmlRelaxng.setInstanceVariable("@parse_options", parseOptions); + try { Schema schema = xmlRelaxng.getSchema(source, context); xmlRelaxng.setVerifier(schema.newVerifier());
ext/java/nokogiri/XmlSchema.java+34 −13 modified@@ -106,10 +106,16 @@ private void setValidator(Validator validator) { this.validator = validator; } - static XmlSchema createSchemaInstance(ThreadContext context, RubyClass klazz, Source source) { + static XmlSchema createSchemaInstance(ThreadContext context, RubyClass klazz, Source source, IRubyObject parseOptions) { Ruby runtime = context.getRuntime(); XmlSchema xmlSchema = (XmlSchema) NokogiriService.XML_SCHEMA_ALLOCATOR.allocate(runtime, klazz); + + if (parseOptions == null) { + parseOptions = defaultParseOptions(context.getRuntime()); + } + xmlSchema.setInstanceVariable("@errors", runtime.newEmptyArray()); + xmlSchema.setInstanceVariable("@parse_options", parseOptions); try { SchemaErrorHandler error_handler = new SchemaErrorHandler(context.getRuntime(), (RubyArray)xmlSchema.getInstanceVariable("@errors")); @@ -121,14 +127,24 @@ static XmlSchema createSchemaInstance(ThreadContext context, RubyClass klazz, So } } + protected static IRubyObject defaultParseOptions(Ruby runtime) { + return ((RubyClass)runtime.getClassFromPath("Nokogiri::XML::ParseOptions")).getConstant("DEFAULT_SCHEMA"); + } + /* * call-seq: * from_document(doc) * * Create a new Schema from the Nokogiri::XML::Document +doc+ */ - @JRubyMethod(meta=true) - public static IRubyObject from_document(ThreadContext context, IRubyObject klazz, IRubyObject document) { + @JRubyMethod(meta=true, required=1, optional=1) + public static IRubyObject from_document(ThreadContext context, IRubyObject klazz, IRubyObject[] args) { + IRubyObject document = args[0]; + IRubyObject parseOptions = null; + if (args.length > 1) { + parseOptions = args[1]; + } + XmlDocument doc = ((XmlDocument) ((XmlNode) document).document(context)); RubyArray errors = (RubyArray) doc.getInstanceVariable("@errors"); @@ -144,25 +160,30 @@ public static IRubyObject from_document(ThreadContext context, IRubyObject klazz source.setSystemId(uri.convertToString().asJavaString()); } - return getSchema(context, (RubyClass)klazz, source); + return getSchema(context, (RubyClass)klazz, source, parseOptions); } - private static IRubyObject getSchema(ThreadContext context, RubyClass klazz, Source source) { + @JRubyMethod(meta=true, required=1, optional=1) + public static IRubyObject read_memory(ThreadContext context, IRubyObject klazz, IRubyObject[] args) { + IRubyObject content = args[0]; + IRubyObject parseOptions = null; + if (args.length > 1) { + parseOptions = args[1]; + } + String data = content.convertToString().asJavaString(); + return getSchema(context, (RubyClass) klazz, new StreamSource(new StringReader(data)), parseOptions); + } + + private static IRubyObject getSchema(ThreadContext context, RubyClass klazz, Source source, IRubyObject parseOptions) { String moduleName = klazz.getName(); if ("Nokogiri::XML::Schema".equals(moduleName)) { - return XmlSchema.createSchemaInstance(context, klazz, source); + return XmlSchema.createSchemaInstance(context, klazz, source, parseOptions); } else if ("Nokogiri::XML::RelaxNG".equals(moduleName)) { - return XmlRelaxng.createSchemaInstance(context, klazz, source); + return XmlRelaxng.createSchemaInstance(context, klazz, source, parseOptions); } return context.getRuntime().getNil(); } - @JRubyMethod(meta=true) - public static IRubyObject read_memory(ThreadContext context, IRubyObject klazz, IRubyObject content) { - String data = content.convertToString().asJavaString(); - return getSchema(context, (RubyClass) klazz, new StreamSource(new StringReader(data))); - } - @JRubyMethod(visibility=Visibility.PRIVATE) public IRubyObject validate_document(ThreadContext context, IRubyObject document) { return validate_document_or_file(context, (XmlDocument)document);
ext/nokogiri/xml_relax_ng.c+28 −11 modified@@ -53,16 +53,24 @@ static VALUE validate_document(VALUE self, VALUE document) * * Create a new RelaxNG from the contents of +string+ */ -static VALUE read_memory(VALUE klass, VALUE content) +static VALUE read_memory(int argc, VALUE *argv, VALUE klass) { - xmlRelaxNGParserCtxtPtr ctx = xmlRelaxNGNewMemParserCtxt( - (const char *)StringValuePtr(content), - (int)RSTRING_LEN(content) - ); + VALUE content; + VALUE parse_options; + xmlRelaxNGParserCtxtPtr ctx; xmlRelaxNGPtr schema; - VALUE errors = rb_ary_new(); + VALUE errors; VALUE rb_schema; + int scanned_args = 0; + + scanned_args = rb_scan_args(argc, argv, "11", &content, &parse_options); + if (scanned_args == 1) { + parse_options = rb_const_get(rb_const_get(mNokogiriXml, rb_intern("ParseOptions")), rb_intern("DEFAULT_SCHEMA")); + } + ctx = xmlRelaxNGNewMemParserCtxt((const char *)StringValuePtr(content), (int)RSTRING_LEN(content)); + + errors = rb_ary_new(); xmlSetStructuredErrorFunc((void *)errors, Nokogiri_error_array_pusher); #ifdef HAVE_XMLRELAXNGSETPARSERSTRUCTUREDERRORS @@ -90,6 +98,7 @@ static VALUE read_memory(VALUE klass, VALUE content) rb_schema = Data_Wrap_Struct(klass, 0, dealloc, schema); rb_iv_set(rb_schema, "@errors", errors); + rb_iv_set(rb_schema, "@parse_options", parse_options); return rb_schema; } @@ -100,18 +109,25 @@ static VALUE read_memory(VALUE klass, VALUE content) * * Create a new RelaxNG schema from the Nokogiri::XML::Document +doc+ */ -static VALUE from_document(VALUE klass, VALUE document) +static VALUE from_document(int argc, VALUE *argv, VALUE klass) { + VALUE document; + VALUE parse_options; xmlDocPtr doc; xmlRelaxNGParserCtxtPtr ctx; xmlRelaxNGPtr schema; VALUE errors; VALUE rb_schema; + int scanned_args = 0; + + scanned_args = rb_scan_args(argc, argv, "11", &document, &parse_options); Data_Get_Struct(document, xmlDoc, doc); + doc = doc->doc; /* In case someone passes us a node. ugh. */ - /* In case someone passes us a node. ugh. */ - doc = doc->doc; + if (scanned_args == 1) { + parse_options = rb_const_get(rb_const_get(mNokogiriXml, rb_intern("ParseOptions")), rb_intern("DEFAULT_SCHEMA")); + } ctx = xmlRelaxNGNewDocParserCtxt(doc); @@ -143,6 +159,7 @@ static VALUE from_document(VALUE klass, VALUE document) rb_schema = Data_Wrap_Struct(klass, 0, dealloc, schema); rb_iv_set(rb_schema, "@errors", errors); + rb_iv_set(rb_schema, "@parse_options", parse_options); return rb_schema; } @@ -156,7 +173,7 @@ void init_xml_relax_ng() cNokogiriXmlRelaxNG = klass; - rb_define_singleton_method(klass, "read_memory", read_memory, 1); - rb_define_singleton_method(klass, "from_document", from_document, 1); + rb_define_singleton_method(klass, "read_memory", read_memory, -1); + rb_define_singleton_method(klass, "from_document", from_document, -1); rb_define_private_method(klass, "validate_document", validate_document, 1); }
ext/nokogiri/xml_schema.c+34 −12 modified@@ -93,23 +93,34 @@ static VALUE validate_file(VALUE self, VALUE rb_filename) * * Create a new Schema from the contents of +string+ */ -static VALUE read_memory(VALUE klass, VALUE content) +static VALUE read_memory(int argc, VALUE *argv, VALUE klass) { + VALUE content; + VALUE parse_options; + int parse_options_int; + xmlSchemaParserCtxtPtr ctx; xmlSchemaPtr schema; - xmlSchemaParserCtxtPtr ctx = xmlSchemaNewMemParserCtxt( - (const char *)StringValuePtr(content), - (int)RSTRING_LEN(content) - ); + VALUE errors; VALUE rb_schema; - VALUE errors = rb_ary_new(); + int scanned_args = 0; + + scanned_args = rb_scan_args(argc, argv, "11", &content, &parse_options); + if (scanned_args == 1) { + parse_options = rb_const_get(rb_const_get(mNokogiriXml, rb_intern("ParseOptions")), rb_intern("DEFAULT_SCHEMA")); + } + parse_options_int = (int)NUM2INT(rb_funcall(parse_options, rb_intern("to_i"), 0)); + + ctx = xmlSchemaNewMemParserCtxt((const char *)StringValuePtr(content), (int)RSTRING_LEN(content)); + + errors = rb_ary_new(); xmlSetStructuredErrorFunc((void *)errors, Nokogiri_error_array_pusher); #ifdef HAVE_XMLSCHEMASETPARSERSTRUCTUREDERRORS xmlSchemaSetParserStructuredErrors( ctx, Nokogiri_error_array_pusher, (void *)errors - ); + ); #endif schema = xmlSchemaParse(ctx); @@ -129,6 +140,7 @@ static VALUE read_memory(VALUE klass, VALUE content) rb_schema = Data_Wrap_Struct(klass, 0, dealloc, schema); rb_iv_set(rb_schema, "@errors", errors); + rb_iv_set(rb_schema, "@parse_options", parse_options); return rb_schema; } @@ -164,18 +176,27 @@ static int has_blank_nodes_p(VALUE cache) * * Create a new Schema from the Nokogiri::XML::Document +doc+ */ -static VALUE from_document(VALUE klass, VALUE document) +static VALUE from_document(int argc, VALUE *argv, VALUE klass) { + VALUE document; + VALUE parse_options; + int parse_options_int; xmlDocPtr doc; xmlSchemaParserCtxtPtr ctx; xmlSchemaPtr schema; VALUE errors; VALUE rb_schema; + int scanned_args = 0; + + scanned_args = rb_scan_args(argc, argv, "11", &document, &parse_options); Data_Get_Struct(document, xmlDoc, doc); + doc = doc->doc; /* In case someone passes us a node. ugh. */ - /* In case someone passes us a node. ugh. */ - doc = doc->doc; + if (scanned_args == 1) { + parse_options = rb_const_get(rb_const_get(mNokogiriXml, rb_intern("ParseOptions")), rb_intern("DEFAULT_SCHEMA")); + } + parse_options_int = (int)NUM2INT(rb_funcall(parse_options, rb_intern("to_i"), 0)); if (has_blank_nodes_p(DOC_NODE_CACHE(doc))) { rb_raise(rb_eArgError, "Creating a schema from a document that has blank nodes exposed to Ruby is dangerous"); @@ -211,6 +232,7 @@ static VALUE from_document(VALUE klass, VALUE document) rb_schema = Data_Wrap_Struct(klass, 0, dealloc, schema); rb_iv_set(rb_schema, "@errors", errors); + rb_iv_set(rb_schema, "@parse_options", parse_options); return rb_schema; @@ -226,8 +248,8 @@ void init_xml_schema() cNokogiriXmlSchema = klass; - rb_define_singleton_method(klass, "read_memory", read_memory, 1); - rb_define_singleton_method(klass, "from_document", from_document, 1); + rb_define_singleton_method(klass, "read_memory", read_memory, -1); + rb_define_singleton_method(klass, "from_document", from_document, -1); rb_define_private_method(klass, "validate_document", validate_document, 1); rb_define_private_method(klass, "validate_file", validate_file, 1);
lib/nokogiri/xml/parse_options.rb+2 −0 modified@@ -73,6 +73,8 @@ class ParseOptions DEFAULT_XML = RECOVER | NONET # the default options used for parsing HTML documents DEFAULT_HTML = RECOVER | NOERROR | NOWARNING | NONET + # the default options used for parsing XML schemas + DEFAULT_SCHEMA = NONET attr_accessor :options def initialize options = STRICT
lib/nokogiri/xml/relax_ng.rb+2 −2 modified@@ -5,8 +5,8 @@ class << self ### # Create a new Nokogiri::XML::RelaxNG document from +string_or_io+. # See Nokogiri::XML::RelaxNG for an example. - def RelaxNG string_or_io - RelaxNG.new(string_or_io) + def RelaxNG(string_or_io, options = ParseOptions::DEFAULT_SCHEMA) + RelaxNG.new(string_or_io, options) end end
lib/nokogiri/xml/schema.rb+6 −4 modified@@ -5,8 +5,8 @@ class << self ### # Create a new Nokogiri::XML::Schema object using a +string_or_io+ # object. - def Schema string_or_io - Schema.new(string_or_io) + def Schema(string_or_io, options = ParseOptions::DEFAULT_SCHEMA) + Schema.new(string_or_io, options) end end @@ -30,12 +30,14 @@ def Schema string_or_io class Schema # Errors while parsing the schema file attr_accessor :errors + # The Nokogiri::XML::ParseOptions used to parse the schema + attr_accessor :parse_options ### # Create a new Nokogiri::XML::Schema object using a +string_or_io+ # object. - def self.new string_or_io - from_document Nokogiri::XML(string_or_io) + def self.new string_or_io, options = ParseOptions::DEFAULT_SCHEMA + from_document(Nokogiri::XML(string_or_io), options) end ###
test/xml/test_relax_ng.rb+34 −0 modified@@ -26,6 +26,40 @@ def test_parse_with_io assert_equal 0, xsd.errors.length end + def test_constructor_method_with_parse_options + schema = Nokogiri::XML::RelaxNG(File.read(ADDRESS_SCHEMA_FILE)) + assert_equal Nokogiri::XML::ParseOptions::DEFAULT_SCHEMA, schema.parse_options + + schema = Nokogiri::XML::RelaxNG(File.read(ADDRESS_SCHEMA_FILE), Nokogiri::XML::ParseOptions.new.recover) + assert_equal Nokogiri::XML::ParseOptions.new.recover, schema.parse_options + end + + def test_new_with_parse_options + schema = Nokogiri::XML::RelaxNG.new(File.read(ADDRESS_SCHEMA_FILE)) + assert_equal Nokogiri::XML::ParseOptions::DEFAULT_SCHEMA, schema.parse_options + + schema = Nokogiri::XML::RelaxNG.new(File.read(ADDRESS_SCHEMA_FILE), Nokogiri::XML::ParseOptions.new.recover) + assert_equal Nokogiri::XML::ParseOptions.new.recover, schema.parse_options + end + + def test_from_document_with_parse_options + schema = Nokogiri::XML::RelaxNG.from_document(Nokogiri::XML::Document.parse(File.read(ADDRESS_SCHEMA_FILE))) + assert_equal Nokogiri::XML::ParseOptions::DEFAULT_SCHEMA, schema.parse_options + + schema = Nokogiri::XML::RelaxNG.from_document(Nokogiri::XML::Document.parse(File.read(ADDRESS_SCHEMA_FILE)), + Nokogiri::XML::ParseOptions.new.recover) + assert_equal Nokogiri::XML::ParseOptions.new.recover, schema.parse_options + end + + def test_read_memory_with_parse_options + schema = Nokogiri::XML::RelaxNG.read_memory(File.read(ADDRESS_SCHEMA_FILE)) + assert_equal Nokogiri::XML::ParseOptions::DEFAULT_SCHEMA, schema.parse_options + + schema = Nokogiri::XML::RelaxNG.read_memory(File.read(ADDRESS_SCHEMA_FILE), + Nokogiri::XML::ParseOptions.new.recover) + assert_equal Nokogiri::XML::ParseOptions.new.recover, schema.parse_options + end + def test_parse_with_errors xml = File.read(ADDRESS_SCHEMA_FILE).sub(/name="/, 'name=') assert_raises(Nokogiri::XML::SyntaxError) {
test/xml/test_schema.rb+33 −0 modified@@ -109,6 +109,39 @@ def test_new assert_instance_of Nokogiri::XML::Schema, xsd end + def test_schema_method_with_parse_options + schema = Nokogiri::XML::Schema(File.read(PO_SCHEMA_FILE)) + assert_equal Nokogiri::XML::ParseOptions::DEFAULT_SCHEMA, schema.parse_options + + schema = Nokogiri::XML::Schema(File.read(PO_SCHEMA_FILE), Nokogiri::XML::ParseOptions.new.recover) + assert_equal Nokogiri::XML::ParseOptions.new.recover, schema.parse_options + end + + def test_schema_new_with_parse_options + schema = Nokogiri::XML::Schema.new(File.read(PO_SCHEMA_FILE)) + assert_equal Nokogiri::XML::ParseOptions::DEFAULT_SCHEMA, schema.parse_options + + schema = Nokogiri::XML::Schema.new(File.read(PO_SCHEMA_FILE), Nokogiri::XML::ParseOptions.new.recover) + assert_equal Nokogiri::XML::ParseOptions.new.recover, schema.parse_options + end + + def test_schema_from_document_with_parse_options + schema = Nokogiri::XML::Schema.from_document(Nokogiri::XML::Document.parse(File.read(PO_SCHEMA_FILE))) + assert_equal Nokogiri::XML::ParseOptions::DEFAULT_SCHEMA, schema.parse_options + + schema = Nokogiri::XML::Schema.from_document(Nokogiri::XML::Document.parse(File.read(PO_SCHEMA_FILE)), + Nokogiri::XML::ParseOptions.new.recover) + assert_equal Nokogiri::XML::ParseOptions.new.recover, schema.parse_options + end + + def test_schema_read_memory_with_parse_options + schema = Nokogiri::XML::Schema.read_memory(File.read(PO_SCHEMA_FILE)) + assert_equal Nokogiri::XML::ParseOptions::DEFAULT_SCHEMA, schema.parse_options + + schema = Nokogiri::XML::Schema.read_memory(File.read(PO_SCHEMA_FILE), Nokogiri::XML::ParseOptions.new.recover) + assert_equal Nokogiri::XML::ParseOptions.new.recover, schema.parse_options + end + def test_parse_with_io xsd = nil File.open(PO_SCHEMA_FILE, "rb") { |f|
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
12- github.com/advisories/GHSA-vr8q-g5c7-m54mghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2020-26247ghsaADVISORY
- security.gentoo.org/glsa/202208-29ghsavendor-advisoryWEB
- github.com/rubysec/ruby-advisory-db/blob/master/gems/nokogiri/CVE-2020-26247.ymlghsaWEB
- github.com/sparklemotion/nokogiri/blob/main/CHANGELOG.mdghsaWEB
- github.com/sparklemotion/nokogiri/commit/9c87439d9afa14a365ff13e73adc809cb2c3d97bghsaWEB
- github.com/sparklemotion/nokogiri/releases/tag/v1.11.0.rc4ghsaWEB
- github.com/sparklemotion/nokogiri/security/advisories/GHSA-vr8q-g5c7-m54mghsaWEB
- hackerone.com/reports/747489ghsaWEB
- lists.debian.org/debian-lts-announce/2021/06/msg00007.htmlghsamailing-listWEB
- lists.debian.org/debian-lts-announce/2022/10/msg00018.htmlghsamailing-listWEB
- rubygems.org/gems/nokogirighsaWEB
News mentions
0No linked articles in our index yet.