Skip to content

Commit

Permalink
Clean JSONP callback
Browse files Browse the repository at this point in the history
* strips any non-word characters: attemps to ensure only valid JavaScript
  function names are returned
* reduces XSS vulnerability
  • Loading branch information
mtodd committed Apr 6, 2011
1 parent 759ab41 commit 67d5660
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 1 deletion.
10 changes: 9 additions & 1 deletion lib/rack/contrib/jsonp.rb
Expand Up @@ -57,7 +57,15 @@ def has_callback?(request)
#
def pad(callback, response, body = "")
response.each{ |s| body << s.to_s }
["#{callback}(#{body})"]
["#{clean(callback)}(#{body})"]
end

# Removes any non-valid characters for a JavaScript function name.
#
# clean("foo<script>alert(1);</script>") #=> "fooscriptalert1script"
#
def clean(callback)
callback.gsub(/\W+/, '')
end

end
Expand Down
35 changes: 35 additions & 0 deletions test/spec_rack_jsonp.rb
Expand Up @@ -52,6 +52,41 @@
headers['Content-Type'].should.equal('application/javascript')
end

context "with XSS vulnerability attempts" do
specify "should clean the callback to include only valid characters for a JavaScript function name" do
test_body = '{"bar":"foo"}'
callback = 'foo<bar>baz()'
callback_cleaned = 'foobarbaz'
app = lambda { |env| [200, {'Content-Type' => 'application/json'}, [test_body]] }
request = Rack::MockRequest.env_for("/", :params => "foo=bar&callback=#{callback}")
body = Rack::JSONP.new(app).call(request).last
body.should.not.include "<script>"
body.should.equal ["#{callback_cleaned}(#{test_body})"]
end

specify "should not include <script> tags in the callback" do
test_body = '{"bar":"foo"}'
callback = 'foo<script>alert(1)</script>'
callback_cleaned = 'fooscriptalert1script'
app = lambda { |env| [200, {'Content-Type' => 'application/json'}, [test_body]] }
request = Rack::MockRequest.env_for("/", :params => "foo=bar&callback=#{callback}")
body = Rack::JSONP.new(app).call(request).last
body.should.not.include "<script>"
body.should.equal ["#{callback_cleaned}(#{test_body})"]
end

specify "should not include multiple statements" do
test_body = '{"bar":"foo"}'
callback = 'foo%3balert(1)//'
callback_cleaned = 'fooalert1'
app = lambda { |env| [200, {'Content-Type' => 'application/json'}, [test_body]] }
request = Rack::MockRequest.env_for("/", :params => "foo=bar&callback=#{callback}")
body = Rack::JSONP.new(app).call(request).last
body.should.not.include "<script>"
body.should.equal ["#{callback_cleaned}(#{test_body})"]
end
end

end

specify "should not change anything if no callback param is provided" do
Expand Down

0 comments on commit 67d5660

Please sign in to comment.