sanitize-rails 0.8.1 → 0.9.1
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.
- 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. [](https://travis-ci.org/vjt/sanitize-rails)
|
2
|
-
======================================
|
1
|
+
# Sanitize-Rails - sanitize .. on Rails. [](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
|