Permalink
Browse files

Add trustable tests

  • Loading branch information...
1 parent e64213e commit fb7a2878caa90ca44e7e730d770f076fa03cb543 @unders committed Mar 22, 2012
View
@@ -0,0 +1,8 @@
+# A sample Guardfile
+# More info at https://github.com/guard/guard#readme
+#
+guard 'minitest' do
+ watch(%r|^spec/(.*)_spec\.rb|)
+ watch(%r{^lib/(.*/)?([^/]+)\.rb$}) { |m| "spec/#{m[1]}#{m[2]}_spec.rb" }
+ watch(%r|^spec/minitest_helper\.rb|) { "spec" }
+end
View
@@ -1,2 +1,18 @@
#!/usr/bin/env rake
require "bundler/gem_tasks"
+require 'rake/testtask'
+
+Rake::TestTask.new(:spec) do |t|
+ t.libs.push "lib"
+ t.libs.push "spec"
+ t.test_files = FileList['spec/**/*_spec.rb']
+ t.verbose = true
+end
+
+task :test => :spec
+task :default => :test
+
+desc "open console (require 'trusted_keys')"
+task :c do
+ system "irb -I lib -r trusted_keys"
+end
@@ -1,8 +1,9 @@
module TrustedKeys
module Error
class NotTrusted < StandardError
- def initialize
+ def initialize(env)
@keys = {}
+ @production = not(env.development? or env.test?)
end
def message
@@ -18,7 +19,7 @@ def keys(options)
scope = options.fetch(:scope)
key = options.fetch(:key)
keys = options.fetch(:keys).flatten.map do |key|
- ":#{key.sub(/\(\di\)/, '')}"
+ ":#{key.to_s.sub(/\(\di\)/, '')}"
end.uniq.join(', ')
scope_key = (scope + [key]).compact.join('.')
@@ -28,16 +29,12 @@ def keys(options)
end
def present?
- if production?
+ if @production
false
else
not @keys.empty?
end
end
-
- def production?
- not(Rails.env.development? or Rails.env.test?)
- end
end
end
end
@@ -1,28 +1,28 @@
require 'trusted_keys/error/not_trusted'
+require 'active_support/core_ext/hash/indifferent_access'
module TrustedKeys
module Trustable
extend ActiveSupport::Concern
include ActiveModel::MassAssignmentSecurity
- attr_reader :keys
def initialize(scope, trusted_keys, *keys)
@scope = scope
@trusted_keys = trusted_keys
- @keys = keys
+
+ self.class.send("attr_accessible", *keys)
end
def attributes(params)
params = @scope.inject(params) { |params, key| params[key] }
result = sanitize_for_mass_assignment(params)
- keys = params.keys(&:to_s) - result.keys.map(&:to_s)
+ keys = params.keys.map(&:to_s) - result.keys.map(&:to_s)
unless keys.empty?
- untrusted = Error::NotTrusted.new.keys(:scope => @scope,
- :key => nil,
- :keys => keys)
-
+ untrusted = Error::NotTrusted.new(env).keys(:scope => @scope,
+ :key => nil,
+ :keys => keys)
raise untrusted if untrusted.present?
end
@@ -41,12 +41,16 @@ def level; @scope.size; end
private
+ def env
+ self.class.env
+ end
+
def remove_untrusted_keys(attributes)
trusted_keys = @trusted_keys.select do |trusted|
trusted.level == (level + 1)
end.map { |trusted| trusted.key.to_s }
- untrusted = Error::NotTrusted.new
+ untrusted = Error::NotTrusted.new(env)
attributes.each do |key, value|
if value.is_a?(Hash) and !trusted_keys.include?(key.to_s)
@@ -57,7 +61,7 @@ def remove_untrusted_keys(attributes)
raise untrusted if untrusted.present?
- attributes
+ HashWithIndifferentAccess.new(attributes)
end
end
end
View
@@ -0,0 +1,8 @@
+require 'minitest/autorun'
+require 'minitest-colorize'
+require "trusted_keys"
+require 'ostruct'
+
+Kernel.instance_eval do
+ alias_method :context, :describe
+end
@@ -0,0 +1,172 @@
+require 'minitest_helper'
+
+Klass = Class.new do
+ include TrustedKeys::Trustable
+end
+
+NotTrusted = TrustedKeys::Error::NotTrusted
+Usage = TrustedKeys::Error::Usage
+
+describe TrustedKeys::Trustable do
+ let(:trustableClass) do
+ Class.new do
+ include TrustedKeys::Trustable
+
+ def env
+ OpenStruct.new(:test? => false, :development? => false)
+ end
+ end
+ end
+
+ let(:klass) do
+ Class.new do
+ include TrustedKeys::Trustable
+
+ def env
+ OpenStruct.new(:test? => true, :development? => false)
+ end
+ end
+ end
+
+ let(:params) do
+ { "email" => "anders@email.com",
+ :controller => "events",
+ :password => "secret",
+ :post => { :body => "I am a body",
+ :title => "This is my title",
+ :comments => { 'body' => 'My body',
+ :email => "an email" } } }
+ end
+
+ describe "#on_scope" do
+
+ end
+
+ describe "#attributes" do
+
+ describe "when in test or development environment" do
+ context "level 0" do
+ it "doesn't raise an exception if not alla params is trusted" do
+ t = klass.new([], [], :email)
+ t.attributes(params).must_equal("email" => "anders@email.com")
+ end
+ end
+
+ context "level 1" do
+ it "raises an exception if not all params is trusted" do
+ t = klass.new([:post], [], :body)
+ proc { t.attributes(params) }.must_raise NotTrusted
+ end
+ end
+
+ context "level 2" do
+ it "raises an exception if not all params is trusted" do
+ t = klass.new([:post, :comments], [], :body)
+ proc { t.attributes(params) }.must_raise NotTrusted
+ end
+ end
+
+ context "next level hash not trusted" do
+ it "raises an exception" do
+ t = klass.new([], [], :email, :password, :post)
+ proc {
+ t.attributes(params)
+ }.must_raise NotTrusted
+ end
+ end
+ end
+
+ context "level 0" do
+ it "returns trusted params for level 0" do
+ t = trustableClass.new([], [], :email)
+ t.attributes(params).must_equal("email" => 'anders@email.com')
+ end
+
+ it "turns next hash into an empty string if next key not trusted" do
+ t = trustableClass.new([], [], :post)
+ t.attributes(params).must_equal("post" => '')
+ end
+
+ it "returns the next hash if it has trusted keys" do
+ level1 = trustableClass.new([:post], [], :body)
+ t = trustableClass.new([], [level1], :post)
+
+ expected = {"post"=> { "body"=>"I am a body",
+ "title"=>"This is my title",
+ "comments"=> { "body"=>"My body",
+ "email"=>"an email"} } }
+ t.attributes(params).must_equal(expected)
+ end
+ end
+
+ context "level 1" do
+ it "returns trusted params for level 1" do
+ post = trustableClass.new([:post], [], :body)
+ post.attributes(params).must_equal("body" => 'I am a body')
+ end
+ end
+
+ context "level 2" do
+ it "returns trusted params for level 2" do
+ post = trustableClass.new([:post, :comments], [], :body)
+ post.attributes(params).must_equal("body" => 'My body')
+ end
+ end
+ end
+
+ describe "#key" do
+ context "scope is empty" do
+ it "returns nil" do
+ Klass.new([], []).key.must_equal nil
+ end
+ end
+
+ context "scope has 1 key" do
+ it "returns the key" do
+ Klass.new([:post], []).key.must_equal :post
+ end
+ end
+
+ context "scope has 2 keys" do
+ it "returns the last key - deepest key" do
+ Klass.new([:post, :comment], []).key.must_equal :comment
+ end
+ end
+ end
+
+ describe "#level" do
+ context "scope is empty" do
+ it "returns 0" do
+ Klass.new([], []).level.must_equal 0
+ end
+ end
+
+ context "scope has 1 key" do
+ it "returns 1" do
+ Klass.new([:post], []).level.must_equal 1
+ end
+ end
+
+ context "scope has 2 keys" do
+ it "returns 2" do
+ Klass.new([:post, :comment], []).level.must_equal 2
+ end
+ end
+ end
+
+ describe "#<=>" do
+ describe "sort an array" do
+ before do
+ @a1 = Klass.new([:post, :comment], [])
+ @a2 = Klass.new([:post], [])
+ @array = [@a1, @a2]
+ @array.first.must_equal @a1
+ end
+
+ it "sort objects with the lowest level first" do
+ @array.sort.first.must_equal @a2
+ end
+ end
+ end
+
+end
View
@@ -8,12 +8,18 @@ Gem::Specification.new do |gem|
gem.summary = %q{Mass assignment security in your controller}
gem.homepage = ""
- gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
+ gem.executables = `git ls-files -- bin/*`.split("\n").map do |f|
+ File.basename(f)
+ end
gem.files = `git ls-files`.split("\n")
gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
gem.name = "trusted_keys"
gem.require_paths = ["lib"]
gem.version = TrustedKeys::VERSION
gem.add_runtime_dependency "rails", ["~> 3.0"]
+ gem.add_development_dependency "minitest", ["~> 2.11"]
+ gem.add_development_dependency "guard", ["~> 1.0"]
+ gem.add_development_dependency "guard-minitest", ["~> 0.5"]
+ gem.add_development_dependency "minitest-colorize", ["~> 0.0.4"]
end

0 comments on commit fb7a287

Please sign in to comment.