Skip to content

Commit

Permalink
Fix a bug that invalid document declaration may be generated
Browse files Browse the repository at this point in the history
HackerOne: HO-1104077

It's caused by quote character.

Reported by Juho Nurminen. Thanks!!!
  • Loading branch information
kou authored and mame committed Apr 5, 2021
1 parent f7bab89 commit f9d88e4
Show file tree
Hide file tree
Showing 2 changed files with 155 additions and 35 deletions.
85 changes: 50 additions & 35 deletions lib/rexml/doctype.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,44 @@
require_relative 'xmltokens'

module REXML
class ReferenceWriter
def initialize(id_type,
public_id_literal,
system_literal,
context=nil)
@id_type = id_type
@public_id_literal = public_id_literal
@system_literal = system_literal
if context and context[:prologue_quote] == :apostrophe
@default_quote = "'"
else
@default_quote = "\""
end
end

def write(output)
output << " #{@id_type}"
if @public_id_literal
if @public_id_literal.include?("'")
quote = "\""
else
quote = @default_quote
end
output << " #{quote}#{@public_id_literal}#{quote}"
end
if @system_literal
if @system_literal.include?("'")
quote = "\""
elsif @system_literal.include?("\"")
quote = "'"
else
quote = @default_quote
end
output << " #{quote}#{@system_literal}#{quote}"
end
end
end

# Represents an XML DOCTYPE declaration; that is, the contents of <!DOCTYPE
# ... >. DOCTYPES can be used to declare the DTD of a document, as well as
# being used to declare entities used in the document.
Expand Down Expand Up @@ -110,19 +148,17 @@ def clone
# Ignored
def write( output, indent=0, transitive=false, ie_hack=false )
f = REXML::Formatters::Default.new
c = context
if c and c[:prologue_quote] == :apostrophe
quote = "'"
else
quote = "\""
end
indent( output, indent )
output << START
output << ' '
output << @name
output << " #{@external_id}" if @external_id
output << " #{quote}#{@long_name}#{quote}" if @long_name
output << " #{quote}#{@uri}#{quote}" if @uri
if @external_id
reference_writer = ReferenceWriter.new(@external_id,
@long_name,
@uri,
context)
reference_writer.write(output)
end
unless @children.empty?
output << ' ['
@children.each { |child|
Expand Down Expand Up @@ -252,32 +288,11 @@ def initialize name, middle, pub, sys
end

def to_s
c = nil
c = parent.context if parent
if c and c[:prologue_quote] == :apostrophe
default_quote = "'"
else
default_quote = "\""
end
notation = "<!NOTATION #{@name} #{@middle}"
if @public
if @public.include?("'")
quote = "\""
else
quote = default_quote
end
notation << " #{quote}#{@public}#{quote}"
end
if @system
if @system.include?("'")
quote = "\""
elsif @system.include?("\"")
quote = "'"
else
quote = default_quote
end
notation << " #{quote}#{@system}#{quote}"
end
context = nil
context = parent.context if parent
notation = "<!NOTATION #{@name}"
reference_writer = ReferenceWriter.new(@middle, @public, @system, context)
reference_writer.write(notation)
notation << ">"
notation
end
Expand Down
105 changes: 105 additions & 0 deletions test/test_doctype.rb
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,111 @@ def test_notations
end
end

class TestDocType < Test::Unit::TestCase
class TestExternalID < self
class TestSystem < self
class TestSystemLiteral < self
def test_to_s
doctype = REXML::DocType.new(["root", "SYSTEM", nil, "root.dtd"])
assert_equal("<!DOCTYPE root SYSTEM \"root.dtd\">",
doctype.to_s)
end

def test_to_s_apostrophe
doctype = REXML::DocType.new(["root", "SYSTEM", nil, "root.dtd"])
doc = REXML::Document.new
doc << doctype
doctype.parent.context[:prologue_quote] = :apostrophe
assert_equal("<!DOCTYPE root SYSTEM 'root.dtd'>",
doctype.to_s)
end

def test_to_s_single_quote_apostrophe
doctype = REXML::DocType.new(["root", "SYSTEM", nil, "root'.dtd"])
doc = REXML::Document.new
doc << doctype
# This isn't used.
doctype.parent.context[:prologue_quote] = :apostrophe
assert_equal("<!DOCTYPE root SYSTEM \"root'.dtd\">",
doctype.to_s)
end

def test_to_s_double_quote
doctype = REXML::DocType.new(["root", "SYSTEM", nil, "root\".dtd"])
doc = REXML::Document.new
doc << doctype
# This isn't used.
doctype.parent.context[:prologue_quote] = :apostrophe
assert_equal("<!DOCTYPE root SYSTEM 'root\".dtd'>",
doctype.to_s)
end
end
end

class TestPublic < self
class TestPublicIDLiteral < self
def test_to_s
doctype = REXML::DocType.new(["root", "PUBLIC", "pub", "root.dtd"])
assert_equal("<!DOCTYPE root PUBLIC \"pub\" \"root.dtd\">",
doctype.to_s)
end

def test_to_s_apostrophe
doctype = REXML::DocType.new(["root", "PUBLIC", "pub", "root.dtd"])
doc = REXML::Document.new
doc << doctype
doctype.parent.context[:prologue_quote] = :apostrophe
assert_equal("<!DOCTYPE root PUBLIC 'pub' 'root.dtd'>",
doctype.to_s)
end

def test_to_s_apostrophe_include_apostrophe
doctype = REXML::DocType.new(["root", "PUBLIC", "pub'", "root.dtd"])
doc = REXML::Document.new
doc << doctype
# This isn't used.
doctype.parent.context[:prologue_quote] = :apostrophe
assert_equal("<!DOCTYPE root PUBLIC \"pub'\" 'root.dtd'>",
doctype.to_s)
end
end

class TestSystemLiteral < self
def test_to_s
doctype = REXML::DocType.new(["root", "PUBLIC", "pub", "root.dtd"])
assert_equal("<!DOCTYPE root PUBLIC \"pub\" \"root.dtd\">",
doctype.to_s)
end

def test_to_s_apostrophe
doctype = REXML::DocType.new(["root", "PUBLIC", "pub", "root.dtd"])
doc = REXML::Document.new
doc << doctype
doctype.parent.context[:prologue_quote] = :apostrophe
assert_equal("<!DOCTYPE root PUBLIC 'pub' 'root.dtd'>",
doctype.to_s)
end

def test_to_s_apostrophe_include_apostrophe
doctype = REXML::DocType.new(["root", "PUBLIC", "pub", "root'.dtd"])
doc = REXML::Document.new
doc << doctype
# This isn't used.
doctype.parent.context[:prologue_quote] = :apostrophe
assert_equal("<!DOCTYPE root PUBLIC 'pub' \"root'.dtd\">",
doctype.to_s)
end

def test_to_s_double_quote
doctype = REXML::DocType.new(["root", "PUBLIC", "pub", "root\".dtd"])
assert_equal("<!DOCTYPE root PUBLIC \"pub\" 'root\".dtd'>",
doctype.to_s)
end
end
end
end
end

class TestNotationDeclPublic < Test::Unit::TestCase
def setup
@name = "vrml"
Expand Down

0 comments on commit f9d88e4

Please sign in to comment.