From 2f2a95da69f47d4b5e27c8e6da1ecd88d726d93d Mon Sep 17 00:00:00 2001 From: Konstantin Haase Date: Wed, 25 May 2011 11:49:39 +0200 Subject: [PATCH] implement escaped params --- .../lib/rack/protection/escaped_params.rb | 49 ++++++++++++++++++- rack-protection/spec/escaped_params_spec.rb | 29 +++++++++++ 2 files changed, 76 insertions(+), 2 deletions(-) diff --git a/rack-protection/lib/rack/protection/escaped_params.rb b/rack-protection/lib/rack/protection/escaped_params.rb index 17b82f97..c13d1e76 100644 --- a/rack-protection/lib/rack/protection/escaped_params.rb +++ b/rack-protection/lib/rack/protection/escaped_params.rb @@ -1,4 +1,5 @@ require 'rack/protection' +require 'escape_utils' module Rack module Protection @@ -9,10 +10,54 @@ module Rack # # Automatically escapes Rack::Request#params so they can be embedded in HTML # or JavaScript without any further issues. Calls +html_safe+ on the escaped - # strings if defined, to avoid double-escaping in Rails. + # strings if defined, to avoid double-escaping in Rails. It does only escape + # for embedding in HTML and Javascript by default, so you have to take care + # of URLs or SQL injection yourself. # - # Not Yet Implemented! + # Options: + # escape:: What escaping modes to use, should be Symbol or Array of Symbols. + # Available: :html, :javascript, :url, default: [:html, :javascript] class EscapedParams < Base + default_options :escape => [:html, :javascript] + + def initialize(*) + super + modes = Array options[:escape] + code = "def self.escape_string(str) %s end" + modes.each { |m| code %= "EscapeUtils.escape_#{m}(%s)"} + eval code % 'str' + end + + def call(env) + request = Request.new(env) + get_was = handle(request.GET) + post_was = handle(request.POST) rescue nil + app.call env + ensure + request.GET.replace get_was + request.POST.replace post_was if post_was + end + + def handle(hash) + was = hash.dup + hash.replace escape(hash) + was + end + + def escape(object) + case object + when Hash then escape_hash(object) + when Array then object.map { |o| escape(o) } + when String then escape_string(object) + else raise ArgumentError, "cannot escape #{object.inspect}" + end + end + + def escape_hash(hash) + hash = hash.dup + hash.each { |k,v| hash[k] = escape(v) } + hash + end end end end diff --git a/rack-protection/spec/escaped_params_spec.rb b/rack-protection/spec/escaped_params_spec.rb index 9e30837a..e9b92a4a 100644 --- a/rack-protection/spec/escaped_params_spec.rb +++ b/rack-protection/spec/escaped_params_spec.rb @@ -2,4 +2,33 @@ require File.expand_path('../spec_helper.rb', __FILE__) describe Rack::Protection::EscapedParams do it_behaves_like "any rack application" + + context 'escaping' do + it 'escapes html entities' do + mock_app do |env| + request = Rack::Request.new(env) + [200, {'Content-Type' => 'text/plain'}, [request.params['foo']]] + end + get '/', :foo => "" + body.should == '<bar>' + end + + it 'leaves normal params untouched' do + mock_app do |env| + request = Rack::Request.new(env) + [200, {'Content-Type' => 'text/plain'}, [request.params['foo']]] + end + get '/', :foo => "bar" + body.should == 'bar' + end + + it 'copes with nested arrays' do + mock_app do |env| + request = Rack::Request.new(env) + [200, {'Content-Type' => 'text/plain'}, [request.params['foo']['bar']]] + end + get '/', :foo => {:bar => ""} + body.should == '<bar>' + end + end end