sanitize_attributes 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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