Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Rack::NoTags
# # # #
# Rack::NoTags removes < and > from all incoming requests
# http://rbjl.net/12-rack-notags
#
# (c) 2009 - 2010 Jan Lelis
# This software is licensed under the MIT license.
# # # #
module Rack
# Sometimes, simple approaches to solve a problem are the best,
# because of the danger, that complex ones have holes...
#
# Usage (Rails)
#
# In config/environment.rb add:
# require 'path/to/rack-notags.rb'
# config.middleware.use Rack::NoTags
#
# You can activate a different filter mode with:
# config.middleware.use Rack::NoTags, :paranoid
class NoTags
PATTERNS = { # replacement => [ array, of, patterns ]
:brackets_only => {
'&lt;' => %w[ < %3C ],
'&gt;' => %w[ > %3E ]
},
# similar to Racks escape_html + url_encoded variants
:valid_xml => {
'&lt;' => %w[ < %3C ],
'&gt;' => %w[ > %3E ],
'&amp;' => %w[ & %26 ],
'&#39;' => %w[ ' %27 ],
'&quot;' => %w[ " %22 ]
},
# encodings which might be interpreted as < or > in some situations
:paranoid => {
'' => %w[ < > %3C %3E ] + [
/&[lg]t;?/i,
/&#0{0,5}6[02];?/,
/&#x0{0,5}3[ce];?/i ]
}
}
def initialize(app, mode = :brackets_only, ignore = {})
@app = app
@patterns = PATTERNS[mode.to_sym] # mode selects the right pattern set
@ignore = ignore # if one entry of the ignore list matches a post param,
# nothing will be filtered
end
def call(env)
# get params in a nice format
post_params = Rack::Utils.parse_query(env['rack.input'].read, "&")
get_params = Rack::Utils.parse_query(env['QUERY_STRING'], "&")
# remove @patterns
unless ignore?(post_params)
post_params = strip_all(post_params)
get_params = strip_all(get_params)
end
# update envirionment
env['rack.input'] = StringIO.new(Rack::Utils.build_query(post_params))
env['QUERY_STRING'] = Rack::Utils.build_query(get_params)
# call framework
@app.call(env)
end
private
# check if param is on ignore list
def ignore?(params)
ret = false
@ignore.each{ |ign_param, ign_value|
params.each{ |param, value|
if !value.is_a?(Array) &&
ign_param.to_s == param.to_s &&
ign_value.to_s == value.to_s
ret = true
end
}
}
ret
end
# applies each 'to-substitute'-pattern to the string
def strip(string)
begin
@patterns.each{ |replacement, patterns|
patterns.each{ |pattern|
string = string.gsub(pattern, replacement)
}
}
end while catch :still_some do
# check if there is still any pattern that needs to be aplied
@patterns.each{ |_, patterns|
patterns.each{ |pattern|
if string[pattern] # like =~ but =~ is not
# defined for two strings
throw :still_some, true
end
}
}
false
end
string
end
# looks at every param-element an sends it to the strip method
def strip_all(params)
ret = {}
params.each{ |param, value|
ret[strip(param)] = value.is_a?(Array) ? value.map{|v|strip(v)} : strip(value)
}
ret
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment