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.

@@ -1,10 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Rails
2
- module Html
3
- # === Rails::Html::PermitScrubber
4
+ module HTML
5
+ # === Rails::HTML::PermitScrubber
4
6
  #
5
- # +Rails::Html::PermitScrubber+ allows you to permit only your own tags and/or attributes.
7
+ # +Rails::HTML::PermitScrubber+ allows you to permit only your own tags and/or attributes.
6
8
  #
7
- # +Rails::Html::PermitScrubber+ can be subclassed to determine:
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 < Html::PermitScrubber
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
- @direction = :bottom_up
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 node.cdata?
65
- text = node.document.create_text_node node.text
66
- node.replace text
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
- def allowed_node?(node)
81
- @tags.include?(node.name)
82
- end
86
+ def skip_node?(node)
87
+ node.text?
88
+ end
83
89
 
84
- def skip_node?(node)
85
- node.text?
86
- end
90
+ def scrub_attribute?(name)
91
+ !@attributes.include?(name)
92
+ end
87
93
 
88
- def scrub_attribute?(name)
89
- !@attributes.include?(name)
90
- end
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
- def keep_node?(node)
93
- if @tags
94
- allowed_node?(node)
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
- def scrub_node(node)
101
- node.before(node.children) # strip
102
- node.remove
103
- end
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
- def scrub_attributes(node)
106
- if @attributes
107
- node.attribute_nodes.each do |attr|
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
- def scrub_css_attribute(node)
119
- if Loofah::HTML5::Scrub.respond_to?(:scrub_css_attribute)
120
- Loofah::HTML5::Scrub.scrub_css_attribute(node)
121
- else
122
- style = node.attributes['style']
123
- style.value = Loofah::HTML5::Scrub.scrub_css(style.value) if style
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
- def validate!(var, name)
128
- if var && !var.is_a?(Enumerable)
129
- raise ArgumentError, "You should pass :#{name} as an Enumerable"
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
- def scrub_attribute(node, attr_node)
135
- attr_name = if attr_node.namespace
136
- "#{attr_node.namespace.prefix}:#{attr_node.node_name}"
137
- else
138
- attr_node.node_name
139
- end
140
-
141
- if Loofah::HTML5::SafeList::ATTR_VAL_IS_URI.include?(attr_name)
142
- # this block lifted nearly verbatim from HTML5 sanitization
143
- val_unescaped = CGI.unescapeHTML(attr_node.value).gsub(Loofah::HTML5::Scrub::CONTROL_CHARACTERS,'').downcase
144
- if val_unescaped =~ /^[a-z0-9][-+.a-z0-9]*:/ && ! Loofah::HTML5::SafeList::ALLOWED_PROTOCOLS.include?(val_unescaped.split(Loofah::HTML5::SafeList::PROTOCOL_SEPARATOR)[0])
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
- node.remove_attribute(attr_node.name) if attr_name == 'src' && attr_node.value !~ /[^[:space:]]/
155
+ node.remove_attribute(attr_node.name) if attr_name == "src" && attr_node.value !~ /[^[:space:]]/
156
156
 
157
- Loofah::HTML5::Scrub.force_correct_attribute_escaping! node
158
- end
157
+ Loofah::HTML5::Scrub.force_correct_attribute_escaping! node
158
+ end
159
159
  end
160
160
 
161
- # === Rails::Html::TargetScrubber
161
+ # === Rails::HTML::TargetScrubber
162
162
  #
163
- # Where +Rails::Html::PermitScrubber+ picks out tags and attributes to permit in
164
- # sanitization, +Rails::Html::TargetScrubber+ targets them for removal.
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::Html::TextOnlyScrubber
181
+ # === Rails::HTML::TextOnlyScrubber
182
182
  #
183
- # +Rails::Html::TextOnlyScrubber+ allows you to permit text nodes.
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
@@ -1,30 +1,14 @@
1
- require "rails/html/sanitizer/version"
2
- require "loofah"
3
- require "rails/html/scrubbers"
4
- require "rails/html/sanitizer"
1
+ # frozen_string_literal: true
5
2
 
6
- module Rails
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
- def link_sanitizer
15
- Html::LinkSanitizer
16
- end
5
+ require "loofah"
17
6
 
18
- def safe_list_sanitizer
19
- Html::SafeListSanitizer
20
- end
7
+ require_relative "rails/html/scrubbers"
8
+ require_relative "rails/html/sanitizer"
21
9
 
22
- def white_list_sanitizer
23
- safe_list_sanitizer
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