提交 9f8ee9dd 编写于 作者: G Greg Ose

Merge tag 'github24' into authenticity_token_tests

github24
2.0.0-github
......@@ -4,3 +4,4 @@ gem install rdoc -v=4.0.1
gem install sqlite3 -v=1.3.7
gem install rack -v=1.4.5
gem install erubis -v=2.7.0
gem install json -v=1.8.0
......@@ -1332,8 +1332,11 @@ def perform_action
if action_methods.include?(action_name)
send(action_name)
default_render unless performed?
elsif respond_to? :method_missing
method_missing action_name
elsif RUBY_VERSION == "1.9.3" && respond_to?(:method_missing)
method_missing action_name.intern
default_render unless performed?
elsif RUBY_VERSION >= "2.0.0" && respond_to?(:method_missing, true) && !method(:method_missing).private?
method_missing action_name.intern
default_render unless performed?
else
begin
......
......@@ -4,7 +4,7 @@
require 'html/sanitizer'
module HTML #:nodoc:
# A top-level HTMl document. You give it a body of text, and it will parse that
# A top-level HTML document. You give it a body of text, and it will parse that
# text into a tree of nodes.
class Document #:nodoc:
......@@ -48,7 +48,7 @@ def initialize(text, strict=false, xml=false)
end
end
end
# Search the tree for (and return) the first node that matches the given
# conditions. The conditions are interpreted differently for different node
# types, see HTML::Text#find and HTML::Tag#find.
......@@ -62,7 +62,7 @@ def find(conditions)
def find_all(conditions)
@root.find_all(conditions)
end
end
end
require 'strscan'
module HTML #:nodoc:
class Conditions < Hash #:nodoc:
def initialize(hash)
super()
......@@ -18,14 +18,14 @@ def initialize(hash)
hash[k] = Conditions.new(v)
when :children
hash[k] = v = keys_to_symbols(v)
v.each do |k,v2|
case k
v.each do |key,value|
case key
when :count, :greater_than, :less_than
# keys are valid, and require no further processing
when :only
v[k] = Conditions.new(v2)
v[key] = Conditions.new(value)
else
raise "illegal key #{k.inspect} => #{v2.inspect}"
raise "illegal key #{key.inspect} => #{value.inspect}"
end
end
else
......@@ -38,18 +38,14 @@ def initialize(hash)
private
def keys_to_strings(hash)
hash.keys.inject({}) do |h,k|
h[k.to_s] = hash[k]
h
end
Hash[hash.keys.map {|k| [k.to_s, hash[k]]}]
end
def keys_to_symbols(hash)
hash.keys.inject({}) do |h,k|
Hash[hash.keys.map do |k|
raise "illegal key #{k.inspect}" unless k.respond_to?(:to_sym)
h[k.to_sym] = hash[k]
h
end
[k.to_sym, hash[k]]
end]
end
end
......@@ -57,17 +53,17 @@ def keys_to_symbols(hash)
class Node #:nodoc:
# The array of children of this node. Not all nodes have children.
attr_reader :children
# The parent node of this node. All nodes have a parent, except for the
# root node.
attr_reader :parent
# The line number of the input where this node was begun
attr_reader :line
# The byte position in the input where this node was begun
attr_reader :position
# Create a new node as a child of the given parent.
def initialize(parent, line=0, pos=0)
@parent = parent
......@@ -77,9 +73,7 @@ def initialize(parent, line=0, pos=0)
# Return a textual representation of the node.
def to_s
s = ""
@children.each { |child| s << child.to_s }
s
@children.join()
end
# Return false (subclasses must override this to provide specific matching
......@@ -92,7 +86,7 @@ def match(conditions)
# returns non +nil+. Returns the result of the #find call that succeeded.
def find(conditions)
conditions = validate_conditions(conditions)
@children.each do |child|
@children.each do |child|
node = child.find(conditions)
return node if node
end
......@@ -133,7 +127,7 @@ def ==(node)
equivalent
end
class <<self
def parse(parent, line, pos, content, strict=true)
if content !~ /^<\S/
......@@ -160,11 +154,11 @@ def parse(parent, line, pos, content, strict=true)
return CDATA.new(parent, line, pos, scanner.pre_match.gsub(/<!\[CDATA\[/, ''))
end
closing = ( scanner.scan(/\//) ? :close : nil )
return Text.new(parent, line, pos, content) unless name = scanner.scan(/[^\s!>\/]+/)
name.downcase!
unless closing
scanner.skip(/\s*/)
attributes = {}
......@@ -177,6 +171,7 @@ def parse(parent, line, pos, content, strict=true)
case text
when "\\" then
value << text
break if scanner.eos?
value << scanner.getch
when delim
break
......@@ -190,13 +185,13 @@ def parse(parent, line, pos, content, strict=true)
attributes[attr.downcase] = value
scanner.skip(/\s*/)
end
closing = ( scanner.scan(/\//) ? :self : nil )
end
unless scanner.scan(/\s*>/)
if strict
raise "expected > (got #{scanner.rest.inspect} for #{content}, #{attributes.inspect})"
raise "expected > (got #{scanner.rest.inspect} for #{content}, #{attributes.inspect})"
else
# throw away all text until we find what we're looking for
scanner.skip_until(/>/) or scanner.terminate
......@@ -211,9 +206,9 @@ def parse(parent, line, pos, content, strict=true)
# A node that represents text, rather than markup.
class Text < Node #:nodoc:
attr_reader :content
# Creates a new text node as a child of the given parent, with the given
# content.
def initialize(parent, line, pos, content)
......@@ -239,7 +234,7 @@ def to_s
def find(conditions)
match(conditions) && self
end
# Returns non-+nil+ if this node meets the given conditions, or +nil+
# otherwise. See the discussion of #find for the valid conditions.
def match(conditions)
......@@ -267,7 +262,7 @@ def ==(node)
content == node.content
end
end
# A CDATA node is simply a text node with a specialized way of displaying
# itself.
class CDATA < Text #:nodoc:
......@@ -280,16 +275,16 @@ def to_s
# closing tag, or a self-closing tag. It has a name, and may have a hash of
# attributes.
class Tag < Node #:nodoc:
# Either +nil+, <tt>:close</tt>, or <tt>:self</tt>
attr_reader :closing
# Either +nil+, or a hash of attributes for this node.
attr_reader :attributes
# The name of this tag.
attr_reader :name
# Create a new node as a child of the given parent, using the given content
# to describe the node. It will be parsed and the node name, attributes and
# closing status extracted.
......@@ -343,7 +338,7 @@ def find(conditions)
def tag?
true
end
# Returns +true+ if the node meets any of the given conditions. The
# +conditions+ parameter must be a hash of any of the following keys
# (all are optional):
......@@ -403,7 +398,7 @@ def tag?
# node.match :descendant => { :tag => "strong" }
#
# # test if the node has between 2 and 4 span tags as immediate children
# node.match :children => { :count => 2..4, :only => { :tag => "span" } }
# node.match :children => { :count => 2..4, :only => { :tag => "span" } }
#
# # get funky: test to see if the node is a "div", has a "ul" ancestor
# # and an "li" parent (with "class" = "enum"), and whether or not it has
......@@ -438,7 +433,7 @@ def match(conditions)
# test children
return false unless children.find { |child| child.match(conditions[:child]) } if conditions[:child]
# test ancestors
if conditions[:ancestor]
return false unless catch :found do
......@@ -456,13 +451,13 @@ def match(conditions)
child.match(:descendant => conditions[:descendant])
end
end
# count children
if opts = conditions[:children]
matches = children.select do |c|
(c.kind_of?(HTML::Tag) and (c.closing == :self or ! c.childless?))
end
matches = matches.select { |c| c.match(opts[:only]) } if opts[:only]
opts.each do |key, value|
next if key == :only
......@@ -488,24 +483,24 @@ def match(conditions)
self_index = siblings.index(self)
if conditions[:sibling]
return false unless siblings.detect do |s|
return false unless siblings.detect do |s|
s != self && s.match(conditions[:sibling])
end
end
if conditions[:before]
return false unless siblings[self_index+1..-1].detect do |s|
return false unless siblings[self_index+1..-1].detect do |s|
s != self && s.match(conditions[:before])
end
end
if conditions[:after]
return false unless siblings[0,self_index].detect do |s|
return false unless siblings[0,self_index].detect do |s|
s != self && s.match(conditions[:after])
end
end
end
true
end
......@@ -514,7 +509,7 @@ def ==(node)
return false unless closing == node.closing && self.name == node.name
attributes == node.attributes
end
private
# Match the given value to the given condition.
def match_condition(value, condition)
......
require 'set'
require 'cgi'
require 'active_support/core_ext/class/attribute'
module HTML
class Sanitizer
def sanitize(text, options = {})
return text unless sanitizeable?(text)
tokenize(text, options).join
end
def sanitizeable?(text)
!(text.nil? || text.empty? || !text.index("<"))
end
protected
def tokenize(text, options)
tokenizer = HTML::Tokenizer.new(text)
......@@ -19,27 +23,27 @@ def tokenize(text, options)
end
result
end
def process_node(node, result, options)
result << node.to_s
end
end
class FullSanitizer < Sanitizer
def sanitize(text, options = {})
result = super
# strip any comments, and if they have a newline at the end (ie. line with
# only a comment) strip that too
result.gsub!(/<!--(.*?)-->[\n]?/m, "") if result
result = result.gsub(/<!--(.*?)-->[\n]?/m, "") if (result && result =~ /<!--(.*?)-->[\n]?/m)
# Recurse - handle all dirty nested tags
result == text ? result : sanitize(result, options)
end
def process_node(node, result, options)
result << node.to_s if node.class == HTML::Text
end
end
class LinkSanitizer < FullSanitizer
cattr_accessor :included_tags, :instance_writer => false
self.included_tags = Set.new(%w(a href))
......@@ -47,17 +51,17 @@ class LinkSanitizer < FullSanitizer
def sanitizeable?(text)
!(text.nil? || text.empty? || !((text.index("<a") || text.index("<href")) && text.index(">")))
end
protected
def process_node(node, result, options)
result << node.to_s unless node.is_a?(HTML::Tag) && included_tags.include?(node.name)
result << node.to_s unless node.is_a?(HTML::Tag) && included_tags.include?(node.name)
end
end
class WhiteListSanitizer < Sanitizer
[:protocol_separator, :uri_attributes, :allowed_attributes, :allowed_tags, :allowed_protocols, :bad_tags,
:allowed_css_properties, :allowed_css_keywords, :shorthand_css_properties].each do |attr|
class_inheritable_accessor attr, :instance_writer => false
class_attribute attr, :instance_writer => false
end
# A regular expression of the valid characters used to separate protocols like
......@@ -70,28 +74,28 @@ class WhiteListSanitizer < Sanitizer
# Specifies a Set of 'bad' tags that the #sanitize helper will remove completely, as opposed
# to just escaping harmless tags like &lt;font&gt;
self.bad_tags = Set.new(%w(script))
# Specifies the default Set of tags that the #sanitize helper will allow unscathed.
self.allowed_tags = Set.new(%w(strong em b i p code pre tt samp kbd var sub
sup dfn cite big small address hr br div span h1 h2 h3 h4 h5 h6 ul ol li dt dd abbr
self.allowed_tags = Set.new(%w(strong em b i p code pre tt samp kbd var sub
sup dfn cite big small address hr br div span h1 h2 h3 h4 h5 h6 ul ol li dl dt dd abbr
acronym a img blockquote del ins))
# Specifies the default Set of html attributes that the #sanitize helper will leave
# Specifies the default Set of html attributes that the #sanitize helper will leave
# in the allowed tag.
self.allowed_attributes = Set.new(%w(href src width height alt cite datetime title class name xml:lang abbr))
# Specifies the default Set of acceptable css properties that #sanitize and #sanitize_css will accept.
self.allowed_protocols = Set.new(%w(ed2k ftp http https irc mailto news gopher nntp telnet webcal xmpp callto
self.allowed_protocols = Set.new(%w(ed2k ftp http https irc mailto news gopher nntp telnet webcal xmpp callto
feed svn urn aim rsync tag ssh sftp rtsp afs))
# Specifies the default Set of acceptable css keywords that #sanitize and #sanitize_css will accept.
self.allowed_css_properties = Set.new(%w(azimuth background-color border-bottom-color border-collapse
border-color border-left-color border-right-color border-top-color clear color cursor direction display
self.allowed_css_properties = Set.new(%w(azimuth background-color border-bottom-color border-collapse
border-color border-left-color border-right-color border-top-color clear color cursor direction display
elevation float font font-family font-size font-style font-variant font-weight height letter-spacing line-height
overflow pause pause-after pause-before pitch pitch-range richness speak speak-header speak-numeral speak-punctuation
speech-rate stress text-align text-decoration text-indent unicode-bidi vertical-align voice-family volume white-space
width))
# Specifies the default Set of acceptable css keywords that #sanitize and #sanitize_css will accept.
self.allowed_css_keywords = Set.new(%w(auto aqua black block blue bold both bottom brown center
collapse dashed dotted fuchsia gray green !important italic left lime maroon medium none navy normal
......@@ -100,14 +104,14 @@ class WhiteListSanitizer < Sanitizer
# Specifies the default Set of allowed shorthand css properties for the #sanitize and #sanitize_css helpers.
self.shorthand_css_properties = Set.new(%w(background border margin padding))
# Sanitizes a block of css code. Used by #sanitize when it comes across a style attribute
# Sanitizes a block of css code. Used by #sanitize when it comes across a style attribute
def sanitize_css(style)
# disallow urls
style = style.to_s.gsub(/url\s*\(\s*[^\s)]+?\s*\)\s*/, ' ')
# gauntlet
if style !~ /^([:,;#%.\sa-zA-Z0-9!]|\w-\w|\'[\s\w]+\'|\"[\s\w]+\"|\([\d,\s]+\))*$/ ||
style !~ /^(\s*[-\w]+\s*:\s*[^:;]*(;|$)\s*)*$/
if style !~ /\A([:,;#%.\sa-zA-Z0-9!]|\w-\w|\'[\s\w]+\'|\"[\s\w]+\"|\([\d,\s]+\))*\z/ ||
style !~ /\A(\s*[-\w]+\s*:\s*[^:;]*(;|$)\s*)*\z/
return ''
end
......@@ -115,10 +119,10 @@ def sanitize_css(style)
style.scan(/([-\w]+)\s*:\s*([^:;]*)/) do |prop,val|
if allowed_css_properties.include?(prop.downcase)
clean << prop + ': ' + val + ';'
elsif shorthand_css_properties.include?(prop.split('-')[0].downcase)
elsif shorthand_css_properties.include?(prop.split('-')[0].downcase)
unless val.split().any? do |keyword|
!allowed_css_keywords.include?(keyword) &&
keyword !~ /^(#[0-9a-f]+|rgb\(\d+%?,\d*%?,?\d*%?\)?|\d{0,2}\.?\d{0,2}(cm|em|ex|in|mm|pc|pt|px|%|,|\))?)$/
!allowed_css_keywords.include?(keyword) &&
keyword !~ /\A(#[0-9a-f]+|rgb\(\d+%?,\d*%?,?\d*%?\)?|\d{0,2}\.?\d{0,2}(cm|em|ex|in|mm|pc|pt|px|%|,|\))?)\z/
end
clean << prop + ': ' + val + ';'
end
......@@ -143,7 +147,7 @@ def process_node(node, result, options)
else
options[:parent].unshift node.name
end
process_attributes_for node, options
options[:tags].include?(node.name) ? node : nil
......@@ -151,7 +155,7 @@ def process_node(node, result, options)
bad_tags.include?(options[:parent].first) ? nil : node.to_s.gsub(/</, "&lt;")
end
end
def process_attributes_for(node, options)
return unless node.attributes
node.attributes.keys.each do |attr_name|
......
......@@ -128,6 +128,8 @@ module HTML
# (no parent element).
# * <tt>:empty</tt> -- Match the element only if it has no child elements,
# and no text content.
# * <tt>:content(string)</tt> -- Match the element only if it has <tt>string</tt>
# as its text content (ignoring leading and trailing whitespace).
# * <tt>:only-child</tt> -- Match the element if it is the only child (element)
# of its parent element.
# * <tt>:only-of-type</tt> -- Match the element if it is the only child (element)
......@@ -182,7 +184,7 @@ module HTML
# not another using <tt>:not</tt>. For example:
# p:not(.post)
# Matches all paragraphs that do not have the class <tt>.post</tt>.
#
#
# === Substitution Values
#
# You can use substitution with identifiers, class names and element values.
......
require 'strscan'
module HTML #:nodoc:
# A simple HTML tokenizer. It simply breaks a stream of text into tokens, where each
# token is a string. Each string represents either "text", or an HTML element.
#
......@@ -14,15 +14,16 @@ module HTML #:nodoc:
# p token
# end
class Tokenizer #:nodoc:
# The current (byte) position in the text
attr_reader :position
# The current line number
attr_reader :line
# Create a new Tokenizer for the given text.
def initialize(text)
text.encode! if text.encoding_aware?
@scanner = StringScanner.new(text)
@position = 0
@line = 0
......@@ -41,7 +42,7 @@ def next
update_current_line(scan_text)
end
end
private
# Treat the text at the current position as a tag, and scan it. Supports
......@@ -68,13 +69,13 @@ def scan_tag
def scan_text
"#{@scanner.getch}#{@scanner.scan(/[^<]*/)}"
end
# Counts the number of newlines in the text and updates the current line
# accordingly.
def update_current_line(text)
text.scan(/\r?\n/) { @current_line += 1 }
end
# Skips over quoted strings, so that less-than and greater-than characters
# within the strings are ignored.
def consume_quoted_regions
......@@ -95,11 +96,12 @@ def consume_quoted_regions
while match = @scanner.scan_until(/[\\#{delim}]/)
text << match
break if @scanner.matched == delim
break if @scanner.eos?
text << @scanner.getch # skip the escaped character
end
end
text
end
end
end
......@@ -277,7 +277,9 @@ def form_for(record_or_name_or_array, *args, &proc)
apply_form_for_options!([object], options)
args.unshift object
end
options[:html] ||= {}
options[:html][:authenticity_token] = options.delete(:authenticity_token)
concat(form_tag(options.delete(:url) || {}, options.delete(:html) || {}))
fields_for(object_name, *(args << options), &proc)
concat('</form>'.html_safe)
......
......@@ -449,16 +449,18 @@ def html_options_for_form(url_for_options, options, *parameters_for_url)
end
def extra_tags_for_form(html_options)
authenticity_token = html_options.delete("authenticity_token")
case method = html_options.delete("method").to_s
when /^get$/i # must be case-insentive, but can't use downcase as might be nil
html_options["method"] = "get"
''
when /^post$/i, "", nil
html_options["method"] = "post"
protect_against_forgery? ? content_tag(:div, token_tag, :style => 'margin:0;padding:0;display:inline') : ''
protect_against_forgery? && authenticity_token != false ? content_tag(:div, token_tag(authenticity_token), :style => 'margin:0;padding:0;display:inline') : ''
else
html_options["method"] = "post"
content_tag(:div, tag(:input, :type => "hidden", :name => "_method", :value => method) + token_tag, :style => 'margin:0;padding:0;display:inline')
content_tag(:div, tag(:input, :type => "hidden", :name => "_method", :value => method) + token_tag(authenticity_token), :style => 'margin:0;padding:0;display:inline')
end
end
......@@ -474,11 +476,12 @@ def form_tag_in_block(html_options, &block)
concat("</form>".html_safe)
end
def token_tag
unless protect_against_forgery?
''
def token_tag(token=nil)
if token != false && protect_against_forgery?
token ||= form_authenticity_token
tag(:input, type: "hidden", name: request_forgery_protection_token.to_s, value: token)
else
tag(:input, :type => "hidden", :name => request_forgery_protection_token.to_s, :value => form_authenticity_token)
''
end
end
......
......@@ -151,6 +151,7 @@ def test_method_missing_is_not_an_action_name
def test_get_on_hidden_should_fail
use_controller NonEmptyController
get :hidden_action
assert_response 404
......
......@@ -9,11 +9,11 @@ def setup
end
def test_filter_parameters
assert FilterParamController.respond_to?(:filter_parameter_logging)
assert !@controller.respond_to?(:filter_parameters)
assert FilterParamController.respond_to?(:filter_parameter_logging, true)
assert !@controller.respond_to?(:filter_parameters, true)
FilterParamController.filter_parameter_logging
assert @controller.respond_to?(:filter_parameters)
assert @controller.respond_to?(:filter_parameters, true)
test_hashes = [[{},{},[]],
[{'foo'=>nil},{'foo'=>nil},[]],
......
......@@ -15,7 +15,7 @@ def test_handle_doctype
assert_match %r{\s+}m, doc.root.children[1].content
assert_equal "html", doc.root.children[2].name
end
def test_find_img
doc = HTML::Document.new <<-HTML.strip
<html>
......@@ -123,7 +123,7 @@ def test_find_empty_tag
def test_parse_invalid_document
assert_nothing_raised do
doc = HTML::Document.new("<html>
HTML::Document.new("<html>
<table>
<tr>
<td style=\"color: #FFFFFF; height: 17px; onclick=\"window.location.href='http://www.rmeinc.com/about_rme.aspx'\" style=\"cursor:pointer; height: 17px;\"; nowrap onclick=\"window.location.href='http://www.rmeinc.com/about_rme.aspx'\" onmouseout=\"this.bgColor='#0066cc'; this.style.color='#FFFFFF'\" onmouseover=\"this.bgColor='#ffffff'; this.style.color='#0033cc'\">About Us</td>
......@@ -135,7 +135,7 @@ def test_parse_invalid_document
def test_invalid_document_raises_exception_when_strict
assert_raise RuntimeError do
doc = HTML::Document.new("<html>
HTML::Document.new("<html>
<table>
<tr>
<td style=\"color: #FFFFFF; height: 17px; onclick=\"window.location.href='http://www.rmeinc.com/about_rme.aspx'\" style=\"cursor:pointer; height: 17px;\"; nowrap onclick=\"window.location.href='http://www.rmeinc.com/about_rme.aspx'\" onmouseout=\"this.bgColor='#0066cc'; this.style.color='#FFFFFF'\" onmouseover=\"this.bgColor='#ffffff'; this.style.color='#0033cc'\">About Us</td>
......
require 'abstract_unit'
class NodeTest < Test::Unit::TestCase
class MockNode
def initialize(matched, value)
@matched = matched
@value = value
end
def find(conditions)
@matched && self
end
def to_s
@value.to_s
end
end
def setup
@node = HTML::Node.new("parent")
@node.children.concat [MockNode.new(false,1), MockNode.new(true,"two"), MockNode.new(false,:three)]
end
def test_match
assert !@node.match("foo")
end
def test_tag
assert !@node.tag?
end
def test_to_s
assert_equal "1twothree", @node.to_s
end
def test_find
assert_equal "two", @node.find('blah').to_s
end
......@@ -58,7 +58,7 @@ def test_to_s_with_boolean_attrs
assert node.attributes.has_key?("bar")
assert "<b foo bar>", node.to_s
end
def test_parse_with_unclosed_tag
s = "<span onmouseover='bang'"
node = nil
......
......@@ -26,17 +26,17 @@ def test_strip_tags
assert_equal "This has a here.", sanitizer.sanitize("This has a <!-- comment --> here.")
assert_equal "This has a here.", sanitizer.sanitize("This has a <![CDATA[<section>]]> here.")
assert_equal "This has an unclosed ", sanitizer.sanitize("This has an unclosed <![CDATA[<section>]] here...")
assert_equal "non printable char is a tag", sanitizer.sanitize("<\x07a href='/hello'>non printable char is a tag</a>")
[nil, '', ' '].each { |blank| assert_equal blank, sanitizer.sanitize(blank) }
assert_nothing_raised { sanitizer.sanitize("This is a frozen string with no tags".freeze) }
end
def test_strip_links
sanitizer = HTML::LinkSanitizer.new
assert_equal "Dont touch me", sanitizer.sanitize("Dont touch me")
assert_equal "Dont touch me", sanitizer.sanitize("Dont touch me")
assert_equal "on my mind\nall day long", sanitizer.sanitize("<a href='almost'>on my mind</a>\n<A href='almost'>all day long</A>")
assert_equal "0wn3d", sanitizer.sanitize("<a href='http://www.rubyonrails.com/'><a href='http://www.rubyonrails.com/' onlclick='steal()'>0wn3d</a></a>")
assert_equal "Magic", sanitizer.sanitize("<a href='http://www.rubyonrails.com/'>Mag<a href='http://www.ruby-lang.org/'>ic")
assert_equal "FrrFox", sanitizer.sanitize("<href onlclick='steal()'>FrrFox</a></href>")
assert_equal "0wn3d", sanitizer.sanitize("<a href='http://www.rubyonrails.com/'><a href='http://www.rubyonrails.com/' onlclick='steal()'>0wn3d</a></a>")
assert_equal "Magic", sanitizer.sanitize("<a href='http://www.rubyonrails.com/'>Mag<a href='http://www.ruby-lang.org/'>ic")
assert_equal "FrrFox", sanitizer.sanitize("<href onlclick='steal()'>FrrFox</a></href>")
assert_equal "My mind\nall <b>day</b> long", sanitizer.sanitize("<a href='almost'>My mind</a>\n<A href='almost'>all <b>day</b> long</A>")
assert_equal "all <b>day</b> long", sanitizer.sanitize("<<a>a href='hello'>all <b>day</b> long<</A>/a>")
......@@ -56,7 +56,7 @@ def test_sanitize_script
assert_sanitized "a b c<script language=\"Javascript\">blah blah blah</script>d e f", "a b cd e f"
end
# fucked
# TODO: Clean up
def test_sanitize_js_handlers
raw = %{onthis="do that" <a href="#" onclick="hello" name="foo" onbogus="remove me">hello</a>}
assert_sanitized raw, %{onthis="do that" <a name="foo" href="#">hello</a>}
......@@ -66,7 +66,7 @@ def test_sanitize_javascript_href
raw = %{href="javascript:bang" <a href="javascript:bang" name="hello">foo</a>, <span href="javascript:bang">bar</span>}
assert_sanitized raw, %{href="javascript:bang" <a name="hello">foo</a>, <span>bar</span>}
end
def test_sanitize_image_src
raw = %{src="javascript:bang" <img src="javascript:bang" width="5">foo</img>, <span src="javascript:bang">bar</span>}
assert_sanitized raw, %{src="javascript:bang" <img width="5">foo</img>, <span>bar</span>}
......@@ -138,6 +138,13 @@ def test_should_flag_bad_protocols
assert sanitizer.send(:contains_bad_protocols?, 'src', "#{proto}://bad")
end
end
def test_should_accept_good_protocols_ignoring_case
sanitizer = HTML::WhiteListSanitizer.new
HTML::WhiteListSanitizer.allowed_protocols.each do |proto|
assert !sanitizer.send(:contains_bad_protocols?, 'src', "#{proto.capitalize}://good")
end
end
def test_should_accept_good_protocols
sanitizer = HTML::WhiteListSanitizer.new
......@@ -155,9 +162,9 @@ def test_should_block_script_tag
assert_sanitized %(<SCRIPT\nSRC=http://ha.ckers.org/xss.js></SCRIPT>), ""
end
[%(<IMG SRC="javascript:alert('XSS');">),
%(<IMG SRC=javascript:alert('XSS')>),
%(<IMG SRC=JaVaScRiPt:alert('XSS')>),
[%(<IMG SRC="javascript:alert('XSS');">),
%(<IMG SRC=javascript:alert('XSS')>),
%(<IMG SRC=JaVaScRiPt:alert('XSS')>),
%(<IMG """><SCRIPT>alert("XSS")</SCRIPT>">),
%(<IMG SRC=javascript:alert(&quot;XSS&quot;)>),
%(<IMG SRC=javascript:alert(String.fromCharCode(88,83,83))>),
......@@ -175,36 +182,36 @@ def test_should_block_script_tag
assert_sanitized img_hack, "<img>"
end
end
def test_should_sanitize_tag_broken_up_by_null
assert_sanitized %(<SCR\0IPT>alert(\"XSS\")</SCR\0IPT>), "alert(\"XSS\")"
end
def test_should_sanitize_invalid_script_tag
assert_sanitized %(<SCRIPT/XSS SRC="http://ha.ckers.org/xss.js"></SCRIPT>), ""
end
def test_should_sanitize_script_tag_with_multiple_open_brackets
assert_sanitized %(<<SCRIPT>alert("XSS");//<</SCRIPT>), "&lt;"
assert_sanitized %(<iframe src=http://ha.ckers.org/scriptlet.html\n<a), %(&lt;a)
end
def test_should_sanitize_unclosed_script
assert_sanitized %(<SCRIPT SRC=http://ha.ckers.org/xss.js?<B>), "<b>"
end
def test_should_sanitize_half_open_scripts
assert_sanitized %(<IMG SRC="javascript:alert('XSS')"), "<img>"
end
def test_should_not_fall_for_ridiculous_hack
img_hack = %(<IMG\nSRC\n=\n"\nj\na\nv\na\ns\nc\nr\ni\np\nt\n:\na\nl\ne\nr\nt\n(\n'\nX\nS\nS\n'\n)\n"\n>)
assert_sanitized img_hack, "<img>"
end
# fucked
# TODO: Clean up
def test_should_sanitize_attributes
assert_sanitized %(<SPAN title="'><script>alert()</script>">blah</SPAN>), %(<span title="'&gt;&lt;script&gt;alert()&lt;/script&gt;">blah</span>)
assert_sanitized %(<SPAN title="'><script>alert()</script>">blah</SPAN>), %(<span title="#{CGI.escapeHTML "'><script>alert()</script>"}">blah</span>)
end
def test_should_sanitize_illegal_style_properties
......@@ -223,15 +230,15 @@ def test_should_sanitize_xul_style_attributes
raw = %(-moz-binding:url('http://ha.ckers.org/xssmoz.xml#xss'))
assert_equal '', sanitize_css(raw)
end
def test_should_sanitize_invalid_tag_names
assert_sanitized(%(a b c<script/XSS src="http://ha.ckers.org/xss.js"></script>d e f), "a b cd e f")
end
def test_should_sanitize_non_alpha_and_non_digit_characters_in_tags
assert_sanitized('<a onclick!#$%&()*~+-_.,:;?@[/|\]^`=alert("XSS")>foo</a>', "<a>foo</a>")
end
def test_should_sanitize_invalid_tag_names_in_single_tags
assert_sanitized('<img/src="http://ha.ckers.org/xss.js"/>', "<img />")
end
......@@ -250,6 +257,11 @@ def test_should_sanitize_div_style_expression
assert_equal '', sanitize_css(raw)
end
def test_should_sanitize_across_newlines
raw = %(\nwidth:\nexpression(alert('XSS'));\n)
assert_equal '', sanitize_css(raw)
end
def test_should_sanitize_img_vbscript
assert_sanitized %(<img src='vbscript:msgbox("XSS")' />), '<img />'
end
......@@ -267,7 +279,6 @@ def test_should_not_mangle_urls_with_ampersand
end
def test_should_sanitize_neverending_attribute
failed_pre_200
assert_sanitized "<span class=\"\\", "<span class=\"\\\">"
end
......
......@@ -7,7 +7,7 @@ def test_open_without_attributes
assert_equal Hash.new, node.attributes
assert_nil node.closing
end
def test_open_with_attributes
node = tag("<TAG1 foo=hey_ho x:bar=\"blah blah\" BAZ='blah blah blah' >")
assert_equal "tag1", node.name
......@@ -15,28 +15,28 @@ def test_open_with_attributes
assert_equal "blah blah", node["x:bar"]
assert_equal "blah blah blah", node["baz"]
end
def test_self_closing_without_attributes
node = tag("<tag/>")
assert_equal "tag", node.name
assert_equal Hash.new, node.attributes
assert_equal :self, node.closing
end
def test_self_closing_with_attributes
node = tag("<tag a=b/>")
assert_equal "tag", node.name
assert_equal( { "a" => "b" }, node.attributes )
assert_equal :self, node.closing
end
def test_closing_without_attributes
node = tag("</tag>")
assert_equal "tag", node.name
assert_nil node.attributes
assert_equal :close, node.closing
end
def test_bracket_op_when_no_attributes
node = tag("</tag>")
assert_nil node["foo"]
......@@ -46,27 +46,32 @@ def test_bracket_op_when_attributes
node = tag("<tag a=b/>")
assert_equal "b", node["a"]
end
def test_attributes_with_escaped_quotes
node = tag("<tag a='b\\'c' b=\"bob \\\"float\\\"\">")
assert_equal "b\\'c", node["a"]
assert_equal "bob \\\"float\\\"", node["b"]
end
def test_to_s
node = tag("<a b=c d='f' g=\"h 'i'\" />")
assert_equal %(<a b='c' d='f' g='h \\'i\\'' />), node.to_s
node = node.to_s
assert node.include?('a')
assert node.include?('b="c"')
assert node.include?('d="f"')
assert node.include?('g="h')
assert node.include?('i')
end
def test_tag
assert tag("<tag>").tag?
end
def test_match_tag_as_string
assert tag("<tag>").match(:tag => "tag")
assert !tag("<tag>").match(:tag => "b")
end
def test_match_tag_as_regexp
assert tag("<tag>").match(:tag => /t.g/)
assert !tag("<tag>").match(:tag => /t[bqs]g/)
......@@ -77,45 +82,45 @@ def test_match_attributes_as_string
assert t.match(:attributes => {"a" => "something"})
assert t.match(:attributes => {"b" => "else"})
end
def test_match_attributes_as_regexp
t = tag("<tag a=something b=else />")
assert t.match(:attributes => {"a" => /^something$/})
assert t.match(:attributes => {"b" => /e.*e/})
assert t.match(:attributes => {"a" => /me..i/, "b" => /.ls.$/})
end
def test_match_attributes_as_number
t = tag("<tag a=15 b=3.1415 />")
assert t.match(:attributes => {"a" => 15})
assert t.match(:attributes => {"b" => 3.1415})
assert t.match(:attributes => {"a" => 15, "b" => 3.1415})
end
def test_match_attributes_exist
t = tag("<tag a=15 b=3.1415 />")
assert t.match(:attributes => {"a" => true})
assert t.match(:attributes => {"b" => true})
assert t.match(:attributes => {"a" => true, "b" => true})
end
def test_match_attributes_not_exist
t = tag("<tag a=15 b=3.1415 />")
assert t.match(:attributes => {"c" => false})
assert t.match(:attributes => {"c" => nil})
assert t.match(:attributes => {"a" => true, "c" => false})
end
def test_match_parent_success
t = tag("<tag a=15 b='hello'>", tag("<foo k='value'>"))
assert t.match(:parent => {:tag => "foo", :attributes => {"k" => /v.l/, "j" => false}})
end
def test_match_parent_fail
t = tag("<tag a=15 b='hello'>", tag("<foo k='value'>"))
assert !t.match(:parent => {:tag => /kafka/})
end
def test_match_child_success
t = tag("<tag x:k='something'>")
tag("<child v=john a=kelly>", t)
......@@ -123,7 +128,7 @@ def test_match_child_success
assert t.match(:child => { :tag => "sib", :attributes => {"v" => /j/}})
assert t.match(:child => { :attributes => {"a" => "kelly"}})
end
def test_match_child_fail
t = tag("<tag x:k='something'>")
tag("<child v=john a=kelly>", t)
......@@ -131,13 +136,13 @@ def test_match_child_fail
assert !t.match(:child => { :tag => "sib", :attributes => {"v" => /r/}})
assert !t.match(:child => { :attributes => {"v" => false}})
end
def test_match_ancestor_success
t = tag("<tag x:k='something'>", tag("<parent v=john a=kelly>", tag("<grandparent m=vaughn v=james>")))
assert t.match(:ancestor => {:tag => "parent", :attributes => {"a" => /ll/}})
assert t.match(:ancestor => {:attributes => {"m" => "vaughn"}})
end
def test_match_ancestor_fail
t = tag("<tag x:k='something'>", tag("<parent v=john a=kelly>", tag("<grandparent m=vaughn v=james>")))
assert !t.match(:ancestor => {:tag => /^parent/, :attributes => {"v" => /m/}})
......@@ -149,13 +154,13 @@ def test_match_descendant_success
assert t.match(:descendant => {:tag => "child", :attributes => {"a" => /ll/}})
assert t.match(:descendant => {:attributes => {"m" => "vaughn"}})
end
def test_match_descendant_fail
tag("<grandchild m=vaughn v=james>", tag("<child v=john a=kelly>", t = tag("<tag x:k='something'>")))
assert !t.match(:descendant => {:tag => /^child/, :attributes => {"v" => /m/}})
assert !t.match(:descendant => {:attributes => {"v" => false}})
end
def test_match_child_count
t = tag("<tag x:k='something'>")
tag("hello", t)
......@@ -221,7 +226,7 @@ def test_match_sibling_after
assert !m.match(:after => {:tag => "span", :attributes => {:k => true}})
end
def test_to_s
def test_tag_to_s
t = tag("<b x='foo'>")
tag("hello", t)
tag("<hr />", t)
......@@ -229,7 +234,7 @@ def test_to_s
end
private
def tag(content, parent=nil)
node = HTML::Node.parse(parent,0,0,content)
parent.children << node if parent
......
......@@ -4,27 +4,27 @@ class TextNodeTest < Test::Unit::TestCase
def setup
@node = HTML::Text.new(nil, 0, 0, "hello, howdy, aloha, annyeong")
end
def test_to_s
assert_equal "hello, howdy, aloha, annyeong", @node.to_s
end
def test_find_string
assert_equal @node, @node.find("hello, howdy, aloha, annyeong")
assert_equal false, @node.find("bogus")
end
def test_find_regexp
assert_equal @node, @node.find(/an+y/)
assert_nil @node.find(/b/)
end
def test_find_hash
assert_equal @node, @node.find(:content => /howdy/)
assert_nil @node.find(:content => /^howdy$/)
assert_equal false, @node.find(:content => "howdy")
end
def test_find_other
assert_nil @node.find(:hello)
end
......
......@@ -29,7 +29,7 @@ def test_tag_simple_closing
tokenize "</tag>"
assert_next "</tag>"
end
def test_tag_with_single_quoted_attribute
tokenize %{<tag a='hello'>x}
assert_next %{<tag a='hello'>}
......@@ -49,7 +49,7 @@ def test_tag_with_double_quoted_attribute_with_escape
tokenize %{<tag a="hello\\"">x}
assert_next %{<tag a="hello\\"">}
end
def test_tag_with_unquoted_attribute
tokenize %{<tag a=hello>x}
assert_next %{<tag a=hello>}
......@@ -59,12 +59,12 @@ def test_tag_with_lt_char_in_attribute
tokenize %{<tag a="x < y">x}
assert_next %{<tag a="x < y">}
end
def test_tag_with_gt_char_in_attribute
tokenize %{<tag a="x > y">x}
assert_next %{<tag a="x > y">}
end
def test_doctype_tag
tokenize %{<!DOCTYPE "blah" "blah" "blah">\n <html>}
assert_next %{<!DOCTYPE "blah" "blah" "blah">}
......@@ -90,7 +90,7 @@ def test_less_than_with_space
assert_next %{original }
assert_next %{< hello > world}
end
def test_less_than_without_matching_greater_than
tokenize %{hello <span onmouseover="gotcha"\n<b>foo</b>\nbar</span>}
assert_next %{hello }
......@@ -109,22 +109,22 @@ def test_unterminated_comment
assert_next %{<!-- neverending...}
assert_end
end
private
def tokenize(text)
@tokenizer = HTML::Tokenizer.new(text)
end
def assert_next(expected, message=nil)
token = @tokenizer.next
assert_equal expected, token, message
end
def assert_sequence(*expected)
assert_next expected.shift until expected.empty?
end
def assert_end(message=nil)
assert_nil @tokenizer.next, message
end
......
......@@ -240,7 +240,7 @@ def test_named_routes
# We need to create a new class in order to install the new named route.
kls = Class.new { include ActionController::UrlWriter }
controller = kls.new
assert controller.respond_to?(:home_url)
assert controller.respond_to?(:home_url, true)
assert_equal 'http://www.basecamphq.com/home/sweet/home/again',
controller.send(:home_url, :host => 'www.basecamphq.com', :user => 'again')
......@@ -278,7 +278,7 @@ def test_only_path
# We need to create a new class in order to install the new named route.
kls = Class.new { include ActionController::UrlWriter }
controller = kls.new
assert controller.respond_to?(:home_url)
assert controller.respond_to?(:home_url, true)
assert_equal '/brave/new/world',
controller.send(:url_for, :controller => 'brave', :action => 'new', :id => 'world', :only_path => true)
......
......@@ -2920,8 +2920,10 @@ def assign_attributes(attributes={})
attributes.each do |k, v|
if k.to_s.include?("(")
multiparameter_attributes << [ k, v ]
else
elsif RUBY_VERSION == "1.9.3"
respond_to?(:"#{k}=") ? send(:"#{k}=", v) : raise(UnknownAttributeError, "unknown attribute: #{k}")
else
(respond_to?(:"#{k}=", true) && !method(:"#{k}=").private?) ? send(:"#{k}=", v) : raise(UnknownAttributeError, "unknown attribute: #{k}")
end
end
......
......@@ -52,9 +52,9 @@ def test_delegates_finds_and_calculations_to_the_base_class
end
def test_scope_should_respond_to_own_methods_and_methods_of_the_proxy
assert Topic.approved.respond_to?(:proxy_found)
assert Topic.approved.respond_to?(:count)
assert Topic.approved.respond_to?(:length)
assert Topic.approved.respond_to?(:proxy_found, true)
assert Topic.approved.respond_to?(:count, true)
assert Topic.approved.respond_to?(:length, true)
end
def test_respond_to_respects_include_private_parameter
......
......@@ -17,7 +17,7 @@ module Hash #:nodoc:
module Slice
# Returns a new hash with only the given keys.
def slice(*keys)
keys = keys.map! { |key| convert_key(key) } if respond_to?(:convert_key, true)
keys = keys.map { |key| convert_key(key) } if respond_to?(:convert_key, true)
hash = self.class.new
keys.each { |k| hash[k] = self[k] if has_key?(k) }
hash
......@@ -27,7 +27,7 @@ def slice(*keys)
# Returns a hash contained the removed key/value pairs
# {:a => 1, :b => 2, :c => 3, :d => 4}.slice!(:a, :b) # => {:c => 3, :d =>4}
def slice!(*keys)
keys = keys.map! { |key| convert_key(key) } if respond_to?(:convert_key, true)
keys = keys.map { |key| convert_key(key) } if respond_to?(:convert_key, true)
omit = slice(*(self.keys - keys))
hash = slice(*keys)
replace(hash)
......
......@@ -4,29 +4,11 @@ module String #:nodoc:
# Additional string tests.
module StartsEndsWith
def self.append_features(base)
if '1.8.7 and up'.respond_to?(:start_with?)
base.class_eval do
alias_method :starts_with?, :start_with?
alias_method :ends_with?, :end_with?
end
else
super
base.class_eval do
alias_method :start_with?, :starts_with?
alias_method :end_with?, :ends_with?
end
base.class_eval do
alias_method :starts_with?, :start_with?
alias_method :ends_with?, :end_with?
end
end
# Does the string start with the specified +prefix+?
def starts_with?(prefix)
prefix.respond_to?(:to_str) && self[0, prefix.length] == prefix
end
# Does the string end with the specified +suffix+?
def ends_with?(suffix)
suffix.respond_to?(:to_str) && self[-suffix.length, suffix.length] == suffix
end
end
end
end
......
......@@ -210,7 +210,7 @@ def interpolate_lambda?(object, string, key)
# for all other file extensions.
def load_file(filename)
type = File.extname(filename).tr('.', '').downcase
raise UnknownFileType.new(type, filename) unless respond_to?(:"load_#{type}")
raise UnknownFileType.new(type, filename) unless respond_to?(:"load_#{type}", true)
data = send(:"load_#{type}", filename) # TODO raise a meaningful exception if this does not yield a Hash
data.each { |locale, d| store_translations(locale, d) }
end
......
......@@ -184,7 +184,6 @@ def test_starts_ends_with_alias
s = "hello"
assert s.starts_with?('h')
assert s.starts_with?('hel')
assert !s.starts_with?(:hel)
assert !s.starts_with?('el')
assert s.start_with?('h')
......@@ -193,7 +192,6 @@ def test_starts_ends_with_alias
assert s.ends_with?('o')
assert s.ends_with?('lo')
assert !s.ends_with?(:lo)
assert !s.ends_with?('el')
assert s.end_with?('o')
......
require 'rails/vendor_gem_source_index'
module Gem
def self.source_index=(index)
@@source_index = index
def self.source_index
Gem::Specification
end
def self.source_index_search(dep)
Gem::Specification.select { |spec|
dep.matches_spec?(spec)
}
end
end
......@@ -101,7 +107,7 @@ def specification
# code repeated from Gem.activate. Find a matching spec, or the currently loaded version.
# error out if loaded version and requested version are incompatible.
@spec ||= begin
matches = Gem.source_index.search(self)
matches = Gem.source_index_search(self)
matches << @@framework_gems[name] if framework_gem?
if Gem.loaded_specs[name] then
# This gem is already loaded. If the currently loaded gem is not in the
......
......@@ -220,6 +220,7 @@ def banner
"Usage: #{$0} #{spec.name} #{spec.name.camelize}Name [options]"
end
public
def attributes
@attributes ||= @args.collect do |attribute|
Rails::Generator::GeneratedAttribute.new(*attribute.split(":"))
......
......@@ -209,7 +209,7 @@ class GemGeneratorSource < AbstractGemSource
# Yield latest versions of generator gems.
def each
dependency = Gem::Dependency.new(/_generator$/, Gem::Requirement.default)
Gem.source_index.search(dependency).inject({}) { |latest, gem|
Gem.source_index_search(dependency).inject({}) { |latest, gem|
hem = latest[gem.name]
latest[gem.name] = gem if hem.nil? or gem.version > hem.version
latest
......@@ -231,17 +231,14 @@ def each
private
def generator_full_paths
@generator_full_paths ||=
Gem.source_index.inject({}) do |latest, name_gem|
name, gem = name_gem
hem = latest[gem.name]
latest[gem.name] = gem if hem.nil? or gem.version > hem.version
latest
end.values.inject([]) do |mem, gem|
Gem::Specification.each_with_object({}) { |g, latest|
hem = latest[g.name]
latest[g.name] = g if hem.nil? or g.version > hem.version
}.values.each_with_object([]) { |gem, mem|
Dir[gem.full_gem_path + '/{rails_,}generators/**/*_generator.rb'].each do |generator|
mem << generator
end
mem
end
}
end
end
......
......@@ -3,8 +3,10 @@
set -x
set -e
RUBY_VERSION="$(cat .ruby-version)"
# Use the right Ruby /without/ rbenv
ruby_dir=$(dirname "$(RBENV_VERSION=1.9.3-p231-tcs-github rbenv which ruby)")
ruby_dir=$(dirname "$(RBENV_VERSION=$RUBY_VERSION rbenv which ruby)")
export PATH=$ruby_dir:$PATH
# Announce who we are
......@@ -13,7 +15,7 @@ ruby -v
# Because Rails 2.3 doesn't use Bundler for its own tests,
# we need to setup an isolated gem environment.
gem_environment_version="$(md5sum Gemfile.sh | cut -d' ' -f1)"
gem_environment_version="$(md5sum Gemfile.sh | cut -d' ' -f1)-$(ruby -e 'print RUBY_VERSION')"
gem_dir=`pwd`/.gem
gem_pristine_dir=${gem_dir}-${gem_environment_version}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册