Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

rack-taint 1.0.0

  • Loading branch information...
commit 782bffdd1717a5c096eecded4c9c939179563c95 0 parents
@tpope authored
4 .gitignore
@@ -0,0 +1,4 @@
+/.bundle
+/Gemfile.lock
+/bin
+/pkg
2  Gemfile
@@ -0,0 +1,2 @@
+source 'https://rubygems.org'
+gemspec
22 LICENSE
@@ -0,0 +1,22 @@
+Copyright (c) 2012 Tim Pope
+
+MIT License
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29 README.markdown
@@ -0,0 +1,29 @@
+# Rack::Taint
+
+Rack::Taint is Rack middleware to taint the the query string (and thus
+GET parameters), input (and thus POST parameters), headers (and thus
+cookies), and everything else that comes in on a request. Among other
+use cases, this may prove helpful as a component in a [scheme that
+limits mass assignment in Rails][mass assignment scheme].
+
+[mass assignment scheme]: http://jkfill.com/2012/03/10/preventing-mass-assignment-injection-in-rails/
+
+## Installation
+
+Add this line to your application's Gemfile:
+
+ gem 'rack-taint'
+
+On Rails, the Railtie takes care of everything else. On everything
+else, you'll need to add the `Rack::Taint` middleware to the stack
+yourself.
+
+## Contributing
+
+Please follow [Git commit message best practices][practices] when
+submitting a pull request.
+
+[practices]: http://stopwritingramblingcommitmessages.com/
+
+If I provide you with feedback on your pull request, generally you should
+squash your changes into the previous commit when submitting a second request.
8 Rakefile
@@ -0,0 +1,8 @@
+#!/usr/bin/env rake
+require 'bundler/gem_tasks'
+require 'rake/testtask'
+
+Rake::TestTask.new do |t|
+ t.pattern = 'test/**/*_test.rb'
+end
+task :default => :test
5 lib/rack-taint.rb
@@ -0,0 +1,5 @@
+require "rack/taint"
+
+if defined?(Rails::Railtie)
+ require 'rack/taint/railtie'
+end
29 lib/rack/taint.rb
@@ -0,0 +1,29 @@
+module Rack
+ class Taint
+
+ def initialize(app)
+ @app = app
+ end
+
+ def call(env)
+ dup._call(env)
+ end
+
+ def _call(env)
+ env.each do |k, v|
+ v.taint unless k.include?('.')
+ end
+ input = env['rack.input'].taint
+ if input.respond_to?(:string)
+ require 'rack/taint/readable'
+ input.extend(Readable).string.taint
+ end
+ # Some middleware (e.g., Rack::MethodOverride) may cause parameter
+ # parsing before we taint.
+ env.delete('rack.request.form_input')
+ env.delete('rack.request.query_string')
+ @app.call(env)
+ end
+
+ end
+end
9 lib/rack/taint/railtie.rb
@@ -0,0 +1,9 @@
+module Rack
+ class Taint
+ class Railtie < Rails::Railtie
+ initializer 'rack.taint.configure_rails_initialization' do |app|
+ app.middleware.insert_before(Rack::MethodOverride, Rack::Taint)
+ end
+ end
+ end
+end
17 lib/rack/taint/readable.rb
@@ -0,0 +1,17 @@
+module Rack
+ class Taint
+ module Readable
+ %w(getc gets read readpartial read_nonblock readline readlines sysread).each do |method|
+ class_eval <<-ruby, __FILE__, __LINE__.succ
+ def #{method}(*args)
+ if tainted?
+ super.taint
+ else
+ super
+ end
+ end
+ ruby
+ end
+ end
+ end
+end
17 rack-taint.gemspec
@@ -0,0 +1,17 @@
+# -*- encoding: utf-8 -*-
+
+Gem::Specification.new do |gem|
+ gem.authors = ["Tim Pope"]
+ gem.email = ["code@tpope.net"]
+ gem.summary = %q{Rack middleware to taint headers, parameters, and input}
+ gem.homepage = "https://github.com/tpope/rack-taint"
+
+ gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
+ gem.files = `git ls-files`.split("\n")
+ gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
+ gem.name = "rack-taint"
+ gem.require_paths = ["lib"]
+ gem.version = '1.0.0'
+ gem.add_development_dependency 'minitest'
+ gem.add_development_dependency 'rack'
+end
30 test/rack_taint_test.rb
@@ -0,0 +1,30 @@
+require File.expand_path('../test_helper.rb', __FILE__)
+
+class RackTaintTest < MiniTest::Unit::TestCase
+
+ def test_skip_dotted
+ env = app.call(
+ 'QUERY_STRING' => 'a=1&b=2',
+ 'rack.url_scheme' => 'http'
+ )
+ assert_tainted env['QUERY_STRING']
+ refute_tainted env['rack.url_scheme']
+ end
+
+ def test_input
+ env = app.call('rack.input' => StringIO.new)
+ assert_tainted env['rack.input'].read
+ end
+
+ def test_integration
+ require 'rack/request'
+ request = Rack::Request.new(app.call(
+ 'QUERY_STRING' => 'get=1',
+ 'CONTENT_TYPE' => 'application/x-www-form-urlencoded',
+ 'rack.input' => StringIO.new('post[nested][]=2')
+ ))
+ assert_tainted request.GET['get']
+ assert_tainted request.POST['post']['nested'].first
+ end
+
+end
22 test/test_helper.rb
@@ -0,0 +1,22 @@
+require 'bundler'
+Bundler.setup
+
+require 'minitest/autorun'
+require 'stringio'
+require 'rack/taint'
+
+class MiniTest::Unit::TestCase
+ def app
+ Rack::Taint.new(lambda { |env| env })
+ end
+
+ def assert_tainted(obj, msg = nil)
+ msg = message(msg) { "Expected #{mu_pp(obj)} to be tainted" }
+ assert obj.tainted?, msg
+ end
+
+ def refute_tainted(obj, msg = nil)
+ msg = message(msg) { "Expected #{mu_pp(obj)} to not be tainted" }
+ refute obj.tainted?, msg
+ end
+end
Please sign in to comment.
Something went wrong with that request. Please try again.