Add a couple of XSS tests.
Some more tests from Clint Ruoho. The main branch of Instiki (and, I guess, the old sanitizer) are vulnerable. Also: under Ruby 1.8.x, CGI.unescapeHTML screws up horribly decoding NCRs which represent high-bit ASCII characters. UTF-8 agrees with 7-bit ASCII, but CGI.unescapeHTML doesn't seem to know that they disagree for i>127.
This commit is contained in:
parent
8832dd3438
commit
52c1f74ecc
4 changed files with 90 additions and 3 deletions
|
@ -133,7 +133,7 @@ module Sanitizer
|
||||||
if node.attributes
|
if node.attributes
|
||||||
node.attributes.delete_if { |attr,v| !ALLOWED_ATTRIBUTES.include?(attr) }
|
node.attributes.delete_if { |attr,v| !ALLOWED_ATTRIBUTES.include?(attr) }
|
||||||
ATTR_VAL_IS_URI.each do |attr|
|
ATTR_VAL_IS_URI.each do |attr|
|
||||||
val_unescaped = CGI.unescapeHTML(node.attributes[attr].to_s).gsub(/`|[\000-\040\177\s\200-\240]/,'').downcase
|
val_unescaped = node.attributes[attr].to_s.unescapeHTML.gsub(/`|[\000-\040\177\s]+|\302[\200-\240]/,'').downcase
|
||||||
if val_unescaped =~ /^[a-z0-9][-+.a-z0-9]*:/ and !ALLOWED_PROTOCOLS.include?(val_unescaped.split(':')[0])
|
if val_unescaped =~ /^[a-z0-9][-+.a-z0-9]*:/ and !ALLOWED_PROTOCOLS.include?(val_unescaped.split(':')[0])
|
||||||
node.attributes.delete attr
|
node.attributes.delete attr
|
||||||
end
|
end
|
||||||
|
|
|
@ -2243,7 +2243,7 @@ class String
|
||||||
end
|
end
|
||||||
when /\A#x([0-9a-f]+)\z/ni then
|
when /\A#x([0-9a-f]+)\z/ni then
|
||||||
if $1.hex < 256
|
if $1.hex < 256
|
||||||
$1.hex.chr
|
[$1.hex].pack("U")
|
||||||
else
|
else
|
||||||
if $1.hex < 1114111
|
if $1.hex < 1114111
|
||||||
[$1.hex].pack("U")
|
[$1.hex].pack("U")
|
||||||
|
|
|
@ -470,6 +470,47 @@
|
||||||
"rexml": "<image src=\"foo\"></image>",
|
"rexml": "<image src=\"foo\"></image>",
|
||||||
"xhtml": "<image src='foo'/>",
|
"xhtml": "<image src='foo'/>",
|
||||||
"output": "<image src=\"foo\"/>"
|
"output": "<image src=\"foo\"/>"
|
||||||
}
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"name": "style_attr_end_with_nothing",
|
||||||
|
"input": "<div style=\"color: blue\" />",
|
||||||
|
"output": "<div style='color: blue;'/>",
|
||||||
|
"rexml": "<div style='color: blue;'></div>"
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"name": "style_attr_end_with_space",
|
||||||
|
"input": "<div style=\"color: blue \" />",
|
||||||
|
"output": "<div style='color: blue ;'/>",
|
||||||
|
"rexml": "<div style='color: blue ;'></div>"
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"name": "style_attr_end_with_semicolon",
|
||||||
|
"input": "<div style=\"color: blue;\" />",
|
||||||
|
"output": "<div style='color: blue;'/>",
|
||||||
|
"rexml": "<div style='color: blue;'></div>"
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"name": "style_attr_end_with_semicolon_space",
|
||||||
|
"input": "<div style=\"color: blue; \" />",
|
||||||
|
"output": "<div style='color: blue;'/>",
|
||||||
|
"rexml": "<div style='color: blue;'></div>"
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"name": "attributes_with_embedded_quotes",
|
||||||
|
"input": "<img src=doesntexist.jpg\"'onerror=\"alert(1) />",
|
||||||
|
"output": "<img src='doesntexist.jpg"'onerror="alert(1)'/>",
|
||||||
|
"rexml": "Ill-formed XHTML!"
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"name": "attributes_with_embedded_quotes_II",
|
||||||
|
"input": "<img src=notthere.jpg\"\"onerror=\"alert(2) />",
|
||||||
|
"output": "<img src='notthere.jpg""onerror="alert(2)'/>",
|
||||||
|
"rexml": "Ill-formed XHTML!"
|
||||||
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -451,5 +451,51 @@
|
||||||
"input": "<image src='foo' />",
|
"input": "<image src='foo' />",
|
||||||
"rexml": "<image src=\"foo\"></image>",
|
"rexml": "<image src=\"foo\"></image>",
|
||||||
"output": "<image src=\"foo\"/>"
|
"output": "<image src=\"foo\"/>"
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"name": "style_attr_end_with_nothing",
|
||||||
|
"input": "<div style=\"color: blue\" />",
|
||||||
|
"output": "<div style='color: blue;'/>",
|
||||||
|
"xhtml": "<div style='color: blue;'></div>",
|
||||||
|
"rexml": "<div style='color: blue;'></div>"
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"name": "style_attr_end_with_space",
|
||||||
|
"input": "<div style=\"color: blue \" />",
|
||||||
|
"output": "<div style='color: blue ;'/>",
|
||||||
|
"xhtml": "<div style='color: blue ;'></div>",
|
||||||
|
"rexml": "<div style='color: blue ;'></div>"
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"name": "style_attr_end_with_semicolon",
|
||||||
|
"input": "<div style=\"color: blue;\" />",
|
||||||
|
"output": "<div style='color: blue;'/>",
|
||||||
|
"xhtml": "<div style='color: blue;'></div>",
|
||||||
|
"rexml": "<div style='color: blue;'></div>"
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"name": "style_attr_end_with_semicolon_space",
|
||||||
|
"input": "<div style=\"color: blue; \" />",
|
||||||
|
"output": "<div style='color: blue;'/>",
|
||||||
|
"xhtml": "<div style='color: blue;'></div>",
|
||||||
|
"rexml": "<div style='color: blue;'></div>"
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"name": "attributes_with_embedded_quotes",
|
||||||
|
"input": "<img src=doesntexist.jpg\"'onerror=\"alert(1) />",
|
||||||
|
"output": "<img src='doesntexist.jpg"'onerror="alert(1)'/>",
|
||||||
|
"rexml": "Ill-formed XHTML!"
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"name": "attributes_with_embedded_quotes_II",
|
||||||
|
"input": "<img src=notthere.jpg\"\"onerror=\"alert(2) />",
|
||||||
|
"output": "<img src='notthere.jpg""onerror="alert(2)'/>",
|
||||||
|
"rexml": "Ill-formed XHTML!"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
Loading…
Add table
Reference in a new issue