1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299
|
require "helper"
module Nokogiri
module XML
class TestSchema < Nokogiri::TestCase
def setup
super
assert @xsd = Nokogiri::XML::Schema(File.read(PO_SCHEMA_FILE))
end
def test_issue_1985_segv_on_schema_parse
skip("Pure Java version doesn't have this bug") unless Nokogiri.uses_libxml?
# This is a test for a workaround for a bug in LibXML2. The upstream
# bug is here: https://gitlab.gnome.org/GNOME/libxml2/issues/148
# Schema creation can result in dangling pointers. If no nodes have
# been exposed, then it should be fine to create a schema. If nodes
# have been exposed to Ruby, then we need to make sure they won't be
# freed out from under us.
doc = <<~EOF
<?xml version="1.0" encoding="UTF-8" ?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="foo" type="xs:string"/>
</xs:schema>
EOF
# This is OK, no nodes have been exposed
xsd_doc = Nokogiri::XML(doc)
assert Nokogiri::XML::Schema.from_document(xsd_doc)
# This is not OK, nodes have been exposed to Ruby
xsd_doc = Nokogiri::XML(doc)
xsd_doc.root.children.find(&:blank?) # Finds a node
ex = assert_raise(ArgumentError) do
Nokogiri::XML::Schema.from_document(xsd_doc)
end
assert_match(/blank nodes/, ex.message)
end
def test_schema_read_memory
xsd = Nokogiri::XML::Schema.read_memory(File.read(PO_SCHEMA_FILE))
assert_instance_of Nokogiri::XML::Schema, xsd
end
def test_schema_from_document
doc = Nokogiri::XML(File.open(PO_SCHEMA_FILE))
assert doc
xsd = Nokogiri::XML::Schema.from_document doc
assert_instance_of Nokogiri::XML::Schema, xsd
end
def test_invalid_schema_do_not_raise_exceptions
xsd = Nokogiri::XML::Schema.new(<<~EOF)
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:group name="foo1">
<xs:sequence>
<xs:element name="bar" type="xs:boolean" />
</xs:sequence>
</xs:group>
<xs:group name="foo2">
<xs:sequence>
<xs:element name="bar" type="xs:string" />
</xs:sequence>
</xs:group>
<xs:element name="foo">
<xs:complexType>
<xs:choice>
<xs:group ref="foo1"/>
<xs:group ref="foo2"/>
</xs:choice>
</xs:complexType>
</xs:element>
</xs:schema>
EOF
assert_instance_of Nokogiri::XML::Schema, xsd
if Nokogiri.jruby?
assert xsd.errors.length > 0
assert_equal 1, xsd.errors.map(&:to_s).grep(/cos-element-consistent/).length
assert_equal 1, xsd.errors.map(&:to_s).grep(/cos-nonambig/).length
end
end
def test_schema_from_document_node
doc = Nokogiri::XML(File.open(PO_SCHEMA_FILE))
assert doc
xsd = Nokogiri::XML::Schema.from_document doc.root
assert_instance_of Nokogiri::XML::Schema, xsd
end
def test_schema_validates_with_relative_paths
xsd = File.join(ASSETS_DIR, "foo", "foo.xsd")
xml = File.join(ASSETS_DIR, "valid_bar.xml")
doc = Nokogiri::XML(File.open(xsd))
xsd = Nokogiri::XML::Schema.from_document doc
doc = Nokogiri::XML(File.open(xml))
assert xsd.valid?(doc)
end
def test_parse_with_memory
assert_instance_of Nokogiri::XML::Schema, @xsd
assert_equal 0, @xsd.errors.length
end
def test_new
assert xsd = Nokogiri::XML::Schema.new(File.read(PO_SCHEMA_FILE))
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|
assert xsd = Nokogiri::XML::Schema(f)
}
assert_equal 0, xsd.errors.length
end
def test_parse_with_errors
xml = File.read(PO_SCHEMA_FILE).sub(/name="/, "name=")
assert_raises(Nokogiri::XML::SyntaxError) {
Nokogiri::XML::Schema(xml)
}
end
def test_validate_document
doc = Nokogiri::XML(File.read(PO_XML_FILE))
assert errors = @xsd.validate(doc)
assert_equal 0, errors.length
end
def test_validate_file
assert errors = @xsd.validate(PO_XML_FILE)
assert_equal 0, errors.length
end
def test_validate_invalid_document
doc = Nokogiri::XML File.read(PO_XML_FILE)
doc.css("city").unlink
assert errors = @xsd.validate(doc)
assert_equal 2, errors.length
end
def test_validate_invalid_file
tempfile = Tempfile.new("xml")
doc = Nokogiri::XML File.read(PO_XML_FILE)
doc.css("city").unlink
tempfile.write doc.to_xml
tempfile.close
assert errors = @xsd.validate(tempfile.path)
assert_equal 2, errors.length
end
def test_validate_non_document
string = File.read(PO_XML_FILE)
assert_raise(ArgumentError) { @xsd.validate(string) }
end
def test_valid?
valid_doc = Nokogiri::XML(File.read(PO_XML_FILE))
invalid_doc = Nokogiri::XML(
File.read(PO_XML_FILE).gsub(/<city>[^<]*<\/city>/, "")
)
assert(@xsd.valid?(valid_doc))
assert(!@xsd.valid?(invalid_doc))
end
def test_xsd_with_dtd
Dir.chdir(File.join(ASSETS_DIR, "saml")) do
# works
Nokogiri::XML::Schema(IO.read("xmldsig_schema.xsd"))
# was not working
Nokogiri::XML::Schema(IO.read("saml20protocol_schema.xsd"))
end
end
describe "CVE-2020-26247" do
# https://github.com/sparklemotion/nokogiri/security/advisories/GHSA-vr8q-g5c7-m54m
let(:schema) do
<<~EOSCHEMA
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:import namespace="test" schemaLocation="http://localhost:8000/making-a-request"/>
</xs:schema>
EOSCHEMA
end
if Nokogiri.uses_libxml?
describe "with default parse options" do
it "XML::Schema parsing does not attempt to access external DTDs" do
doc = Nokogiri::XML::Schema.new(schema)
errors = doc.errors.map(&:to_s)
assert_equal(1, errors.grep(/ERROR: Attempt to load network entity/).length,
"Should see xmlIO.c:xmlNoNetExternalEntityLoader() raising XML_IO_NETWORK_ATTEMPT")
assert_empty(errors.grep(/WARNING: failed to load HTTP resource/),
"Should not see xmlIO.c:xmlCheckHTTPInput() raising 'failed to load HTTP resource'")
assert_empty(errors.grep(/WARNING: failed to load external entity/),
"Should not see xmlIO.c:xmlDefaultExternalEntityLoader() raising 'failed to load external entity'")
end
it "XML::Schema parsing of memory does not attempt to access external DTDs" do
doc = Nokogiri::XML::Schema.read_memory(schema)
errors = doc.errors.map(&:to_s)
assert_equal(1, errors.grep(/ERROR: Attempt to load network entity/).length,
"Should see xmlIO.c:xmlNoNetExternalEntityLoader() raising XML_IO_NETWORK_ATTEMPT")
assert_empty(errors.grep(/WARNING: failed to load HTTP resource/),
"Should not see xmlIO.c:xmlCheckHTTPInput() raising 'failed to load HTTP resource'")
assert_empty(errors.grep(/WARNING: failed to load external entity/),
"Should not see xmlIO.c:xmlDefaultExternalEntityLoader() raising 'failed to load external entity'")
end
end
describe "with NONET turned off" do
it "XML::Schema parsing attempts to access external DTDs" do
doc = Nokogiri::XML::Schema.new(schema, Nokogiri::XML::ParseOptions.new.nononet)
errors = doc.errors.map(&:to_s)
assert_equal(0, errors.grep(/ERROR: Attempt to load network entity/).length,
"Should not see xmlIO.c:xmlNoNetExternalEntityLoader() raising XML_IO_NETWORK_ATTEMPT")
assert_equal(1, errors.grep(/WARNING: failed to load HTTP resource|WARNING: failed to load external entity/).length)
end
it "XML::Schema parsing of memory attempts to access external DTDs" do
doc = Nokogiri::XML::Schema.read_memory(schema, Nokogiri::XML::ParseOptions.new.nononet)
errors = doc.errors.map(&:to_s)
assert_equal(0, errors.grep(/ERROR: Attempt to load network entity/).length,
"Should not see xmlIO.c:xmlNoNetExternalEntityLoader() raising XML_IO_NETWORK_ATTEMPT")
assert_equal(1, errors.grep(/WARNING: failed to load HTTP resource|WARNING: failed to load external entity/).length)
end
end
end
if Nokogiri.jruby?
describe "with default parse options" do
it "XML::Schema parsing does not attempt to access external DTDs" do
doc = Nokogiri::XML::Schema.new(schema)
assert_equal 1, doc.errors.map(&:to_s).grep(/WARNING: Attempt to load network entity/).length
end
it "XML::Schema parsing of memory does not attempt to access external DTDs" do
doc = Nokogiri::XML::Schema.read_memory(schema)
assert_equal 1, doc.errors.map(&:to_s).grep(/WARNING: Attempt to load network entity/).length
end
end
describe "with NONET turned off" do
it "XML::Schema parsing attempts to access external DTDs" do
doc = Nokogiri::XML::Schema.new(schema, Nokogiri::XML::ParseOptions.new.nononet)
assert_equal 0, doc.errors.map(&:to_s).grep(/WARNING: Attempt to load network entity/).length
end
it "XML::Schema parsing of memory attempts to access external DTDs" do
doc = Nokogiri::XML::Schema.read_memory(schema, Nokogiri::XML::ParseOptions.new.nononet)
assert_equal 0, doc.errors.map(&:to_s).grep(/WARNING: Attempt to load network entity/).length
end
end
end
end
end
end
end
|