sanitize_attributes 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in sanitize_attributes.gemspec
4
+ gemspec
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 [name of plugin creator]
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,78 @@
1
+ = SanitizeAttributes
2
+
3
+ This is a simple plugin for ActiveRecord models to define sanitizable attributes. When an object is saved, those attributes will be run through whatever filter you've defined. You can define a default filter for all sanitizations.
4
+
5
+ Sanitization only happens for non-nil attributes. (Because a nil attribute may be valid for your model, and the sanitzers should only have to worry about working with strings.)
6
+
7
+ This plugin was created to implement anti-XSS validation. My gem of choice is Sanitize: http://github.com/rgrove/sanitize/tree/master
8
+
9
+ For Rails 3, add this line to your Gemfile:
10
+
11
+ gem 'sanitize_attributes', :git => "git@github.com:devp/sanitize_attributes.git"
12
+
13
+ For Rails 2.3, it should work when installed as a plugin:
14
+
15
+ ./script/plugin install git@github.com:devp/sanitize_attributes.git
16
+
17
+ == Example
18
+
19
+ # config/initializers/sanitize_attributes.rb
20
+ SanitizeAttributes.default_sanitization_method = lambda do |text|
21
+ text.gsub(/[^\w\s]/, "") # very simple, very limited
22
+ end
23
+
24
+ # app/models/bookmark.rb
25
+ class Bookmark
26
+ sanitize_attributes :sitename
27
+ end
28
+
29
+ # app/models/article.rb
30
+ class Article
31
+ sanitize_attributes :title, :author
32
+
33
+ sanitize_attributes :body do |body_text|
34
+ # This needs to be safe, renderable HTML, so let's use a real sanization tool
35
+ # I recommend: http://github.com/rgrove/sanitize/tree/master
36
+ Sanitize.clean(body_text)
37
+ end
38
+ end
39
+
40
+ Article.default_sanitization_method_for_class = lambda do |text|
41
+ text.gsub(/[^\w\s\'".,?!]/, "") # more reasonable, for titles and such
42
+ end
43
+
44
+ # in action...
45
+ b = Bookmark.create(:sitename => "boston.rb!!!", :url => "http://http://bostonrb.org/")
46
+ b.sitename # => "bostonrb"
47
+ a = Article.create(:title => "<b>Hello</b>!", :body => "Please remove the <script>script tags</script>!")
48
+ a.title # => "Hello!"
49
+ a.body # => "Please remove the script tags!"
50
+
51
+
52
+ == Future Work
53
+
54
+ Things to work on in the future:
55
+
56
+ * allowing strings/symbols for sanitization methods, not just blocks
57
+
58
+ Nacho.default_sanitization_method_for_class = :microwave # uses Nacho.microwave
59
+ Nacho.default_sanitization_method_for_class = "Sanitize.clean"
60
+
61
+ * add validation helpers, if you want to flag problematic text rather than cleaning it.
62
+
63
+ class Foo
64
+ validate_sanitized :value
65
+ end
66
+ Foo.new(:value => "abc").valid? #=> false if a sanitized copy of #value is different than the original
67
+
68
+ * better functionality for subclasses. Currently, they will share sanitized attributes and sanitization methods across subclasses and the base class.
69
+
70
+ === Etc
71
+
72
+ Thanks to contributors:
73
+
74
+ * Josh Nichols
75
+ * Michael Reinsch
76
+ * Paul McMahon
77
+
78
+ (c) 2009 Dev Purkayastha, released under the MIT license
@@ -0,0 +1,35 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+ require 'rake/rdoctask'
4
+ require 'bundler/gem_tasks'
5
+
6
+ desc 'Default: run unit tests.'
7
+ task :default => :test
8
+
9
+ desc 'Test the sanitize_attributes plugin.'
10
+ Rake::TestTask.new(:test) do |t|
11
+ t.libs << 'lib'
12
+ t.pattern = 'test/**/*_test.rb'
13
+ t.verbose = true
14
+ end
15
+
16
+ desc 'Generate documentation for the sanitize_attributes plugin.'
17
+ Rake::RDocTask.new(:rdoc) do |rdoc|
18
+ rdoc.rdoc_dir = 'rdoc'
19
+ rdoc.title = 'SanitizeAttributes'
20
+ rdoc.options << '--line-numbers' << '--inline-source'
21
+ rdoc.rdoc_files.include('README.rdoc')
22
+ rdoc.rdoc_files.include('lib/**/*.rb')
23
+ end
24
+
25
+ begin
26
+ require 'rcov/rcovtask'
27
+ Rcov::RcovTask.new(:rcov) do |rcov|
28
+ rcov.libs << 'test'
29
+ rcov.pattern = 'test/*.rb'
30
+ end
31
+ rescue LoadError
32
+ task :rcov do
33
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
34
+ end
35
+ end
data/init.rb ADDED
@@ -0,0 +1,2 @@
1
+ require 'sanitize_attributes'
2
+ SanitizeAttributes.hook!
@@ -0,0 +1,28 @@
1
+ require 'sanitize_attributes/macros'
2
+ require 'sanitize_attributes/class_methods'
3
+ require 'sanitize_attributes/instance_methods'
4
+
5
+ module SanitizeAttributes
6
+ include Macros
7
+
8
+ class << self
9
+ attr_accessor :default_sanitization_method
10
+
11
+ def hook!
12
+ if ActiveSupport.respond_to?(:on_load)
13
+ ActiveSupport.on_load(:active_record) { extend SanitizeAttributes }
14
+ else # Rails 2 fallback
15
+ ActiveRecord::Base.class_eval { extend SanitizeAttributes }
16
+ end
17
+ end
18
+ end
19
+
20
+ # Thrown when sanitize! is called without a defined sanitization method at the class or global level
21
+ class NoSanitizationMethodDefined < StandardError ; end
22
+
23
+ if defined?(Rails) && defined?(Rails::Railtie)
24
+ class Railtie < Rails::Railtie
25
+ initializer("sanitize_attirbutes") { SanitizeAttributes.hook! }
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,15 @@
1
+ module SanitizeAttributes
2
+ module ClassMethods
3
+
4
+ attr_accessor :default_sanitization_method_for_class
5
+
6
+ def sanitizable_attributes #:nodoc:
7
+ self.sanitizable_attribute_hash.keys
8
+ end
9
+
10
+ def sanitization_method_for_attribute(attribute) #:nodoc:
11
+ index = self.sanitizable_attribute_hash[attribute]
12
+ self.sanitization_block_array[index] if index
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,26 @@
1
+ module SanitizeAttributes
2
+ module InstanceMethods
3
+
4
+ # sanitize! is the method that is called when a model is saved.
5
+ # It can be called to manually sanitize attributes.
6
+ def sanitize!
7
+ self.class.sanitizable_attributes.each do |attr_name|
8
+ cleaned_text = process_text_via_sanitization_method(self.send(attr_name), attr_name)
9
+ self.send((attr_name.to_s + '='), cleaned_text)
10
+ end
11
+ end
12
+
13
+ private
14
+ def process_text_via_sanitization_method(txt, attr_name = nil)
15
+ return nil if txt.nil?
16
+ sanitization_method = self.class.sanitization_method_for_attribute(attr_name) || # attribute level
17
+ self.class.default_sanitization_method_for_class || # class level
18
+ SanitizeAttributes::default_sanitization_method # global
19
+ if sanitization_method && sanitization_method.is_a?(Proc)
20
+ sanitization_method.call(txt)
21
+ else
22
+ raise SanitizeAttributes::NoSanitizationMethodDefined
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,41 @@
1
+ module SanitizeAttributes
2
+ module Macros
3
+ # sanitize_attributes is used to define sanitizable attributes within a model definition.
4
+ def sanitize_attributes(*args, &block)
5
+ if (args.last && args.last.is_a?(Hash))
6
+ options = args.pop
7
+ end
8
+ options ||= {}
9
+ unless @sanitize_hook_already_defined
10
+ include InstanceMethods
11
+ extend ClassMethods
12
+
13
+ if options[:before_validation]
14
+ before_validation :sanitize!
15
+ else
16
+ before_save :sanitize!
17
+ end
18
+
19
+ cattr_accessor :sanitizable_attribute_hash
20
+ cattr_accessor :sanitization_block_array
21
+ self.sanitizable_attribute_hash ||= {}
22
+ self.sanitization_block_array ||= []
23
+
24
+ @sanitize_hook_already_defined = true
25
+ end
26
+
27
+ if block
28
+ self.sanitization_block_array << block
29
+ block = self.sanitization_block_array.index(block)
30
+ else
31
+ block = nil
32
+ end
33
+
34
+ args.each do |attr|
35
+ self.sanitizable_attribute_hash[attr] = block
36
+ end
37
+
38
+ true
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,3 @@
1
+ module SanitizeAttributes
2
+ VERSION = "0.0.2"
3
+ end
@@ -0,0 +1,26 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "sanitize_attributes/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "sanitize_attributes"
7
+ s.version = SanitizeAttributes::VERSION
8
+ s.authors = ["Dev Purkayastha", "Paul McMahon"]
9
+ s.email = ["dev@forgreatjustice.net"]
10
+ s.homepage = "https://github.com/devp/sanitize_attributes"
11
+ s.summary = %q{This is a simple plugin for ActiveRecord models to define sanitizable attributes.}
12
+ s.description = %q{This is a simple plugin for ActiveRecord models to define sanitizable attributes. When an object is saved, those attributes will be run through whatever filter you’ve defined. You can define a default filter for all sanitizations.}
13
+
14
+ s.rubyforge_project = "sanitize_attributes"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ s.add_dependency 'rails', '>= 3.0.0'
22
+ s.add_development_dependency 'rake'
23
+ s.add_development_dependency 'sqlite3'
24
+ s.add_development_dependency 'thoughtbot-shoulda'
25
+ s.add_development_dependency 'mocha'
26
+ end
@@ -0,0 +1,119 @@
1
+ require "#{File.dirname(__FILE__)}/test_helper"
2
+
3
+ ActiveRecord::Schema.define do
4
+ create_table :articles do |t|
5
+ t.column :title, :string
6
+ t.column :body, :string
7
+ t.column :review, :string
8
+ end
9
+ end
10
+
11
+ ActiveRecord::Schema.define do
12
+ create_table :albums do |t|
13
+ t.column :title, :string
14
+ t.column :artist, :string
15
+ t.column :rating, :string
16
+ end
17
+ end
18
+
19
+ class Article < ActiveRecord::Base
20
+ sanitize_attributes :title, :body
21
+ end
22
+
23
+ class Album < ActiveRecord::Base
24
+ sanitize_attributes :title, :artist
25
+ end
26
+
27
+ class SanitizeAttributesTest < Test::Unit::TestCase
28
+
29
+ context "Two classes with #sanitizable_attributes in their defnition" do
30
+ should "not share the same sanitizable_attributes" do
31
+ assert_not_equal Album.sanitizable_attributes, Article.sanitizable_attributes
32
+ end
33
+ end
34
+
35
+ context "#sanitize_attributes" do
36
+ should "mark expected attributes as sanitizable" do
37
+ assert_equal Set.new([:title, :body]), Set.new(Article.sanitizable_attributes)
38
+ end
39
+
40
+ should "not create duplicates entries" do
41
+ Article.sanitize_attributes :title
42
+ assert_equal Set.new([:title, :body]), Set.new(Article.sanitizable_attributes)
43
+ end
44
+ end
45
+
46
+ context "given an undefined sanitization methods" do
47
+ setup do
48
+ SanitizeAttributes.default_sanitization_method = nil
49
+ Article.default_sanitization_method_for_class = nil
50
+ end
51
+
52
+ should "raise NoSanitizationMethodDefined on #save!" do
53
+ assert_raise(SanitizeAttributes::NoSanitizationMethodDefined) do
54
+ Article.create!(:title => "This Article")
55
+ end
56
+ end
57
+ end
58
+
59
+ context "given a defined default sanitization method" do
60
+ setup do
61
+ SanitizeAttributes.default_sanitization_method = lambda{|s| s.generic_sanitize}
62
+ Album.default_sanitization_method_for_class = nil
63
+ Article.default_sanitization_method_for_class = nil
64
+ end
65
+
66
+ should "use the default method on #save!, changing only the sanitizable attribute" do
67
+ title = "This Article"
68
+ title.expects(:generic_sanitize).returns("sanitized")
69
+ article = Article.new(:title => title, :review => "do not change")
70
+ assert article.save!
71
+ assert_equal "sanitized", article.title
72
+ assert_equal "do not change", article.review
73
+ end
74
+
75
+ should "not sanitize nil attributes" do
76
+ title = nil
77
+ body = "Lorem Ipsum"
78
+ title.expects(:generic_sanitize).never
79
+ body.expects(:generic_sanitize).once
80
+ article = Article.new(:title => title, :body => body)
81
+ assert article.save!
82
+ end
83
+
84
+ context "and defined class-level sanitization methods" do
85
+ setup do
86
+ Album.default_sanitization_method_for_class = lambda{|s| Album.album_sanitizer}
87
+ Article.default_sanitization_method_for_class = lambda{|s| Article.article_sanitizer}
88
+ end
89
+
90
+ should "use the appropriate method for each class" do
91
+ Album.expects(:album_sanitizer).times(2)
92
+ Album.create!(:title => "This Album", :artist => "Rock Star", :rating => "great")
93
+
94
+ Article.expects(:article_sanitizer).times(2)
95
+ Article.create!(:title => "This Article", :body => "Lorem Ipsum", :review => "awesome")
96
+ end
97
+
98
+ context "and further attribute-level sanitization" do
99
+ setup do
100
+ Album.sanitize_attributes :artist do
101
+ Album.artist_sanitizer
102
+ end
103
+ end
104
+
105
+ teardown do
106
+ Album.sanitize_attributes :artist # unsetting the attribute-specific block
107
+ end
108
+
109
+ should "use the appropriate method for each attribute" do
110
+ Album.expects(:album_sanitizer).once
111
+ Album.expects(:artist_sanitizer).once
112
+ Album.create!(:title => "This Album", :artist => "Rock Star", :rating => "great")
113
+ end
114
+ end
115
+ end
116
+
117
+ end
118
+
119
+ end
@@ -0,0 +1,13 @@
1
+ require 'test/unit'
2
+ require 'set'
3
+ require 'rubygems'
4
+ require 'active_record'
5
+ ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :database => ":memory:")
6
+ gem 'thoughtbot-shoulda'
7
+ require 'shoulda'
8
+ gem 'mocha'
9
+ require 'mocha'
10
+
11
+ $:.unshift "#{File.dirname(__FILE__)}/../lib"
12
+
13
+ require "#{File.dirname(__FILE__)}/../init"
metadata ADDED
@@ -0,0 +1,119 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sanitize_attributes
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Dev Purkayastha
9
+ - Paul McMahon
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+ date: 2011-11-26 00:00:00.000000000Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: rails
17
+ requirement: &2153166840 !ruby/object:Gem::Requirement
18
+ none: false
19
+ requirements:
20
+ - - ! '>='
21
+ - !ruby/object:Gem::Version
22
+ version: 3.0.0
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: *2153166840
26
+ - !ruby/object:Gem::Dependency
27
+ name: rake
28
+ requirement: &2153166320 !ruby/object:Gem::Requirement
29
+ none: false
30
+ requirements:
31
+ - - ! '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: *2153166320
37
+ - !ruby/object:Gem::Dependency
38
+ name: sqlite3
39
+ requirement: &2153165800 !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ! '>='
43
+ - !ruby/object:Gem::Version
44
+ version: '0'
45
+ type: :development
46
+ prerelease: false
47
+ version_requirements: *2153165800
48
+ - !ruby/object:Gem::Dependency
49
+ name: thoughtbot-shoulda
50
+ requirement: &2153165020 !ruby/object:Gem::Requirement
51
+ none: false
52
+ requirements:
53
+ - - ! '>='
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
56
+ type: :development
57
+ prerelease: false
58
+ version_requirements: *2153165020
59
+ - !ruby/object:Gem::Dependency
60
+ name: mocha
61
+ requirement: &2153164540 !ruby/object:Gem::Requirement
62
+ none: false
63
+ requirements:
64
+ - - ! '>='
65
+ - !ruby/object:Gem::Version
66
+ version: '0'
67
+ type: :development
68
+ prerelease: false
69
+ version_requirements: *2153164540
70
+ description: This is a simple plugin for ActiveRecord models to define sanitizable
71
+ attributes. When an object is saved, those attributes will be run through whatever
72
+ filter you’ve defined. You can define a default filter for all sanitizations.
73
+ email:
74
+ - dev@forgreatjustice.net
75
+ executables: []
76
+ extensions: []
77
+ extra_rdoc_files: []
78
+ files:
79
+ - .gitignore
80
+ - Gemfile
81
+ - MIT-LICENSE
82
+ - README.rdoc
83
+ - Rakefile
84
+ - init.rb
85
+ - lib/sanitize_attributes.rb
86
+ - lib/sanitize_attributes/class_methods.rb
87
+ - lib/sanitize_attributes/instance_methods.rb
88
+ - lib/sanitize_attributes/macros.rb
89
+ - lib/sanitize_attributes/version.rb
90
+ - sanitize_attributes.gemspec
91
+ - test/sanitize_attributes_test.rb
92
+ - test/test_helper.rb
93
+ homepage: https://github.com/devp/sanitize_attributes
94
+ licenses: []
95
+ post_install_message:
96
+ rdoc_options: []
97
+ require_paths:
98
+ - lib
99
+ required_ruby_version: !ruby/object:Gem::Requirement
100
+ none: false
101
+ requirements:
102
+ - - ! '>='
103
+ - !ruby/object:Gem::Version
104
+ version: '0'
105
+ required_rubygems_version: !ruby/object:Gem::Requirement
106
+ none: false
107
+ requirements:
108
+ - - ! '>='
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ requirements: []
112
+ rubyforge_project: sanitize_attributes
113
+ rubygems_version: 1.8.11
114
+ signing_key:
115
+ specification_version: 3
116
+ summary: This is a simple plugin for ActiveRecord models to define sanitizable attributes.
117
+ test_files:
118
+ - test/sanitize_attributes_test.rb
119
+ - test/test_helper.rb