rails-html-sanitizer 1.4.3 → 1.6.0
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.
Potentially problematic release.
This version of rails-html-sanitizer might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +114 -0
- data/MIT-LICENSE +1 -1
- data/README.md +163 -34
- data/lib/rails/html/sanitizer/version.rb +4 -2
- data/lib/rails/html/sanitizer.rb +371 -121
- data/lib/rails/html/scrubbers.rb +78 -78
- data/lib/rails-html-sanitizer.rb +7 -23
- data/test/rails_api_test.rb +88 -0
- data/test/sanitizer_test.rb +925 -505
- data/test/scrubbers_test.rb +57 -30
- metadata +19 -57
data/lib/rails/html/scrubbers.rb
CHANGED
@@ -1,10 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Rails
|
2
|
-
module
|
3
|
-
# === Rails::
|
4
|
+
module HTML
|
5
|
+
# === Rails::HTML::PermitScrubber
|
4
6
|
#
|
5
|
-
# +Rails::
|
7
|
+
# +Rails::HTML::PermitScrubber+ allows you to permit only your own tags and/or attributes.
|
6
8
|
#
|
7
|
-
# +Rails::
|
9
|
+
# +Rails::HTML::PermitScrubber+ can be subclassed to determine:
|
8
10
|
# - When a node should be skipped via +skip_node?+.
|
9
11
|
# - When a node is allowed via +allowed_node?+.
|
10
12
|
# - When an attribute should be scrubbed via +scrub_attribute?+.
|
@@ -27,7 +29,7 @@ module Rails
|
|
27
29
|
# If set, attributes excluded will be removed.
|
28
30
|
# If not, attributes are removed based on Loofahs +HTML5::Scrub.scrub_attributes+.
|
29
31
|
#
|
30
|
-
# class CommentScrubber <
|
32
|
+
# class CommentScrubber < Rails::HTML::PermitScrubber
|
31
33
|
# def initialize
|
32
34
|
# super
|
33
35
|
# self.tags = %w(form script comment blockquote)
|
@@ -45,10 +47,11 @@ module Rails
|
|
45
47
|
# See the documentation for +Nokogiri::XML::Node+ to understand what's possible
|
46
48
|
# with nodes: https://nokogiri.org/rdoc/Nokogiri/XML/Node.html
|
47
49
|
class PermitScrubber < Loofah::Scrubber
|
48
|
-
attr_reader :tags, :attributes
|
50
|
+
attr_reader :tags, :attributes, :prune
|
49
51
|
|
50
|
-
def initialize
|
51
|
-
@
|
52
|
+
def initialize(prune: false)
|
53
|
+
@prune = prune
|
54
|
+
@direction = @prune ? :top_down : :bottom_up
|
52
55
|
@tags, @attributes = nil, nil
|
53
56
|
end
|
54
57
|
|
@@ -61,9 +64,9 @@ module Rails
|
|
61
64
|
end
|
62
65
|
|
63
66
|
def scrub(node)
|
64
|
-
if
|
65
|
-
|
66
|
-
node.replace
|
67
|
+
if Loofah::HTML5::Scrub.cdata_needs_escaping?(node)
|
68
|
+
replacement = Loofah::HTML5::Scrub.cdata_escape(node)
|
69
|
+
node.replace(replacement)
|
67
70
|
return CONTINUE
|
68
71
|
end
|
69
72
|
return CONTINUE if skip_node?(node)
|
@@ -76,92 +79,89 @@ module Rails
|
|
76
79
|
end
|
77
80
|
|
78
81
|
protected
|
82
|
+
def allowed_node?(node)
|
83
|
+
@tags.include?(node.name)
|
84
|
+
end
|
79
85
|
|
80
|
-
|
81
|
-
|
82
|
-
|
86
|
+
def skip_node?(node)
|
87
|
+
node.text?
|
88
|
+
end
|
83
89
|
|
84
|
-
|
85
|
-
|
86
|
-
|
90
|
+
def scrub_attribute?(name)
|
91
|
+
!@attributes.include?(name)
|
92
|
+
end
|
87
93
|
|
88
|
-
|
89
|
-
|
90
|
-
|
94
|
+
def keep_node?(node)
|
95
|
+
if @tags
|
96
|
+
allowed_node?(node)
|
97
|
+
else
|
98
|
+
Loofah::HTML5::Scrub.allowed_element?(node.name)
|
99
|
+
end
|
100
|
+
end
|
91
101
|
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
else
|
96
|
-
Loofah::HTML5::Scrub.allowed_element?(node.name)
|
102
|
+
def scrub_node(node)
|
103
|
+
node.before(node.children) unless prune # strip
|
104
|
+
node.remove
|
97
105
|
end
|
98
|
-
end
|
99
106
|
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
107
|
+
def scrub_attributes(node)
|
108
|
+
if @attributes
|
109
|
+
node.attribute_nodes.each do |attr|
|
110
|
+
attr.remove if scrub_attribute?(attr.name)
|
111
|
+
scrub_attribute(node, attr)
|
112
|
+
end
|
104
113
|
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
attr.remove if scrub_attribute?(attr.name)
|
109
|
-
scrub_attribute(node, attr)
|
114
|
+
scrub_css_attribute(node)
|
115
|
+
else
|
116
|
+
Loofah::HTML5::Scrub.scrub_attributes(node)
|
110
117
|
end
|
111
|
-
|
112
|
-
scrub_css_attribute(node)
|
113
|
-
else
|
114
|
-
Loofah::HTML5::Scrub.scrub_attributes(node)
|
115
118
|
end
|
116
|
-
end
|
117
119
|
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
120
|
+
def scrub_css_attribute(node)
|
121
|
+
if Loofah::HTML5::Scrub.respond_to?(:scrub_css_attribute)
|
122
|
+
Loofah::HTML5::Scrub.scrub_css_attribute(node)
|
123
|
+
else
|
124
|
+
style = node.attributes["style"]
|
125
|
+
style.value = Loofah::HTML5::Scrub.scrub_css(style.value) if style
|
126
|
+
end
|
124
127
|
end
|
125
|
-
end
|
126
128
|
|
127
|
-
|
128
|
-
|
129
|
-
|
129
|
+
def validate!(var, name)
|
130
|
+
if var && !var.is_a?(Enumerable)
|
131
|
+
raise ArgumentError, "You should pass :#{name} as an Enumerable"
|
132
|
+
end
|
133
|
+
var
|
130
134
|
end
|
131
|
-
var
|
132
|
-
end
|
133
135
|
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
136
|
+
def scrub_attribute(node, attr_node)
|
137
|
+
attr_name = if attr_node.namespace
|
138
|
+
"#{attr_node.namespace.prefix}:#{attr_node.node_name}"
|
139
|
+
else
|
140
|
+
attr_node.node_name
|
141
|
+
end
|
142
|
+
|
143
|
+
if Loofah::HTML5::SafeList::ATTR_VAL_IS_URI.include?(attr_name)
|
144
|
+
return if Loofah::HTML5::Scrub.scrub_uri_attribute(attr_node)
|
145
|
+
end
|
146
|
+
|
147
|
+
if Loofah::HTML5::SafeList::SVG_ATTR_VAL_ALLOWS_REF.include?(attr_name)
|
148
|
+
Loofah::HTML5::Scrub.scrub_attribute_that_allows_local_ref(attr_node)
|
149
|
+
end
|
150
|
+
|
151
|
+
if Loofah::HTML5::SafeList::SVG_ALLOW_LOCAL_HREF.include?(node.name) && attr_name == "xlink:href" && attr_node.value =~ /^\s*[^#\s].*/m
|
145
152
|
attr_node.remove
|
146
153
|
end
|
147
|
-
end
|
148
|
-
if Loofah::HTML5::SafeList::SVG_ATTR_VAL_ALLOWS_REF.include?(attr_name)
|
149
|
-
attr_node.value = attr_node.value.gsub(/url\s*\(\s*[^#\s][^)]+?\)/m, ' ') if attr_node.value
|
150
|
-
end
|
151
|
-
if Loofah::HTML5::SafeList::SVG_ALLOW_LOCAL_HREF.include?(node.name) && attr_name == 'xlink:href' && attr_node.value =~ /^\s*[^#\s].*/m
|
152
|
-
attr_node.remove
|
153
|
-
end
|
154
154
|
|
155
|
-
|
155
|
+
node.remove_attribute(attr_node.name) if attr_name == "src" && attr_node.value !~ /[^[:space:]]/
|
156
156
|
|
157
|
-
|
158
|
-
|
157
|
+
Loofah::HTML5::Scrub.force_correct_attribute_escaping! node
|
158
|
+
end
|
159
159
|
end
|
160
160
|
|
161
|
-
# === Rails::
|
161
|
+
# === Rails::HTML::TargetScrubber
|
162
162
|
#
|
163
|
-
# Where +Rails::
|
164
|
-
# sanitization, +Rails::
|
163
|
+
# Where +Rails::HTML::PermitScrubber+ picks out tags and attributes to permit in
|
164
|
+
# sanitization, +Rails::HTML::TargetScrubber+ targets them for removal.
|
165
165
|
#
|
166
166
|
# +tags=+
|
167
167
|
# If set, elements included will be stripped.
|
@@ -178,9 +178,9 @@ module Rails
|
|
178
178
|
end
|
179
179
|
end
|
180
180
|
|
181
|
-
# === Rails::
|
181
|
+
# === Rails::HTML::TextOnlyScrubber
|
182
182
|
#
|
183
|
-
# +Rails::
|
183
|
+
# +Rails::HTML::TextOnlyScrubber+ allows you to permit text nodes.
|
184
184
|
#
|
185
185
|
# Unallowed elements will be stripped, i.e. element is removed but its subtree kept.
|
186
186
|
class TextOnlyScrubber < Loofah::Scrubber
|
data/lib/rails-html-sanitizer.rb
CHANGED
@@ -1,30 +1,14 @@
|
|
1
|
-
|
2
|
-
require "loofah"
|
3
|
-
require "rails/html/scrubbers"
|
4
|
-
require "rails/html/sanitizer"
|
1
|
+
# frozen_string_literal: true
|
5
2
|
|
6
|
-
|
7
|
-
module Html
|
8
|
-
class Sanitizer
|
9
|
-
class << self
|
10
|
-
def full_sanitizer
|
11
|
-
Html::FullSanitizer
|
12
|
-
end
|
3
|
+
require_relative "rails/html/sanitizer/version"
|
13
4
|
|
14
|
-
|
15
|
-
Html::LinkSanitizer
|
16
|
-
end
|
5
|
+
require "loofah"
|
17
6
|
|
18
|
-
|
19
|
-
|
20
|
-
end
|
7
|
+
require_relative "rails/html/scrubbers"
|
8
|
+
require_relative "rails/html/sanitizer"
|
21
9
|
|
22
|
-
|
23
|
-
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
10
|
+
module Rails
|
11
|
+
Html = HTML # :nodoc:
|
28
12
|
end
|
29
13
|
|
30
14
|
module ActionView
|
@@ -0,0 +1,88 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "minitest/autorun"
|
4
|
+
require "rails-html-sanitizer"
|
5
|
+
|
6
|
+
class RailsApiTest < Minitest::Test
|
7
|
+
def test_html_module_name_alias
|
8
|
+
assert_equal(Rails::Html, Rails::HTML)
|
9
|
+
assert_equal("Rails::HTML", Rails::Html.name)
|
10
|
+
assert_equal("Rails::HTML", Rails::HTML.name)
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_html_scrubber_class_names
|
14
|
+
assert(Rails::Html::PermitScrubber)
|
15
|
+
assert(Rails::Html::TargetScrubber)
|
16
|
+
assert(Rails::Html::TextOnlyScrubber)
|
17
|
+
assert(Rails::Html::Sanitizer)
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_best_supported_vendor_when_html5_is_not_supported_returns_html4
|
21
|
+
Rails::HTML::Sanitizer.stub(:html5_support?, false) do
|
22
|
+
assert_equal(Rails::HTML4::Sanitizer, Rails::HTML::Sanitizer.best_supported_vendor)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_best_supported_vendor_when_html5_is_supported_returns_html5
|
27
|
+
skip("no HTML5 support on this platform") unless Rails::HTML::Sanitizer.html5_support?
|
28
|
+
|
29
|
+
Rails::HTML::Sanitizer.stub(:html5_support?, true) do
|
30
|
+
assert_equal(Rails::HTML5::Sanitizer, Rails::HTML::Sanitizer.best_supported_vendor)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_html4_sanitizer_alias_full
|
35
|
+
assert_equal(Rails::HTML4::FullSanitizer, Rails::HTML::FullSanitizer)
|
36
|
+
assert_equal("Rails::HTML4::FullSanitizer", Rails::HTML::FullSanitizer.name)
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_html4_sanitizer_alias_link
|
40
|
+
assert_equal(Rails::HTML4::LinkSanitizer, Rails::HTML::LinkSanitizer)
|
41
|
+
assert_equal("Rails::HTML4::LinkSanitizer", Rails::HTML::LinkSanitizer.name)
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_html4_sanitizer_alias_safe_list
|
45
|
+
assert_equal(Rails::HTML4::SafeListSanitizer, Rails::HTML::SafeListSanitizer)
|
46
|
+
assert_equal("Rails::HTML4::SafeListSanitizer", Rails::HTML::SafeListSanitizer.name)
|
47
|
+
end
|
48
|
+
|
49
|
+
def test_html4_full_sanitizer
|
50
|
+
assert_equal(Rails::HTML4::FullSanitizer, Rails::HTML::Sanitizer.full_sanitizer)
|
51
|
+
assert_equal(Rails::HTML4::FullSanitizer, Rails::HTML4::Sanitizer.full_sanitizer)
|
52
|
+
end
|
53
|
+
|
54
|
+
def test_html4_link_sanitizer
|
55
|
+
assert_equal(Rails::HTML4::LinkSanitizer, Rails::HTML::Sanitizer.link_sanitizer)
|
56
|
+
assert_equal(Rails::HTML4::LinkSanitizer, Rails::HTML4::Sanitizer.link_sanitizer)
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_html4_safe_list_sanitizer
|
60
|
+
assert_equal(Rails::HTML4::SafeListSanitizer, Rails::HTML::Sanitizer.safe_list_sanitizer)
|
61
|
+
assert_equal(Rails::HTML4::SafeListSanitizer, Rails::HTML4::Sanitizer.safe_list_sanitizer)
|
62
|
+
end
|
63
|
+
|
64
|
+
def test_html4_white_list_sanitizer
|
65
|
+
assert_equal(Rails::HTML4::SafeListSanitizer, Rails::HTML::Sanitizer.white_list_sanitizer)
|
66
|
+
assert_equal(Rails::HTML4::SafeListSanitizer, Rails::HTML4::Sanitizer.white_list_sanitizer)
|
67
|
+
end
|
68
|
+
|
69
|
+
def test_html5_full_sanitizer
|
70
|
+
skip("no HTML5 support on this platform") unless Rails::HTML::Sanitizer.html5_support?
|
71
|
+
assert_equal(Rails::HTML5::FullSanitizer, Rails::HTML5::Sanitizer.full_sanitizer)
|
72
|
+
end
|
73
|
+
|
74
|
+
def test_html5_link_sanitizer
|
75
|
+
skip("no HTML5 support on this platform") unless Rails::HTML::Sanitizer.html5_support?
|
76
|
+
assert_equal(Rails::HTML5::LinkSanitizer, Rails::HTML5::Sanitizer.link_sanitizer)
|
77
|
+
end
|
78
|
+
|
79
|
+
def test_html5_safe_list_sanitizer
|
80
|
+
skip("no HTML5 support on this platform") unless Rails::HTML::Sanitizer.html5_support?
|
81
|
+
assert_equal(Rails::HTML5::SafeListSanitizer, Rails::HTML5::Sanitizer.safe_list_sanitizer)
|
82
|
+
end
|
83
|
+
|
84
|
+
def test_html5_white_list_sanitizer
|
85
|
+
skip("no HTML5 support on this platform") unless Rails::HTML::Sanitizer.html5_support?
|
86
|
+
assert_equal(Rails::HTML5::SafeListSanitizer, Rails::HTML5::Sanitizer.white_list_sanitizer)
|
87
|
+
end
|
88
|
+
end
|