sanitize-whitelist 0.0.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: e5b9340d1fdf955406ddf07541231b65e77c936b
4
+ data.tar.gz: 4b3963b3848cb1fd5b90b53d780d89b25232974b
5
+ SHA512:
6
+ metadata.gz: e49f199fba770fcddc0b2b442d8224dc35afddbdfe1940e3b508d4363d0638d60d48cdb8c16116a184ca63de633a5ce2ccbee43ab7d15d123faa0e638cc3ff40
7
+ data.tar.gz: faef079c1b47f2ae5556fa256441417814cbc8dc608eed164f49d73c36f07b5882ed714d021515183bcee835c6b3403bd6080ac8493c316dce45595534a53ba5
data/.gitignore ADDED
@@ -0,0 +1,22 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ *.bundle
19
+ *.so
20
+ *.o
21
+ *.a
22
+ mkmf.log
data/Gemfile ADDED
@@ -0,0 +1,11 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ group :guard do
6
+ gem 'guard-rspec'
7
+ gem 'guard-bundler'
8
+ gem 'rb-fsevent'
9
+ end
10
+
11
+ gem "byebug"
data/Guardfile ADDED
@@ -0,0 +1,9 @@
1
+ guard "bundler" do
2
+ watch("Gemfile")
3
+ end
4
+
5
+ guard "rspec" do
6
+ watch(%r{^spec/.+_spec\.rb$})
7
+ watch(%r{^spec/spec_helper.rb$}) { "spec" }
8
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
9
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Brandon Keepers
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,85 @@
1
+ # Sanitize::Whitelist
2
+
3
+ Objects to represent a whitelist that can be used by the sanitize gem.
4
+
5
+ Problem: the `sanitize` gem uses a deeply nested hash to configure sanitization. It is cumbersome to inherit and modify sanitization configuration without modifying the original hash.
6
+
7
+ This wraps it with real objects, which means:
8
+
9
+ - The entire whitelist is frozen after the yielded block.
10
+ - #dup behaves as expected and returns a deep clone
11
+ - #to_hash creates a hash that can be passed to the sanitize gem
12
+
13
+ ## Installation
14
+
15
+ Add this line to your application's Gemfile:
16
+
17
+ gem 'sanitize-whitelist'
18
+
19
+ And then execute:
20
+
21
+ $ bundle
22
+
23
+ Or install it yourself as:
24
+
25
+ $ gem install sanitize-whitelist
26
+
27
+ ## Usage
28
+
29
+ ```ruby
30
+ Whitelist.new do
31
+ # Explicitly declare elements that are allowed.
32
+ allow %w(
33
+ h1 h2 h3 h4 h5 h6 h7 h8 br b i strong em a pre code img tt
34
+ div ins del sup sub p ol ul table thead tbody tfoot blockquote
35
+ dl dt dd kbd q samp var hr ruby rt rp li tr td th s strike
36
+ )
37
+
38
+ # Elements to completely remove instead of escape.
39
+ remove "script"
40
+
41
+ # Allow href and src attributes, and specify the protocols that they can use.
42
+ element("a").allow("href").protocols('http', 'https', 'mailto', :relative, 'github-windows', 'github-mac')
43
+ element("img").allow("src").protocols('http', 'https', :relative)
44
+
45
+ # Allow other elements on divs
46
+ element("div").allow %w(itemscope itemtype)
47
+
48
+ # All elements can have these attributes
49
+ element(:all).allow %w(
50
+ abbr accept accept-charset accesskey action align alt axis border
51
+ cellpadding cellspacing char charoff charset checked cite clear cols
52
+ colspan color compact coords datetime dir disabled enctype for frame
53
+ headers height hreflang hspace ismap label lang longdesc maxlength media
54
+ method multiple name nohref noshade nowrap prompt readonly rel rev rows
55
+ rowspan rules scope selected shape size span start summary tabindex target
56
+ title type usemap valign value vspace width itempro
57
+ )
58
+
59
+ # Top-level <li> elements are removed because they can break out of
60
+ # containing markup.
61
+ transform do |env|
62
+ name, node = env[:node_name], env[:node]
63
+ if name == "li" && !node.ancestors.any?{ |n| %w(ul ol).include?(n.name) }
64
+ node.replace(node.children)
65
+ end
66
+ end
67
+
68
+ # Table child elements that are not contained by a <table> are removed.
69
+ # Otherwise they can be used to break out of containing markup.
70
+ transform do |env|
71
+ name, node = env[:node_name], env[:node]
72
+ if (%w(thead tbody tfoot).include?(name) || %w(tr td th).include?(name)) && !node.ancestors.any? { |n| n.name == "table" }
73
+ node.replace(node.children)
74
+ end
75
+ end
76
+ end
77
+ ```
78
+
79
+ ## Contributing
80
+
81
+ 1. Fork it ( https://github.com/bkeepers/sanitize-whitelist/fork )
82
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
83
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
84
+ 4. Push to the branch (`git push origin my-new-feature`)
85
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
@@ -0,0 +1,98 @@
1
+ class Sanitize
2
+ class Whitelist
3
+ attr_reader :elements, :transformers
4
+
5
+ def initialize(&block)
6
+ @elements = {}
7
+ @transformers = []
8
+ eval_block(&block)
9
+ freeze
10
+ end
11
+
12
+ def freeze
13
+ super
14
+ @elements.freeze.values.each(&:freeze)
15
+ @transformers.freeze
16
+ end
17
+
18
+ def initialize_dup(original)
19
+ @elements = original.elements.each_with_object({}) do |(name,element), elements|
20
+ elements[name] = element.dup
21
+ end
22
+ @transformers = original.transformers.dup
23
+ super
24
+ end
25
+
26
+ def dup(&block)
27
+ super.tap do |object|
28
+ object.eval_block(&block)
29
+ freeze
30
+ end
31
+ end
32
+
33
+ def eval_block(&block)
34
+ block.arity == 1 ? block.call(self) : instance_eval(&block) if block
35
+ end
36
+
37
+ def allow(elements)
38
+ Array(elements).map { |name| element(name) }
39
+ end
40
+
41
+ def remove(boolean_or_elements)
42
+ Array(boolean_or_elements).map { |name| element(name).remove! }
43
+ end
44
+
45
+ def remove_non_whitelisted!
46
+ @remove_non_whitelisted = true
47
+ end
48
+
49
+ def escape_non_whitelisted!
50
+ @remove_non_whitelisted = false
51
+ end
52
+
53
+ def element(name)
54
+ @elements[name] ||= Sanitize::Whitelist::Element.new(name)
55
+ end
56
+
57
+ def transform(&block)
58
+ @transformers << block
59
+ end
60
+
61
+ def allowed_elements
62
+ @elements.values.select(&:allowed?)
63
+ end
64
+
65
+ def remove_elements
66
+ @elements.values.select(&:remove?)
67
+ end
68
+
69
+ def to_hash
70
+ {}.tap do |result|
71
+ result[:elements] = allowed_elements.map(&:name)
72
+
73
+ if @remove_non_whitelisted
74
+ result[:remove_contents] = @remove_non_whitelisted
75
+ else
76
+ elements = remove_elements.map(&:name)
77
+ result[:remove_contents] = elements unless elements.empty?
78
+ end
79
+
80
+ result[:transformers] = @transformers unless @transformers.empty?
81
+
82
+ attributes = allowed_elements.each_with_object({}) do |element,attrs|
83
+ attrs.merge! element.to_hash
84
+ end
85
+ result[:attributes] = attributes unless attributes.empty?
86
+
87
+ protocols = allowed_elements.each_with_object({}) do |element,attrs|
88
+ attrs.merge! element.to_protocols_hash
89
+ end
90
+ result[:protocols] = protocols unless protocols.empty?
91
+ end
92
+ end
93
+ end
94
+ end
95
+
96
+ require "sanitize/whitelist/version"
97
+ require "sanitize/whitelist/attribute"
98
+ require "sanitize/whitelist/element"
@@ -0,0 +1,18 @@
1
+ class Sanitize::Whitelist::Attribute
2
+ def initialize(name)
3
+ @name = name
4
+ end
5
+
6
+ def protocols(protocols)
7
+ @protocols = Array(protocols)
8
+ end
9
+
10
+ def to_hash
11
+ @protocols ? {@name => @protocols} : {}
12
+ end
13
+
14
+ def freeze
15
+ super
16
+ @protocols.freeze if @protocols
17
+ end
18
+ end
@@ -0,0 +1,55 @@
1
+ class Sanitize::Whitelist::Element
2
+ attr_reader :name, :attributes
3
+
4
+ def initialize(name)
5
+ @name = name
6
+ @attributes = {}
7
+ @allowed = true
8
+ end
9
+
10
+ def freeze
11
+ super
12
+ @attributes.values.each(&:freeze)
13
+ @attributes.freeze
14
+ end
15
+
16
+ def initialize_dup(original)
17
+ super
18
+ @attributes = original.attributes.each_with_object({}) do |(key,value), result|
19
+ result[key] = value.dup
20
+ end
21
+ end
22
+
23
+ def allow!
24
+ @allowed = true
25
+ end
26
+
27
+ def allowed?
28
+ !!@allowed
29
+ end
30
+
31
+ def remove!
32
+ @allowed = false
33
+ end
34
+
35
+ def remove?
36
+ !@allowed
37
+ end
38
+
39
+ def allow(attributes)
40
+ result = Array(attributes).map do |attr|
41
+ @attributes[attr] = Sanitize::Whitelist::Attribute.new(attr)
42
+ end
43
+ result.size == 1 ? result.first : result
44
+ end
45
+
46
+ def to_hash
47
+ @attributes.empty? ? {} : {@name => @attributes.keys}
48
+ end
49
+
50
+ def to_protocols_hash
51
+ @attributes.values.each_with_object({}) do |attribute, result|
52
+ result.merge! @name => attribute.to_hash unless attribute.to_hash.empty?
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,5 @@
1
+ class Sanitize
2
+ class Whitelist
3
+ VERSION = "0.0.1"
4
+ end
5
+ end
@@ -0,0 +1,24 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'sanitize/whitelist/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "sanitize-whitelist"
8
+ spec.version = Sanitize::Whitelist::VERSION
9
+ spec.authors = ["Brandon Keepers"]
10
+ spec.email = ["brandon@opensoul.org"]
11
+ spec.summary = %q{Objects to represent a whitelist that can be used by the sanitize gem.}
12
+ spec.description = %q{Objects to represent a whitelist that can be used by the sanitize gem.}
13
+ spec.homepage = "https://github.com/bkeepers/sanitize-whitelist"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.6"
22
+ spec.add_development_dependency "rake"
23
+ spec.add_development_dependency "rspec"
24
+ end
@@ -0,0 +1,160 @@
1
+ require "spec_helper"
2
+
3
+ describe Sanitize::Whitelist do
4
+ describe "initialize" do
5
+ it "instance_evals block with arity of 0" do
6
+ whitelist = Sanitize::Whitelist.new { allow "div" }
7
+ expect(whitelist.to_hash).to eq({:elements => ["div"]})
8
+ end
9
+
10
+ it "yields with self as arg if arity of 1" do
11
+ self_in_block = nil
12
+ arg_in_block = nil
13
+ whitelist = Sanitize::Whitelist.new do |w|
14
+ w.allow "div"
15
+ arg_in_block = w
16
+ self_in_block = self
17
+ end
18
+ expect(whitelist.to_hash).to eq({:elements => ["div"]})
19
+ expect(arg_in_block).to be(whitelist)
20
+ expect(self_in_block).to be(self)
21
+ end
22
+
23
+ it "freezes elements after initialization" do
24
+ whitelist = Sanitize::Whitelist.new do
25
+ allow "div"
26
+ element("a").allow("href")
27
+ end
28
+ expect { whitelist.allow("p") }.to raise_error(RuntimeError, /frozen/)
29
+ expect { whitelist.element("div").allow("class") }.to raise_error(RuntimeError, /frozen/)
30
+ expect { whitelist.element("a").allow("href").protocols("http") }.to raise_error(RuntimeError, /frozen/)
31
+ end
32
+ end
33
+
34
+ describe "allow" do
35
+ it "allows nothing by default" do
36
+ whitelist = Sanitize::Whitelist.new
37
+ expect(whitelist.to_hash).to eq({:elements => []})
38
+ end
39
+
40
+ it "adds a single element" do
41
+ whitelist = Sanitize::Whitelist.new { allow "div" }
42
+ expect(whitelist.to_hash).to eq({:elements => ["div"]})
43
+ end
44
+
45
+ it "adds an array of elements" do
46
+ whitelist = Sanitize::Whitelist.new { allow %w(div p) }
47
+ expect(whitelist.to_hash).to eq({:elements => %w(div p)})
48
+ end
49
+
50
+ it "adds multiple calls" do
51
+ whitelist = Sanitize::Whitelist.new do
52
+ allow "p"
53
+ allow "a"
54
+ end
55
+ expect(whitelist.to_hash).to eq({:elements => %w(p a)})
56
+ end
57
+ end
58
+
59
+ describe "remove" do
60
+ it "adds a single element" do
61
+ whitelist = Sanitize::Whitelist.new { remove "script" }
62
+ expect(whitelist.to_hash).to eq({:remove_contents => %w(script), :elements => []})
63
+ end
64
+
65
+ it "adds an array of elements" do
66
+ whitelist = Sanitize::Whitelist.new { remove %w(script object) }
67
+ expect(whitelist.to_hash).to eq({:remove_contents => %w(script object), :elements => []})
68
+ end
69
+ end
70
+
71
+ describe "remove_non_whitelisted!" do
72
+ it "sets remove_contents to true" do
73
+ whitelist = Sanitize::Whitelist.new { remove_non_whitelisted! }
74
+ expect(whitelist.to_hash).to eq({:remove_contents => true, :elements => []})
75
+ end
76
+ end
77
+
78
+ describe "element#allow" do
79
+ it "adds a single attribute" do
80
+ whitelist = Sanitize::Whitelist.new { element("a").allow("href") }
81
+ expect(whitelist.to_hash).to eq({:elements => ["a"], :attributes => {"a" => ["href"]}})
82
+ end
83
+ end
84
+
85
+ describe "attribute#protocols" do
86
+ it "adds a single protocol" do
87
+ whitelist = Sanitize::Whitelist.new { element("a").allow("href").protocols("https") }
88
+ expect(whitelist.to_hash).to eq({
89
+ :protocols => {"a" => {"href" => ["https"]}},
90
+ :elements => ["a"], :attributes => {"a" => ["href"]}
91
+ })
92
+ end
93
+
94
+ it "adds multiple protocols" do
95
+ whitelist = Sanitize::Whitelist.new do
96
+ element("a").allow("href").protocols(%w(http https ftp))
97
+ end
98
+ expect(whitelist.to_hash).to eq({
99
+ :protocols => {"a" => {"href" => %w(http https ftp)}},
100
+ :elements => ["a"], :attributes => {"a" => ["href"]}
101
+ })
102
+ end
103
+
104
+ it "accepts empty protocols" do
105
+ whitelist = Sanitize::Whitelist.new do
106
+ element("a").allow("href").protocols([])
107
+ end
108
+ expect(whitelist.to_hash).to eq({
109
+ :protocols => {"a" => {"href" => []}},
110
+ :elements => ["a"], :attributes => {"a" => ["href"]}
111
+ })
112
+ end
113
+ end
114
+
115
+ describe "transform" do
116
+ it "adds block to transformers" do
117
+ block = lambda { }
118
+ whitelist = Sanitize::Whitelist.new { transform(&block) }
119
+ expect(whitelist.to_hash).to eq({:transformers => [block], :elements => []})
120
+ end
121
+ end
122
+
123
+ describe "dup" do
124
+ let(:whitelist) { Sanitize::Whitelist.new { allow "p" } }
125
+
126
+ it "dups elements" do
127
+ dup = whitelist.dup { allow "div" }
128
+ expect(dup.to_hash).to eq({:elements => ["p", "div"]})
129
+ expect(whitelist.to_hash).to eq({:elements => ["p"]})
130
+ end
131
+
132
+ it "does not remove from original" do
133
+ dup = whitelist.dup { remove "p" }
134
+ expect(dup.to_hash).to eq({:elements => [], :remove_contents => ["p"]})
135
+ expect(whitelist.to_hash).to eq({:elements => ["p"]})
136
+ end
137
+
138
+ it "dups attributes" do
139
+ dup = whitelist.dup { element("p").allow("class") }
140
+ expect(dup.to_hash).to eq({:elements => ["p"], :attributes => {"p" => ["class"]}})
141
+ expect(whitelist.to_hash).to eq({:elements => ["p"]})
142
+ end
143
+
144
+ it "dups protocols" do
145
+ whitelist = Sanitize::Whitelist.new { element("a").allow("href").protocols("ftp") }
146
+ dup = whitelist.dup { element("a").allow("href").protocols("https") }
147
+
148
+ expect(dup.to_hash).to eq({
149
+ :elements => ["a"],
150
+ :attributes => {"a" => ["href"]},
151
+ :protocols => {"a" => {"href" => ["https"]}}
152
+ })
153
+ expect(whitelist.to_hash).to eq({
154
+ :elements => ["a"],
155
+ :attributes => {"a" => ["href"]},
156
+ :protocols => {"a" => {"href" => ["ftp"]}}
157
+ })
158
+ end
159
+ end
160
+ end
@@ -0,0 +1 @@
1
+ require "sanitize/whitelist"
metadata ADDED
@@ -0,0 +1,101 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sanitize-whitelist
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Brandon Keepers
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-10-01 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.6'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.6'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ description: Objects to represent a whitelist that can be used by the sanitize gem.
56
+ email:
57
+ - brandon@opensoul.org
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - ".gitignore"
63
+ - Gemfile
64
+ - Guardfile
65
+ - LICENSE.txt
66
+ - README.md
67
+ - Rakefile
68
+ - lib/sanitize/whitelist.rb
69
+ - lib/sanitize/whitelist/attribute.rb
70
+ - lib/sanitize/whitelist/element.rb
71
+ - lib/sanitize/whitelist/version.rb
72
+ - sanitize-whitelist.gemspec
73
+ - spec/sanitize/whitelist_spec.rb
74
+ - spec/spec_helper.rb
75
+ homepage: https://github.com/bkeepers/sanitize-whitelist
76
+ licenses:
77
+ - MIT
78
+ metadata: {}
79
+ post_install_message:
80
+ rdoc_options: []
81
+ require_paths:
82
+ - lib
83
+ required_ruby_version: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - ">="
86
+ - !ruby/object:Gem::Version
87
+ version: '0'
88
+ required_rubygems_version: !ruby/object:Gem::Requirement
89
+ requirements:
90
+ - - ">="
91
+ - !ruby/object:Gem::Version
92
+ version: '0'
93
+ requirements: []
94
+ rubyforge_project:
95
+ rubygems_version: 2.2.2
96
+ signing_key:
97
+ specification_version: 4
98
+ summary: Objects to represent a whitelist that can be used by the sanitize gem.
99
+ test_files:
100
+ - spec/sanitize/whitelist_spec.rb
101
+ - spec/spec_helper.rb