require'erb'require'active_support/core_ext/kernel/singleton_class'classERBmoduleUtilHTML_ESCAPE={'&'=>'&','>'=>'>','<'=>'<','"'=>'"',"'"=>'''}JSON_ESCAPE={'&'=>'\u0026','>'=>'\u003e','<'=>'\u003c',"\u2028"=>'\u2028',"\u2029"=>'\u2029'}HTML_ESCAPE_ONCE_REGEXP=/["><']|&(?!([a-zA-Z]+|(#\d+)|(#[xX][\dA-Fa-f]+));)/JSON_ESCAPE_REGEXP=/[\u2028\u2029&><]/u# A utility method for escaping HTML tag characters.# This method is also aliased as <tt>h</tt>.## In your ERB templates, use this method to escape any unsafe content. For example:# <%= h @person.name %>## puts html_escape('is a > 0 & a < 10?')# # => is a > 0 & a < 10?defhtml_escape(s)unwrapped_html_escape(s).html_safeend# Aliasing twice issues a warning "discarding old...". Remove first to avoid it.remove_method(:h)aliashhtml_escapemodule_function:hsingleton_class.send(:remove_method,:html_escape)module_function:html_escape# HTML escapes strings but doesn't wrap them with an ActiveSupport::SafeBuffer.# This method is not for public consumption! Seriously!defunwrapped_html_escape(s)# :nodoc:s=s.to_sifs.html_safe?selseCGI.escapeHTML(ActiveSupport::Multibyte::Unicode.tidy_bytes(s))endendmodule_function:unwrapped_html_escape# A utility method for escaping HTML without affecting existing escaped entities.## html_escape_once('1 < 2 & 3')# # => "1 < 2 & 3"## html_escape_once('<< Accept & Checkout')# # => "<< Accept & Checkout"defhtml_escape_once(s)result=ActiveSupport::Multibyte::Unicode.tidy_bytes(s.to_s).gsub(HTML_ESCAPE_ONCE_REGEXP,HTML_ESCAPE)s.html_safe??result.html_safe:resultendmodule_function:html_escape_once# A utility method for escaping HTML entities in JSON strings. Specifically, the# &, > and < characters are replaced with their equivalent unicode escaped form -# \u0026, \u003e, and \u003c. The Unicode sequences \u2028 and \u2029 are also# escaped as they are treated as newline characters in some JavaScript engines.# These sequences have identical meaning as the original characters inside the# context of a JSON string, so assuming the input is a valid and well-formed# JSON value, the output will have equivalent meaning when parsed:## json = JSON.generate({ name: "</script><script>alert('PWNED!!!')</script>"})# # => "{\"name\":\"</script><script>alert('PWNED!!!')</script>\"}"## json_escape(json)# # => "{\"name\":\"\\u003C/script\\u003E\\u003Cscript\\u003Ealert('PWNED!!!')\\u003C/script\\u003E\"}"## JSON.parse(json) == JSON.parse(json_escape(json))# # => true## The intended use case for this method is to escape JSON strings before including# them inside a script tag to avoid XSS vulnerability:## <script># var currentUser = <%= raw json_escape(current_user.to_json) %>;# </script>## It is necessary to +raw+ the result of +json_escape+, so that quotation marks# don't get converted to <tt>"</tt> entities. +json_escape+ doesn't# automatically flag the result as HTML safe, since the raw value is unsafe to# use inside HTML attributes.## If your JSON is being used downstream for insertion into the DOM, be aware of# whether or not it is being inserted via +html()+. Most jQuery plugins do this.# If that is the case, be sure to +html_escape+ or +sanitize+ any user-generated# content returned by your JSON.## If you need to output JSON elsewhere in your HTML, you can just do something# like this, as any unsafe characters (including quotation marks) will be# automatically escaped for you:## <div data-user-info="<%= current_user.to_json %>">...</div>## WARNING: this helper only works with valid JSON. Using this on non-JSON values# will open up serious XSS vulnerabilities. For example, if you replace the# +current_user.to_json+ in the example above with user input instead, the browser# will happily eval() that string as JavaScript.## The escaping performed in this method is identical to those performed in the# Active Support JSON encoder when +ActiveSupport.escape_html_entities_in_json+ is# set to true. Because this transformation is idempotent, this helper can be# applied even if +ActiveSupport.escape_html_entities_in_json+ is already true.## Therefore, when you are unsure if +ActiveSupport.escape_html_entities_in_json+# is enabled, or if you are unsure where your JSON string originated from, it# is recommended that you always apply this helper (other libraries, such as the# JSON gem, do not provide this kind of protection by default; also some gems# might override +to_json+ to bypass Active Support's encoder).defjson_escape(s)result=s.to_s.gsub(JSON_ESCAPE_REGEXP,JSON_ESCAPE)s.html_safe??result.html_safe:resultendmodule_function:json_escapeendendclassObjectdefhtml_safe?falseendendclassNumericdefhtml_safe?trueendendmoduleActiveSupport#:nodoc:classSafeBuffer<StringUNSAFE_STRING_METHODS=%w(
capitalize chomp chop delete downcase gsub lstrip next reverse rstrip
slice squeeze strip sub succ swapcase tr tr_s upcase
)alias_method:original_concat,:concatprivate:original_concat# Raised when <tt>ActiveSupport::SafeBuffer#safe_concat</tt> is called on unsafe buffers.classSafeConcatError<StandardErrordefinitializesuper'Could not concatenate to the buffer because it is not html safe.'endenddef[](*args)ifargs.size<2superelseifhtml_safe?new_safe_buffer=superifnew_safe_buffernew_safe_buffer.instance_variable_set:@html_safe,trueendnew_safe_bufferelseto_str[*args]endendenddefsafe_concat(value)raiseSafeConcatErrorunlesshtml_safe?original_concat(value)enddefinitialize(str='')@html_safe=truesuperenddefinitialize_copy(other)super@html_safe=other.html_safe?enddefclone_emptyself[0,0]enddefconcat(value)super(html_escape_interpolated_argument(value))endalias<<concatdefprepend(value)super(html_escape_interpolated_argument(value))enddef+(other)dup.concat(other)enddef%(args)caseargswhenHashescaped_args=Hash[args.map{|k,arg|[k,html_escape_interpolated_argument(arg)]}]elseescaped_args=Array(args).map{|arg|html_escape_interpolated_argument(arg)}endself.class.new(super(escaped_args))enddefhtml_safe?defined?(@html_safe)&&@html_safeenddefto_sselfenddefto_paramto_strenddefencode_with(coder)coder.represent_objectnil,to_strendUNSAFE_STRING_METHODS.eachdo|unsafe_method|ifunsafe_method.respond_to?(unsafe_method)class_eval<<-EOT,__FILE__,__LINE__+1
def #{unsafe_method}(*args, &block) # def capitalize(*args, &block)
to_str.#{unsafe_method}(*args, &block) # to_str.capitalize(*args, &block)
end # end
def #{unsafe_method}!(*args) # def capitalize!(*args)
@html_safe = false # @html_safe = false
super # super
end # end
EOTendendprivatedefhtml_escape_interpolated_argument(arg)(!html_safe?||arg.html_safe?)?arg:CGI.escapeHTML(arg.to_s)endendendclassString# Marks a string as trusted safe. It will be inserted into HTML with no# additional escaping performed. It is your responsibility to ensure that the# string contains no malicious content. This method is equivalent to the# `raw` helper in views. It is recommended that you use `sanitize` instead of# this method. It should never be called on user input.defhtml_safeActiveSupport::SafeBuffer.new(self)endend