sanitize-rails 0.8.1 → 0.9.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +2 -0
- data/.travis.yml +3 -2
- data/LICENSE +21 -0
- data/README.md +64 -34
- data/example/sanitizer.rb +8 -0
- data/lib/sanitize/rails/action_view.rb +20 -0
- data/lib/sanitize/rails/active_record.rb +38 -0
- data/lib/sanitize/rails/engine.rb +58 -0
- data/lib/sanitize/rails/matchers.rb +112 -0
- data/lib/sanitize/rails/railtie.rb +21 -0
- data/lib/sanitize/rails/string.rb +20 -0
- data/lib/sanitize/rails/test_helpers.rb +63 -0
- data/lib/sanitize/rails/version.rb +1 -1
- data/lib/sanitize/rails.rb +14 -188
- data/sanitize-rails.gemspec +3 -3
- data/test/sanitize_rails_engine_test.rb +0 -4
- data/test/sanitize_rails_string_extension_test.rb +0 -4
- data/test/test_helper.rb +4 -0
- metadata +12 -3
- data/lib/sanitize/railtie.rb +0 -19
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 92119626e5ebb931fe890e757c3c2fc9b287a351
|
4
|
+
data.tar.gz: 315002fac67c6275c2b6ef6eafeef4f946cf3f52
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 452ec0f4ac318d174ed4625646ad62e9f3524faa926eaff28726d2fb1e57127ac9db389bf1aadbd1f312da30c760ea12beed184e4d69cb739d50070ccd710aae
|
7
|
+
data.tar.gz: 44077d2a5c5a330f61c2903649b0ef71e246af81d6323047713d69d21292c0893af6883cf41168b2cac25547ee5397c89c47ae4bf647a2d1015d9c11400a60ad
|
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2011-2014 Marcello Barnaba <vjt@openssl.it>
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
CHANGED
@@ -1,64 +1,94 @@
|
|
1
|
-
Sanitize-Rails - sanitize .. on Rails. [![Build Status](https://travis-ci.org/vjt/sanitize-rails.png)](https://travis-ci.org/vjt/sanitize-rails)
|
2
|
-
======================================
|
1
|
+
# Sanitize-Rails - sanitize .. on Rails. [![Build Status](https://travis-ci.org/vjt/sanitize-rails.png)](https://travis-ci.org/vjt/sanitize-rails)
|
3
2
|
|
4
|
-
|
5
|
-
|
3
|
+
An easy bridge to integrate Ryan Grove's [HTML Whitelist Sanitizer][sanitize]
|
4
|
+
in your Rails application.
|
6
5
|
|
7
|
-
|
6
|
+
## Installation
|
8
7
|
|
9
|
-
|
8
|
+
`Gemfile`:
|
10
9
|
|
11
|
-
|
12
|
-
-------------
|
10
|
+
gem 'sanitize-rails', require: 'sanitize/rails'
|
13
11
|
|
14
|
-
|
12
|
+
## Configuration
|
13
|
+
|
14
|
+
Pass the configuration to `Sanitize` calling `Sanitize::Rails.configure` in
|
15
|
+
an initializer, say `config/initializers/sanitizer.rb`:
|
15
16
|
|
16
17
|
Sanitize::Rails.configure(
|
17
|
-
:
|
18
|
-
:
|
18
|
+
elements: [ ... ],
|
19
|
+
attributes: { ... },
|
19
20
|
...
|
20
21
|
)
|
21
22
|
|
22
|
-
|
23
|
+
Check out the [example][] in the `example/` directory.
|
24
|
+
|
25
|
+
## Usage
|
26
|
+
|
27
|
+
ActionView `sanitize` helper is transparently overriden to use the `Sanitize`
|
28
|
+
gem.
|
29
|
+
|
30
|
+
A `sanitize` helper is added to `ActiveRecord`, that installs on create/save
|
31
|
+
callbacks that sanitize the given attributes before persisting them to the
|
32
|
+
database. Example:
|
33
|
+
|
34
|
+
`app/models/foo.rb`:
|
35
|
+
|
36
|
+
class Foo < ActiveRecord::Base
|
37
|
+
sanitizes :description # on save by default
|
38
|
+
|
39
|
+
sanitizes :body, on: :create
|
40
|
+
sanitizes :remarks, on: :save
|
41
|
+
end
|
42
|
+
|
43
|
+
## Testing
|
44
|
+
|
45
|
+
### RSpec
|
46
|
+
|
47
|
+
`spec/spec_helper.rb`:
|
48
|
+
|
49
|
+
require 'sanitize/rails/matchers'
|
50
|
+
|
51
|
+
in spec code:
|
23
52
|
|
24
|
-
|
25
|
-
|
53
|
+
describe Post do
|
54
|
+
# Simplest variant, single field and default values
|
55
|
+
it { should sanitize_field :title }
|
26
56
|
|
27
|
-
|
57
|
+
# Multiple fields
|
58
|
+
it { should sanitize_fields :title, :body }
|
28
59
|
|
29
|
-
|
30
|
-
|
31
|
-
|
60
|
+
# Specifing both text to sanitize and expected result
|
61
|
+
it { should sanitize_field(:title).replacing('©').with('©') }
|
62
|
+
end
|
32
63
|
|
33
|
-
|
34
|
-
the
|
64
|
+
You should pass field names to matcher in the same way as you do with the
|
65
|
+
`sanitize` call in the model, otherwise sanitize method won't be found in
|
66
|
+
model.
|
35
67
|
|
36
|
-
|
37
|
-
-------
|
68
|
+
### Test::Unit
|
38
69
|
|
39
|
-
|
40
|
-
and send a pull request :-)
|
70
|
+
`test/test_helper.rb:`
|
41
71
|
|
42
|
-
|
72
|
+
require 'sanitize/rails/test_helpers'
|
43
73
|
|
44
74
|
Sanitize::Rails::TestHelpers.setup(self,
|
45
|
-
:
|
46
|
-
:
|
75
|
+
invalid: 'some <a>string',
|
76
|
+
valid: 'some <a>string</a>'
|
47
77
|
)
|
48
78
|
|
49
79
|
your test:
|
50
80
|
|
51
|
-
assert_sanitizes
|
81
|
+
assert_sanitizes Model, :field, :some_other_field
|
52
82
|
|
53
|
-
Compatibility
|
54
|
-
-------------
|
83
|
+
## Compatibility
|
55
84
|
|
56
|
-
Tested with Rails 3.0
|
85
|
+
Tested with Rails 3.0 and :up: under Ruby 1.9.3 and :up:.
|
57
86
|
|
58
|
-
License
|
59
|
-
-------
|
87
|
+
## License
|
60
88
|
|
61
89
|
MIT
|
62
90
|
|
91
|
+
## :smiley: Have fun!
|
63
92
|
|
64
|
-
|
93
|
+
[sanitize]: https://github.com/rgrove/sanitize
|
94
|
+
[example]: https://github.com/vjt/sanitize-rails/blob/master/example/sanitizer.rb
|
data/example/sanitizer.rb
CHANGED
@@ -1,3 +1,11 @@
|
|
1
|
+
# Sanitize::Rails example configuration
|
2
|
+
#
|
3
|
+
# https://github.com/vjt/sanitize-rails
|
4
|
+
#
|
5
|
+
# Enjoy, Share and Love <3
|
6
|
+
#
|
7
|
+
# -- vjt@openssl.it
|
8
|
+
#
|
1
9
|
HTML::WhiteListSanitizer.allowed_css_properties = %w(text-align background-color)
|
2
10
|
HTML::WhiteListSanitizer.shorthand_css_properties = %w()
|
3
11
|
HTML::WhiteListSanitizer.allowed_css_keywords = %w(left center right justify rgb)
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Sanitize::Rails
|
2
|
+
|
3
|
+
module ActionView # :nodoc:
|
4
|
+
def self.included(base)
|
5
|
+
base.class_eval do
|
6
|
+
# To make sure we're called *after* the method is defined
|
7
|
+
undef_method :sanitize
|
8
|
+
|
9
|
+
# Overrides ActionView's sanitize() helper to use +Engine#clean+
|
10
|
+
#
|
11
|
+
# FIXME: Options are currently ignored.
|
12
|
+
#
|
13
|
+
def sanitize(string, options = {})
|
14
|
+
Engine.clean(string)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Sanitize::Rails
|
2
|
+
|
3
|
+
# Adds the +sanitizes+ method to ActiveRecord children classes
|
4
|
+
#
|
5
|
+
module ActiveRecord
|
6
|
+
# Generates before_save/before_create filters that implement
|
7
|
+
# sanitization on the given fields, in the given callback
|
8
|
+
# point.
|
9
|
+
#
|
10
|
+
# Usage:
|
11
|
+
#
|
12
|
+
# sanitizes :some_field, :some_other_field #, :on => :save
|
13
|
+
#
|
14
|
+
# Valid callback points are :save and :create, callbacks are installed "before_"
|
15
|
+
# by default. Generated callbacks are named with the "sanitize_" prefix follwed
|
16
|
+
# by the field names separated by an underscore.
|
17
|
+
#
|
18
|
+
def sanitizes(*fields)
|
19
|
+
options = fields.extract_options!
|
20
|
+
callback = Engine.callback_for(options)
|
21
|
+
sanitizer = Engine.method_for(fields)
|
22
|
+
|
23
|
+
define_method(sanitizer) do # # Unrolled version
|
24
|
+
fields.each do |field| #
|
25
|
+
value = send(field)
|
26
|
+
unless value.blank? # def sanitize_fieldA_fieldB
|
27
|
+
sanitized = Engine.clean(value) # self.fieldA = Engine.clean(self.fieldA) unless fieldA.blank?
|
28
|
+
send("#{field}=", sanitized) # self.fieldB = Engine.clean(self.fieldB) unless fieldB.blank?
|
29
|
+
end # end
|
30
|
+
end #
|
31
|
+
end # end
|
32
|
+
|
33
|
+
protected sanitizer # protected :sanitize_fieldA_fieldB
|
34
|
+
send callback, sanitizer # before_save :sanitize_fieldA_fieldB
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module Sanitize::Rails
|
2
|
+
|
3
|
+
module Engine
|
4
|
+
extend self
|
5
|
+
|
6
|
+
def configure(config)
|
7
|
+
@@config = config.freeze
|
8
|
+
end
|
9
|
+
|
10
|
+
# Returns a memoized instance of the Engine with the
|
11
|
+
# configuration passed to the +configure+ method or with
|
12
|
+
# the ActionView's default config
|
13
|
+
#
|
14
|
+
def cleaner
|
15
|
+
@@config ||= begin
|
16
|
+
{
|
17
|
+
:elements => ::ActionView::Base.sanitized_allowed_tags.to_a,
|
18
|
+
:attributes => { :all => ::ActionView::Base.sanitized_allowed_attributes.to_a},
|
19
|
+
:protocols => { :all => ::ActionView::Base.sanitized_allowed_protocols.to_a }
|
20
|
+
}
|
21
|
+
rescue
|
22
|
+
warn "ActionView not available, falling back to Sanitize's BASIC config"
|
23
|
+
::Sanitize::Config::BASIC
|
24
|
+
end
|
25
|
+
@sanitizer ||= ::Sanitize.new(@@config)
|
26
|
+
end
|
27
|
+
|
28
|
+
# Returns a copy of the given `string` after sanitizing it and marking it
|
29
|
+
# as `html_safe`
|
30
|
+
#
|
31
|
+
# Ensuring this methods return instances of ActiveSupport::SafeBuffer
|
32
|
+
# means that text passed through `Sanitize::Rails::Engine.clean`
|
33
|
+
# will not be escaped by ActionView's XSS filtering utilities.
|
34
|
+
def clean(string)
|
35
|
+
::ActiveSupport::SafeBuffer.new string.to_s.dup.tap { |s| clean!(s) }
|
36
|
+
end
|
37
|
+
|
38
|
+
# Sanitizes the given `string` in place and does NOT mark it as `html_safe`
|
39
|
+
#
|
40
|
+
def clean!(string)
|
41
|
+
cleaner.clean!(string.to_s).to_s
|
42
|
+
end
|
43
|
+
|
44
|
+
def callback_for(options) #:nodoc:
|
45
|
+
point = (options[:on] || 'save').to_s
|
46
|
+
|
47
|
+
unless %w( save create ).include?(point)
|
48
|
+
raise ArgumentError, "Invalid callback point #{point}, valid ones are :save and :create"
|
49
|
+
end
|
50
|
+
|
51
|
+
"before_#{point}".intern
|
52
|
+
end
|
53
|
+
|
54
|
+
def method_for(fields) #:nodoc:
|
55
|
+
"sanitize_#{fields.join('_')}".intern
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
module Sanitize::Rails::Matchers
|
2
|
+
|
3
|
+
# RSpec custom matcher to check for field sanitization
|
4
|
+
#
|
5
|
+
# Verifies that the matcher subject sanitizes the given `fields`, by
|
6
|
+
# checking that the sanitize callback works as expected.
|
7
|
+
#
|
8
|
+
# Matcher can be used in the following variants:
|
9
|
+
#
|
10
|
+
# describe Post do
|
11
|
+
# # Simplest variant, single field and default values
|
12
|
+
# it { should sanitize_field :title }
|
13
|
+
# # Multiple fields
|
14
|
+
# it { should sanitize_fields :title, :body }
|
15
|
+
# # Specifing both text to sanitize and expected result
|
16
|
+
# it { should sanitize_field(:title).replacing('©').with('©') }
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
def sanitize_field(*fields)
|
20
|
+
if fields.empty?
|
21
|
+
raise ArgumentError, 'need at least one argument'
|
22
|
+
else
|
23
|
+
SanitizeFieldsMatcher.new(*fields)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# Sintactic sugar
|
28
|
+
alias_method :sanitize_fields, :sanitize_field
|
29
|
+
|
30
|
+
# Add matchers module to rspec configuration
|
31
|
+
RSpec.configure { |c| c.include(self) } if defined? RSpec and RSpec.respond_to?(:configure)
|
32
|
+
|
33
|
+
# Actual matcher class
|
34
|
+
class SanitizeFieldsMatcher
|
35
|
+
|
36
|
+
# Take an array of fields to check, they must respect the same order given in model `sanitize` call
|
37
|
+
def initialize(*fields)
|
38
|
+
self.options = fields.extract_options!
|
39
|
+
self.sanitizer = ::Sanitize::Rails::Engine.method_for(fields)
|
40
|
+
self.fields = fields
|
41
|
+
end
|
42
|
+
|
43
|
+
# Used to specify invalid text assigned to fields
|
44
|
+
def replacing(invalid)
|
45
|
+
@invalid_changed = true
|
46
|
+
@invalid = invalid
|
47
|
+
self
|
48
|
+
end
|
49
|
+
|
50
|
+
# Used to specify expected output for the invalid text
|
51
|
+
def with(valid)
|
52
|
+
@valid_changed = true
|
53
|
+
@valid = valid
|
54
|
+
self
|
55
|
+
end
|
56
|
+
|
57
|
+
# Actual match code
|
58
|
+
def matches?(instance)
|
59
|
+
self.instance = instance
|
60
|
+
# assign invalid value to each field
|
61
|
+
fields.each { |field| instance.send("#{field}=", invalid_value) }
|
62
|
+
# sanitize the object calling the method
|
63
|
+
instance.send(sanitizer) rescue nil
|
64
|
+
# check expectation on results
|
65
|
+
fields.all? { |field| valid_value == instance.send(field) }
|
66
|
+
end
|
67
|
+
|
68
|
+
def failure_message_for_should
|
69
|
+
"Expected #{should_helper} to return sanitized value '#{valid_value}', got '#{attribute_values}'"
|
70
|
+
end
|
71
|
+
|
72
|
+
def failure_message_for_should_not
|
73
|
+
"Expected #{field_helper} not to be sanitized"
|
74
|
+
end
|
75
|
+
|
76
|
+
def description
|
77
|
+
"sanitize #{should_helper}"
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
|
82
|
+
attr_accessor :options, :sanitizer, :instance, :fields
|
83
|
+
|
84
|
+
def invalid_value
|
85
|
+
@invalid ||= '<b>valid<br>'
|
86
|
+
end
|
87
|
+
|
88
|
+
def valid_value
|
89
|
+
@valid ||= '<b>valid<br></b>'
|
90
|
+
end
|
91
|
+
|
92
|
+
def custom_values?
|
93
|
+
@invalid_changed && @invalid_changed
|
94
|
+
end
|
95
|
+
|
96
|
+
def field_helper
|
97
|
+
"#{'field'.pluralize(fields.count)} #{fields.to_sentence}"
|
98
|
+
end
|
99
|
+
|
100
|
+
def should_helper
|
101
|
+
field_helper.tap do |desc|
|
102
|
+
desc << " by replacing '#{invalid_value}' with '#{valid_value}'" if custom_values?
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def attribute_values
|
107
|
+
instance.attributes.slice(*fields.map(&:to_s))
|
108
|
+
end
|
109
|
+
|
110
|
+
end
|
111
|
+
|
112
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Sanitize::Rails
|
2
|
+
|
3
|
+
class Railtie < ::Rails::Railtie
|
4
|
+
initializer 'sanitize-rails.insert_into_action_view' do
|
5
|
+
::ActiveSupport.on_load :action_view do
|
6
|
+
::ActionView::Helpers::SanitizeHelper.instance_eval { include Sanitize::Rails::ActionView }
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
initializer 'sanitize-rails.insert_into_active_record' do
|
11
|
+
::ActiveSupport.on_load :active_record do
|
12
|
+
::ActiveRecord::Base.extend Sanitize::Rails::ActiveRecord
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
initializer 'sanitize-rails.insert_into_string' do
|
17
|
+
::String.instance_eval { include Sanitize::Rails::String }
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Sanitize::Rails
|
2
|
+
|
3
|
+
# Adds two `sanitize_as_html{,!}` helpers to String itself,
|
4
|
+
# that call +Engine#clean+ or +Engine#clean!+ in turn
|
5
|
+
#
|
6
|
+
module String
|
7
|
+
# Calls +Engine#clean+ on this String instance
|
8
|
+
#
|
9
|
+
def sanitize_as_html
|
10
|
+
Engine.clean(self)
|
11
|
+
end
|
12
|
+
|
13
|
+
# Calls +Engine#clean!+ on this String instance
|
14
|
+
#
|
15
|
+
def sanitize_as_html!
|
16
|
+
Engine.clean!(self)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module Sanitize::Rails
|
2
|
+
|
3
|
+
# Test instrumentation
|
4
|
+
#
|
5
|
+
module TestHelpers
|
6
|
+
class << self
|
7
|
+
# Instruments the given base class with the +assert_sanitizes+
|
8
|
+
# helper, and memoizes the given options, accessible from the
|
9
|
+
# helper itself via the +valid+ and +invalid+ methods.
|
10
|
+
#
|
11
|
+
# Those methods contains two HTML strings, one assumed to be
|
12
|
+
# "invalid" and the other, well, "valid".
|
13
|
+
#
|
14
|
+
# In your ActiveSupport::Testcase:
|
15
|
+
#
|
16
|
+
# Sanitize::Rails::TestHelpers.setup(self,
|
17
|
+
# :invalid => 'some <a>string',
|
18
|
+
# :valid => 'some <a>string</a>'
|
19
|
+
# )
|
20
|
+
#
|
21
|
+
def setup(base, options = {})
|
22
|
+
base.instance_eval { include TestHelpers }
|
23
|
+
@@options = options
|
24
|
+
end
|
25
|
+
|
26
|
+
def valid; @@options[:valid] rescue nil end
|
27
|
+
def invalid; @@options[:invalid] rescue nil end
|
28
|
+
end
|
29
|
+
|
30
|
+
# Verifies that the given `klass` sanitizes the given `fields`, by
|
31
|
+
# checking both the presence of the sanitize callback and that it
|
32
|
+
# works as expected, by setting the +invalid+ string first, invoking
|
33
|
+
# the callback and then checking that the string has been changed
|
34
|
+
# into the +valid+ one.
|
35
|
+
#
|
36
|
+
# If you pass an Hash as the last argument, it can contain `:valid`,
|
37
|
+
# `:invalid` and `:object` keys. The first two ones override the
|
38
|
+
# configured defaults, while the third executes assertions on the
|
39
|
+
# specified object. If no :object is given, a new object is instantiated
|
40
|
+
# by the given `klass` with no arguments.
|
41
|
+
#
|
42
|
+
# If neither `:valid`/`:invalid` strings are configured nor are passed
|
43
|
+
# via the options, the two default strings in the method source are
|
44
|
+
# used.
|
45
|
+
#
|
46
|
+
def assert_sanitizes(klass, *fields)
|
47
|
+
options = fields.extract_options!
|
48
|
+
sanitizer = Engine.method_for(fields)
|
49
|
+
|
50
|
+
# Verify the callback works
|
51
|
+
invalid = options[:invalid] || TestHelpers.invalid || '<b>ntani<br>'
|
52
|
+
valid = options[:valid] || TestHelpers.valid || '<b>ntani<br /></b>'
|
53
|
+
object = options[:object] || klass.new
|
54
|
+
|
55
|
+
fields.each {|field| object.send("#{field}=", invalid) }
|
56
|
+
|
57
|
+
object.send sanitizer
|
58
|
+
|
59
|
+
fields.each {|field| assert_equal(valid, object.send(field)) }
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
data/lib/sanitize/rails.rb
CHANGED
@@ -1,9 +1,21 @@
|
|
1
|
-
#
|
1
|
+
#
|
2
|
+
# Sanitize Gem Bridge and Helpers for Rails
|
3
|
+
#
|
4
|
+
# https://github.com/vjt/sanitize-rails
|
5
|
+
#
|
6
|
+
# (C) 2011-2014 vjt@openssl.it
|
7
|
+
#
|
8
|
+
# MIT License
|
2
9
|
#
|
3
10
|
require 'sanitize'
|
4
|
-
require 'sanitize/railtie' if defined? Rails
|
11
|
+
require 'sanitize/rails/railtie' if defined? Rails
|
5
12
|
|
6
13
|
module Sanitize::Rails
|
14
|
+
autoload :Engine, 'sanitize/rails/engine'
|
15
|
+
autoload :ActionView, 'sanitize/rails/action_view'
|
16
|
+
autoload :ActiveRecord, 'sanitize/rails/active_record'
|
17
|
+
autoload :String, 'sanitize/rails/string'
|
18
|
+
autoload :VERSION, 'sanitize/rails/version'
|
7
19
|
|
8
20
|
# Configures the sanitizer with the given `config` hash.
|
9
21
|
#
|
@@ -18,190 +30,4 @@ module Sanitize::Rails
|
|
18
30
|
def self.configure(config)
|
19
31
|
Engine.configure(config)
|
20
32
|
end
|
21
|
-
|
22
|
-
module Engine
|
23
|
-
extend self
|
24
|
-
|
25
|
-
def configure(config)
|
26
|
-
@@config = config.freeze
|
27
|
-
end
|
28
|
-
|
29
|
-
# Returns a memoized instance of the Engine with the
|
30
|
-
# configuration passed to the +configure+ method or with
|
31
|
-
# the ActionView's default config
|
32
|
-
#
|
33
|
-
def cleaner
|
34
|
-
@@config ||= begin
|
35
|
-
{
|
36
|
-
:elements => ::ActionView::Base.sanitized_allowed_tags.to_a,
|
37
|
-
:attributes => { :all => ::ActionView::Base.sanitized_allowed_attributes.to_a},
|
38
|
-
:protocols => { :all => ::ActionView::Base.sanitized_allowed_protocols.to_a }
|
39
|
-
}
|
40
|
-
rescue
|
41
|
-
warn "ActionView not available, falling back to Sanitize's BASIC config"
|
42
|
-
::Sanitize::Config::BASIC
|
43
|
-
end
|
44
|
-
@sanitizer ||= ::Sanitize.new(@@config)
|
45
|
-
end
|
46
|
-
|
47
|
-
# Returns a copy of the given `string` after sanitizing it and marking it
|
48
|
-
# as `html_safe`
|
49
|
-
#
|
50
|
-
# Ensuring this methods return instances of ActiveSupport::SafeBuffer
|
51
|
-
# means that text passed through `Sanitize::Rails::Engine.clean`
|
52
|
-
# will not be escaped by ActionView's XSS filtering utilities.
|
53
|
-
def clean(string)
|
54
|
-
::ActiveSupport::SafeBuffer.new string.to_s.dup.tap { |s| clean!(s) }
|
55
|
-
end
|
56
|
-
|
57
|
-
# Sanitizes the given `string` in place and does NOT mark it as `html_safe`
|
58
|
-
#
|
59
|
-
def clean!(string)
|
60
|
-
cleaner.clean!(string.to_s).to_s
|
61
|
-
end
|
62
|
-
|
63
|
-
def callback_for(options) #:nodoc:
|
64
|
-
point = (options[:on] || 'save').to_s
|
65
|
-
|
66
|
-
unless %w( save create ).include?(point)
|
67
|
-
raise ArgumentError, "Invalid callback point #{point}, valid ones are :save and :create"
|
68
|
-
end
|
69
|
-
|
70
|
-
"before_#{point}".intern
|
71
|
-
end
|
72
|
-
|
73
|
-
def method_for(fields) #:nodoc:
|
74
|
-
"sanitize_#{fields.join('_')}".intern
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
module ActionView
|
79
|
-
def self.included(base)
|
80
|
-
base.class_eval do
|
81
|
-
# To make sure we're called *after* the method is defined
|
82
|
-
undef_method :sanitize
|
83
|
-
|
84
|
-
# Overrides ActionView's sanitize() helper to use +Engine#clean+
|
85
|
-
#
|
86
|
-
# FIXME: Options are currently ignored.
|
87
|
-
#
|
88
|
-
def sanitize(string, options = {})
|
89
|
-
Engine.clean(string)
|
90
|
-
end
|
91
|
-
end
|
92
|
-
end
|
93
|
-
|
94
|
-
end
|
95
|
-
|
96
|
-
# Adds the +sanitizes+ method to ActiveRecord children classes
|
97
|
-
#
|
98
|
-
module ActiveRecord
|
99
|
-
# Generates before_save/before_create filters that implement
|
100
|
-
# sanitization on the given fields, in the given callback
|
101
|
-
# point.
|
102
|
-
#
|
103
|
-
# Usage:
|
104
|
-
#
|
105
|
-
# sanitizes :some_field, :some_other_field #, :on => :save
|
106
|
-
#
|
107
|
-
# Valid callback points are :save and :create, callbacks are installed "before_"
|
108
|
-
# by default. Generated callbacks are named with the "sanitize_" prefix follwed
|
109
|
-
# by the field names separated by an underscore.
|
110
|
-
#
|
111
|
-
def sanitizes(*fields)
|
112
|
-
options = fields.extract_options!
|
113
|
-
callback = Engine.callback_for(options)
|
114
|
-
sanitizer = Engine.method_for(fields)
|
115
|
-
|
116
|
-
define_method(sanitizer) do # # Unrolled version
|
117
|
-
fields.each do |field| #
|
118
|
-
value = send(field)
|
119
|
-
unless value.blank? # def sanitize_fieldA_fieldB
|
120
|
-
sanitized = Engine.clean(value) # self.fieldA = Engine.clean(self.fieldA) unless fieldA.blank?
|
121
|
-
send("#{field}=", sanitized) # self.fieldB = Engine.clean(self.fieldB) unless fieldB.blank?
|
122
|
-
end # end
|
123
|
-
end #
|
124
|
-
end # end
|
125
|
-
|
126
|
-
protected sanitizer # protected :sanitize_fieldA_fieldB
|
127
|
-
send callback, sanitizer # before_save :sanitize_fieldA_fieldB
|
128
|
-
end
|
129
|
-
end
|
130
|
-
|
131
|
-
# Adds two `sanitize_as_html{,!}` helpers to String itself,
|
132
|
-
# that call +Engine#clean+ or +Engine#clean!+ in turn
|
133
|
-
#
|
134
|
-
module String
|
135
|
-
# Calls +Engine#clean+ on this String instance
|
136
|
-
#
|
137
|
-
def sanitize_as_html
|
138
|
-
Engine.clean(self)
|
139
|
-
end
|
140
|
-
|
141
|
-
# Calls +Engine#clean!+ on this String instance
|
142
|
-
#
|
143
|
-
def sanitize_as_html!
|
144
|
-
Engine.clean!(self)
|
145
|
-
end
|
146
|
-
end
|
147
|
-
|
148
|
-
# Test instrumentation
|
149
|
-
#
|
150
|
-
module TestHelpers
|
151
|
-
class << self
|
152
|
-
# Instruments the given base class with the +assert_sanitizes+
|
153
|
-
# helper, and memoizes the given options, accessible from the
|
154
|
-
# helper itself via the +valid+ and +invalid+ methods.
|
155
|
-
#
|
156
|
-
# Those methods contains two HTML strings, one assumed to be
|
157
|
-
# "invalid" and the other, well, "valid".
|
158
|
-
#
|
159
|
-
# In your ActiveSupport::Testcase:
|
160
|
-
#
|
161
|
-
# Sanitize::Rails::TestHelpers.setup(self,
|
162
|
-
# :invalid => 'some <a>string',
|
163
|
-
# :valid => 'some <a>string</a>'
|
164
|
-
# )
|
165
|
-
#
|
166
|
-
def setup(base, options = {})
|
167
|
-
base.instance_eval { include TestHelpers }
|
168
|
-
@@options = options
|
169
|
-
end
|
170
|
-
|
171
|
-
def valid; @@options[:valid] rescue nil end
|
172
|
-
def invalid; @@options[:invalid] rescue nil end
|
173
|
-
end
|
174
|
-
|
175
|
-
# Verifies that the given `klass` sanitizes the given `fields`, by
|
176
|
-
# checking both the presence of the sanitize callback and that it
|
177
|
-
# works as expected, by setting the +invalid+ string first, invoking
|
178
|
-
# the callback and then checking that the string has been changed
|
179
|
-
# into the +valid+ one.
|
180
|
-
#
|
181
|
-
# If you pass an Hash as the last argument, it can contain `:valid`,
|
182
|
-
# `:invalid` and `:object` keys. The first two ones override the
|
183
|
-
# configured defaults, while the third executes assertions on the
|
184
|
-
# specified object. If no :object is given, a new object is instantiated
|
185
|
-
# by the given `klass` with no arguments.
|
186
|
-
#
|
187
|
-
# If neither `:valid`/`:invalid` strings are configured nor are passed
|
188
|
-
# via the options, the two default strings in the method source are
|
189
|
-
# used.
|
190
|
-
#
|
191
|
-
def assert_sanitizes(klass, *fields)
|
192
|
-
options = fields.extract_options!
|
193
|
-
sanitizer = Engine.method_for(fields)
|
194
|
-
|
195
|
-
# Verify the callback works
|
196
|
-
invalid = options[:invalid] || TestHelpers.invalid || '<b>ntani<br>'
|
197
|
-
valid = options[:valid] || TestHelpers.valid || '<b>ntani<br /></b>'
|
198
|
-
object = options[:object] || klass.new
|
199
|
-
|
200
|
-
fields.each {|field| object.send("#{field}=", invalid) }
|
201
|
-
|
202
|
-
object.send sanitizer
|
203
|
-
|
204
|
-
fields.each {|field| assert_equal(valid, object.send(field)) }
|
205
|
-
end
|
206
|
-
end
|
207
33
|
end
|
data/sanitize-rails.gemspec
CHANGED
@@ -7,9 +7,9 @@ require 'sanitize/rails/version'
|
|
7
7
|
Gem::Specification.new do |s|
|
8
8
|
s.name = "sanitize-rails"
|
9
9
|
s.version = Sanitize::Rails::VERSION
|
10
|
-
s.date = "2014-
|
11
|
-
s.authors = ["Marcello Barnaba", "Damien Wilson"]
|
12
|
-
s.email = ["vjt@openssl.it", "damien@mindglob.com"]
|
10
|
+
s.date = "2014-06-05"
|
11
|
+
s.authors = ["Marcello Barnaba", "Damien Wilson", "Fabio Napoleoni"]
|
12
|
+
s.email = ["vjt@openssl.it", "damien@mindglob.com", "f.napoleoni@gmail.com"]
|
13
13
|
s.homepage = "http://github.com/vjt/sanitize-rails"
|
14
14
|
s.summary = "A sanitizer bridge for Rails applications"
|
15
15
|
s.license = "MIT"
|
@@ -1,9 +1,5 @@
|
|
1
1
|
require 'test_helper'
|
2
2
|
|
3
|
-
require 'action_view'
|
4
|
-
require 'sanitize'
|
5
|
-
require 'sanitize/rails'
|
6
|
-
|
7
3
|
# Test suite for Sanitize::Rails::Engine
|
8
4
|
class SanitizeRailsStringExtensionTest < Minitest::Test
|
9
5
|
SanitizableString = Class.new(String) { include Sanitize::Rails::String }
|
data/test/test_helper.rb
CHANGED
metadata
CHANGED
@@ -1,15 +1,16 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sanitize-rails
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.9.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Marcello Barnaba
|
8
8
|
- Damien Wilson
|
9
|
+
- Fabio Napoleoni
|
9
10
|
autorequire:
|
10
11
|
bindir: bin
|
11
12
|
cert_chain: []
|
12
|
-
date: 2014-
|
13
|
+
date: 2014-06-05 00:00:00.000000000 Z
|
13
14
|
dependencies:
|
14
15
|
- !ruby/object:Gem::Dependency
|
15
16
|
name: rails
|
@@ -43,6 +44,7 @@ description:
|
|
43
44
|
email:
|
44
45
|
- vjt@openssl.it
|
45
46
|
- damien@mindglob.com
|
47
|
+
- f.napoleoni@gmail.com
|
46
48
|
executables: []
|
47
49
|
extensions: []
|
48
50
|
extra_rdoc_files: []
|
@@ -50,12 +52,19 @@ files:
|
|
50
52
|
- ".gitignore"
|
51
53
|
- ".travis.yml"
|
52
54
|
- Gemfile
|
55
|
+
- LICENSE
|
53
56
|
- README.md
|
54
57
|
- Rakefile
|
55
58
|
- example/sanitizer.rb
|
56
59
|
- lib/sanitize/rails.rb
|
60
|
+
- lib/sanitize/rails/action_view.rb
|
61
|
+
- lib/sanitize/rails/active_record.rb
|
62
|
+
- lib/sanitize/rails/engine.rb
|
63
|
+
- lib/sanitize/rails/matchers.rb
|
64
|
+
- lib/sanitize/rails/railtie.rb
|
65
|
+
- lib/sanitize/rails/string.rb
|
66
|
+
- lib/sanitize/rails/test_helpers.rb
|
57
67
|
- lib/sanitize/rails/version.rb
|
58
|
-
- lib/sanitize/railtie.rb
|
59
68
|
- sanitize-rails.gemspec
|
60
69
|
- test/sanitize_rails_engine_test.rb
|
61
70
|
- test/sanitize_rails_string_extension_test.rb
|
data/lib/sanitize/railtie.rb
DELETED
@@ -1,19 +0,0 @@
|
|
1
|
-
require 'sanitize/rails'
|
2
|
-
|
3
|
-
class Sanitize::Railtie < ::Rails::Railtie
|
4
|
-
initializer 'sanitize-rails.insert_into_action_view' do
|
5
|
-
ActiveSupport.on_load :action_view do
|
6
|
-
ActionView::Helpers::SanitizeHelper.instance_eval { include Sanitize::Rails::ActionView }
|
7
|
-
end
|
8
|
-
end
|
9
|
-
|
10
|
-
initializer 'sanitize-rails.insert_into_active_record' do
|
11
|
-
ActiveSupport.on_load :active_record do
|
12
|
-
ActiveRecord::Base.extend Sanitize::Rails::ActiveRecord
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
|
-
initializer 'sanitie-rails.insert_into_string' do
|
17
|
-
::String.instance_eval { include Sanitize::Rails::String }
|
18
|
-
end
|
19
|
-
end
|