sanitize 6.1.3 → 7.0.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.
- checksums.yaml +4 -4
- data/{HISTORY.md → CHANGELOG.md} +32 -14
- data/LICENSE +3 -1
- data/README.md +120 -238
- data/lib/sanitize/config/basic.rb +15 -15
- data/lib/sanitize/config/default.rb +45 -45
- data/lib/sanitize/config/relaxed.rb +136 -32
- data/lib/sanitize/config/restricted.rb +2 -2
- data/lib/sanitize/config.rb +12 -14
- data/lib/sanitize/css.rb +308 -308
- data/lib/sanitize/transformers/clean_cdata.rb +9 -9
- data/lib/sanitize/transformers/clean_comment.rb +9 -9
- data/lib/sanitize/transformers/clean_css.rb +59 -55
- data/lib/sanitize/transformers/clean_doctype.rb +15 -15
- data/lib/sanitize/transformers/clean_element.rb +220 -237
- data/lib/sanitize/version.rb +3 -1
- data/lib/sanitize.rb +38 -38
- data/test/common.rb +4 -3
- data/test/test_clean_comment.rb +26 -25
- data/test/test_clean_css.rb +14 -13
- data/test/test_clean_doctype.rb +21 -20
- data/test/test_clean_element.rb +258 -273
- data/test/test_config.rb +22 -21
- data/test/test_malicious_css.rb +20 -19
- data/test/test_malicious_html.rb +100 -99
- data/test/test_parser.rb +26 -25
- data/test/test_sanitize.rb +70 -69
- data/test/test_sanitize_css.rb +149 -114
- data/test/test_transformers.rb +81 -83
- metadata +14 -43
data/lib/sanitize/css.rb
CHANGED
@@ -1,331 +1,333 @@
|
|
1
|
-
#
|
2
|
-
|
3
|
-
require
|
4
|
-
require
|
5
|
-
|
6
|
-
class Sanitize
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
self.
|
23
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "crass"
|
4
|
+
require "set"
|
5
|
+
|
6
|
+
class Sanitize
|
7
|
+
class CSS
|
8
|
+
attr_reader :config
|
9
|
+
|
10
|
+
# -- Class Methods ---------------------------------------------------------
|
11
|
+
|
12
|
+
# Sanitizes inline CSS style properties.
|
13
|
+
#
|
14
|
+
# This is most useful for sanitizing non-stylesheet fragments of CSS like
|
15
|
+
# you would find in the `style` attribute of an HTML element. To sanitize a
|
16
|
+
# full CSS stylesheet, use {.stylesheet}.
|
17
|
+
#
|
18
|
+
# @example
|
19
|
+
# Sanitize::CSS.properties("background: url(foo.png); color: #fff;")
|
20
|
+
#
|
21
|
+
# @return [String] Sanitized CSS properties.
|
22
|
+
def self.properties(css, config = {})
|
23
|
+
new(config).properties(css)
|
24
|
+
end
|
24
25
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
26
|
+
# Sanitizes a full CSS stylesheet.
|
27
|
+
#
|
28
|
+
# A stylesheet may include selectors, at-rules, and comments. To sanitize
|
29
|
+
# only inline style properties such as the contents of an HTML `style`
|
30
|
+
# attribute, use {.properties}.
|
31
|
+
#
|
32
|
+
# @example
|
33
|
+
# css = %[
|
34
|
+
# .foo {
|
35
|
+
# background: url(foo.png);
|
36
|
+
# color: #fff;
|
37
|
+
# }
|
38
|
+
#
|
39
|
+
# #bar {
|
40
|
+
# font: 42pt 'Comic Sans MS';
|
41
|
+
# }
|
42
|
+
# ]
|
43
|
+
#
|
44
|
+
# Sanitize::CSS.stylesheet(css, Sanitize::Config::RELAXED)
|
45
|
+
#
|
46
|
+
# @return [String] Sanitized CSS stylesheet.
|
47
|
+
def self.stylesheet(css, config = {})
|
48
|
+
new(config).stylesheet(css)
|
49
|
+
end
|
49
50
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
51
|
+
# Sanitizes the given Crass CSS parse tree and all its children, modifying
|
52
|
+
# it in place.
|
53
|
+
#
|
54
|
+
# @example
|
55
|
+
# css = %[
|
56
|
+
# .foo {
|
57
|
+
# background: url(foo.png);
|
58
|
+
# color: #fff;
|
59
|
+
# }
|
60
|
+
#
|
61
|
+
# #bar {
|
62
|
+
# font: 42pt 'Comic Sans MS';
|
63
|
+
# }
|
64
|
+
# ]
|
65
|
+
#
|
66
|
+
# tree = Crass.parse(css)
|
67
|
+
# Sanitize::CSS.tree!(tree, Sanitize::Config::RELAXED)
|
68
|
+
#
|
69
|
+
# @return [Array] Sanitized Crass CSS parse tree.
|
70
|
+
def self.tree!(tree, config = {})
|
71
|
+
new(config).tree!(tree)
|
72
|
+
end
|
72
73
|
|
73
|
-
|
74
|
+
# -- Instance Methods ------------------------------------------------------
|
74
75
|
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
76
|
+
# Returns a new Sanitize::CSS object initialized with the settings in
|
77
|
+
# _config_.
|
78
|
+
def initialize(config = {})
|
79
|
+
@config = Config.merge(Config::DEFAULT[:css], config[:css] || config)
|
79
80
|
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
81
|
+
@at_rules = Set.new(@config[:at_rules])
|
82
|
+
@at_rules_with_properties = Set.new(@config[:at_rules_with_properties])
|
83
|
+
@at_rules_with_styles = Set.new(@config[:at_rules_with_styles])
|
84
|
+
@import_url_validator = @config[:import_url_validator]
|
85
|
+
end
|
85
86
|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
87
|
+
# Sanitizes inline CSS style properties.
|
88
|
+
#
|
89
|
+
# This is most useful for sanitizing non-stylesheet fragments of CSS like
|
90
|
+
# you would find in the `style` attribute of an HTML element. To sanitize a
|
91
|
+
# full CSS stylesheet, use {#stylesheet}.
|
92
|
+
#
|
93
|
+
# @example
|
94
|
+
# scss = Sanitize::CSS.new(Sanitize::Config::RELAXED)
|
95
|
+
# scss.properties("background: url(foo.png); color: #fff;")
|
96
|
+
#
|
97
|
+
# @return [String] Sanitized CSS properties.
|
98
|
+
def properties(css)
|
99
|
+
tree = Crass.parse_properties(css,
|
100
|
+
preserve_comments: @config[:allow_comments],
|
101
|
+
preserve_hacks: @config[:allow_hacks])
|
102
|
+
|
103
|
+
tree!(tree)
|
104
|
+
Crass::Parser.stringify(tree)
|
105
|
+
end
|
105
106
|
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
107
|
+
# Sanitizes a full CSS stylesheet.
|
108
|
+
#
|
109
|
+
# A stylesheet may include selectors, at-rules, and comments. To sanitize
|
110
|
+
# only inline style properties such as the contents of an HTML `style`
|
111
|
+
# attribute, use {#properties}.
|
112
|
+
#
|
113
|
+
# @example
|
114
|
+
# css = %[
|
115
|
+
# .foo {
|
116
|
+
# background: url(foo.png);
|
117
|
+
# color: #fff;
|
118
|
+
# }
|
119
|
+
#
|
120
|
+
# #bar {
|
121
|
+
# font: 42pt 'Comic Sans MS';
|
122
|
+
# }
|
123
|
+
# ]
|
124
|
+
#
|
125
|
+
# scss = Sanitize::CSS.new(Sanitize::Config::RELAXED)
|
126
|
+
# scss.stylesheet(css)
|
127
|
+
#
|
128
|
+
# @return [String] Sanitized CSS stylesheet.
|
129
|
+
def stylesheet(css)
|
130
|
+
tree = Crass.parse(css,
|
131
|
+
preserve_comments: @config[:allow_comments],
|
132
|
+
preserve_hacks: @config[:allow_hacks])
|
133
|
+
|
134
|
+
tree!(tree)
|
135
|
+
Crass::Parser.stringify(tree)
|
136
|
+
end
|
137
|
+
|
138
|
+
# Sanitizes the given Crass CSS parse tree and all its children, modifying
|
139
|
+
# it in place.
|
140
|
+
#
|
141
|
+
# @example
|
142
|
+
# css = %[
|
143
|
+
# .foo {
|
144
|
+
# background: url(foo.png);
|
145
|
+
# color: #fff;
|
146
|
+
# }
|
147
|
+
#
|
148
|
+
# #bar {
|
149
|
+
# font: 42pt 'Comic Sans MS';
|
150
|
+
# }
|
151
|
+
# ]
|
152
|
+
#
|
153
|
+
# scss = Sanitize::CSS.new(Sanitize::Config::RELAXED)
|
154
|
+
# tree = Crass.parse(css)
|
155
|
+
#
|
156
|
+
# scss.tree!(tree)
|
157
|
+
#
|
158
|
+
# @return [Array] Sanitized Crass CSS parse tree.
|
159
|
+
def tree!(tree)
|
160
|
+
preceded_by_property = false
|
161
|
+
|
162
|
+
tree.map! do |node|
|
163
|
+
next nil if node.nil?
|
164
|
+
|
165
|
+
case node[:node]
|
166
|
+
when :at_rule
|
167
|
+
preceded_by_property = false
|
168
|
+
next at_rule!(node)
|
169
|
+
|
170
|
+
when :comment
|
171
|
+
next node if @config[:allow_comments]
|
172
|
+
|
173
|
+
when :property
|
174
|
+
prop = property!(node)
|
175
|
+
preceded_by_property = !prop.nil?
|
176
|
+
next prop
|
177
|
+
|
178
|
+
when :semicolon
|
179
|
+
# Only preserve the semicolon if it was preceded by an allowlisted
|
180
|
+
# property. Otherwise, omit it in order to prevent redundant
|
181
|
+
# semicolons.
|
182
|
+
if preceded_by_property
|
183
|
+
preceded_by_property = false
|
184
|
+
next node
|
185
|
+
end
|
136
186
|
|
137
|
-
|
138
|
-
# in place.
|
139
|
-
#
|
140
|
-
# @example
|
141
|
-
# css = %[
|
142
|
-
# .foo {
|
143
|
-
# background: url(foo.png);
|
144
|
-
# color: #fff;
|
145
|
-
# }
|
146
|
-
#
|
147
|
-
# #bar {
|
148
|
-
# font: 42pt 'Comic Sans MS';
|
149
|
-
# }
|
150
|
-
# ]
|
151
|
-
#
|
152
|
-
# scss = Sanitize::CSS.new(Sanitize::Config::RELAXED)
|
153
|
-
# tree = Crass.parse(css)
|
154
|
-
#
|
155
|
-
# scss.tree!(tree)
|
156
|
-
#
|
157
|
-
# @return [Array] Sanitized Crass CSS parse tree.
|
158
|
-
def tree!(tree)
|
159
|
-
preceded_by_property = false
|
160
|
-
|
161
|
-
tree.map! do |node|
|
162
|
-
next nil if node.nil?
|
163
|
-
|
164
|
-
case node[:node]
|
165
|
-
when :at_rule
|
166
|
-
preceded_by_property = false
|
167
|
-
next at_rule!(node)
|
168
|
-
|
169
|
-
when :comment
|
170
|
-
next node if @config[:allow_comments]
|
171
|
-
|
172
|
-
when :property
|
173
|
-
prop = property!(node)
|
174
|
-
preceded_by_property = !prop.nil?
|
175
|
-
next prop
|
176
|
-
|
177
|
-
when :semicolon
|
178
|
-
# Only preserve the semicolon if it was preceded by an allowlisted
|
179
|
-
# property. Otherwise, omit it in order to prevent redundant semicolons.
|
180
|
-
if preceded_by_property
|
187
|
+
when :style_rule
|
181
188
|
preceded_by_property = false
|
189
|
+
tree!(node[:children])
|
182
190
|
next node
|
183
|
-
end
|
184
191
|
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
next node
|
192
|
+
when :whitespace
|
193
|
+
next node
|
194
|
+
end
|
189
195
|
|
190
|
-
|
191
|
-
next node
|
196
|
+
nil
|
192
197
|
end
|
193
198
|
|
194
|
-
|
199
|
+
tree
|
195
200
|
end
|
196
201
|
|
197
|
-
|
198
|
-
|
202
|
+
# -- Protected Instance Methods --------------------------------------------
|
203
|
+
protected
|
199
204
|
|
200
|
-
|
201
|
-
|
205
|
+
# Sanitizes a CSS at-rule node. Returns the sanitized node, or `nil` if the
|
206
|
+
# current config doesn't allow this at-rule.
|
207
|
+
def at_rule!(rule)
|
208
|
+
name = rule[:name].downcase
|
202
209
|
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
210
|
+
if @at_rules_with_styles.include?(name)
|
211
|
+
styles = Crass::Parser.parse_rules(rule[:block],
|
212
|
+
preserve_comments: @config[:allow_comments],
|
213
|
+
preserve_hacks: @config[:allow_hacks])
|
207
214
|
|
208
|
-
|
209
|
-
styles = Crass::Parser.parse_rules(rule[:block],
|
210
|
-
:preserve_comments => @config[:allow_comments],
|
211
|
-
:preserve_hacks => @config[:allow_hacks])
|
215
|
+
rule[:block] = tree!(styles)
|
212
216
|
|
213
|
-
|
217
|
+
elsif @at_rules_with_properties.include?(name)
|
218
|
+
props = Crass::Parser.parse_properties(rule[:block],
|
219
|
+
preserve_comments: @config[:allow_comments],
|
220
|
+
preserve_hacks: @config[:allow_hacks])
|
214
221
|
|
215
|
-
|
216
|
-
props = Crass::Parser.parse_properties(rule[:block],
|
217
|
-
:preserve_comments => @config[:allow_comments],
|
218
|
-
:preserve_hacks => @config[:allow_hacks])
|
222
|
+
rule[:block] = tree!(props)
|
219
223
|
|
220
|
-
|
224
|
+
elsif @at_rules.include?(name)
|
225
|
+
return nil if name == "import" && !import_url_allowed?(rule)
|
226
|
+
return nil if rule.has_key?(:block)
|
227
|
+
else
|
228
|
+
return nil
|
229
|
+
end
|
221
230
|
|
222
|
-
|
223
|
-
return nil if name == "import" && !import_url_allowed?(rule)
|
224
|
-
return nil if rule.has_key?(:block)
|
225
|
-
else
|
226
|
-
return nil
|
231
|
+
rule
|
227
232
|
end
|
228
233
|
|
229
|
-
|
230
|
-
|
234
|
+
# Returns `true` if the given CSS function name is an image-related function
|
235
|
+
# that may contain image URLs that need to be validated.
|
236
|
+
def image_function?(name)
|
237
|
+
["image", "image-set", "-webkit-image-set"].include?(name)
|
238
|
+
end
|
231
239
|
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
end
|
240
|
+
# Passes the URL value of an @import rule to a block to ensure
|
241
|
+
# it's an allowed URL
|
242
|
+
def import_url_allowed?(rule)
|
243
|
+
return true unless @import_url_validator
|
237
244
|
|
238
|
-
|
239
|
-
# it's an allowed URL
|
240
|
-
def import_url_allowed?(rule)
|
241
|
-
return true unless @import_url_validator
|
245
|
+
url_token = rule[:tokens].detect { |t| t[:node] == :url || t[:node] == :string }
|
242
246
|
|
243
|
-
|
247
|
+
# don't allow @imports with no URL value
|
248
|
+
return false unless url_token && (import_url = url_token[:value])
|
244
249
|
|
245
|
-
|
246
|
-
|
250
|
+
@import_url_validator.call(import_url)
|
251
|
+
end
|
247
252
|
|
248
|
-
|
249
|
-
|
253
|
+
# Sanitizes a CSS property node. Returns the sanitized node, or `nil` if the
|
254
|
+
# current config doesn't allow this property.
|
255
|
+
def property!(prop)
|
256
|
+
name = prop[:name].downcase
|
250
257
|
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
258
|
+
# Preserve IE * and _ hacks if desired.
|
259
|
+
if @config[:allow_hacks]
|
260
|
+
name.slice!(0) if /\A[*_]/.match?(name)
|
261
|
+
end
|
255
262
|
|
256
|
-
|
257
|
-
if @config[:allow_hacks]
|
258
|
-
name.slice!(0) if name =~ /\A[*_]/
|
259
|
-
end
|
263
|
+
return nil unless @config[:properties].include?(name)
|
260
264
|
|
261
|
-
|
265
|
+
nodes = prop[:children].dup
|
266
|
+
combined_value = +""
|
262
267
|
|
263
|
-
|
264
|
-
|
268
|
+
nodes.each do |child|
|
269
|
+
value = child[:value]
|
265
270
|
|
266
|
-
|
267
|
-
|
271
|
+
case child[:node]
|
272
|
+
when :ident
|
273
|
+
combined_value << value.downcase if String === value
|
268
274
|
|
269
|
-
|
270
|
-
|
271
|
-
|
275
|
+
when :function
|
276
|
+
if child.key?(:name)
|
277
|
+
name = child[:name].downcase
|
272
278
|
|
273
|
-
|
274
|
-
|
275
|
-
|
279
|
+
if name == "url"
|
280
|
+
return nil unless valid_url?(child)
|
281
|
+
end
|
276
282
|
|
277
|
-
|
278
|
-
|
283
|
+
if image_function?(name)
|
284
|
+
return nil unless valid_image?(child)
|
285
|
+
end
|
286
|
+
|
287
|
+
combined_value << name
|
288
|
+
return nil if name == "expression" || combined_value == "expression"
|
279
289
|
end
|
280
290
|
|
281
|
-
if
|
282
|
-
|
291
|
+
if Array === value
|
292
|
+
nodes.concat(value)
|
293
|
+
elsif String === value
|
294
|
+
lowercase_value = value.downcase
|
295
|
+
combined_value << lowercase_value
|
296
|
+
return nil if lowercase_value == "expression" || combined_value == "expression"
|
283
297
|
end
|
284
298
|
|
285
|
-
|
286
|
-
return nil
|
287
|
-
end
|
299
|
+
when :url
|
300
|
+
return nil unless valid_url?(child)
|
288
301
|
|
289
|
-
|
290
|
-
|
291
|
-
elsif String === value
|
292
|
-
lowercase_value = value.downcase
|
293
|
-
combined_value << lowercase_value
|
294
|
-
return nil if lowercase_value == 'expression' || combined_value == 'expression'
|
302
|
+
when :bad_url
|
303
|
+
return nil
|
295
304
|
end
|
296
|
-
|
297
|
-
when :url
|
298
|
-
return nil unless valid_url?(child)
|
299
|
-
|
300
|
-
when :bad_url
|
301
|
-
return nil
|
302
305
|
end
|
303
|
-
end
|
304
306
|
|
305
|
-
|
306
|
-
|
307
|
+
prop
|
308
|
+
end
|
307
309
|
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
310
|
+
# Returns `true` if the given node (which may be of type `:url` or
|
311
|
+
# `:function`, since the CSS syntax can produce both) uses an allowlisted
|
312
|
+
# protocol.
|
313
|
+
def valid_url?(node)
|
314
|
+
type = node[:node]
|
313
315
|
|
314
|
-
|
315
|
-
|
316
|
-
|
316
|
+
if type == :function
|
317
|
+
return false unless node.key?(:name) && node[:name].downcase == "url"
|
318
|
+
return false unless Array === node[:value]
|
317
319
|
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
320
|
+
# A URL function's `:value` should be an array containing no more than
|
321
|
+
# one `:string` node and any number of `:whitespace` nodes.
|
322
|
+
#
|
323
|
+
# If it contains more than one `:string` node, or if it contains any
|
324
|
+
# other nodes except `:whitespace` nodes, it's not valid.
|
325
|
+
url_string_node = nil
|
324
326
|
|
325
|
-
|
326
|
-
|
327
|
+
node[:value].each do |token|
|
328
|
+
return false unless Hash === token
|
327
329
|
|
328
|
-
|
330
|
+
case token[:node]
|
329
331
|
when :string
|
330
332
|
return false unless url_string_node.nil?
|
331
333
|
url_string_node = token
|
@@ -335,47 +337,45 @@ class Sanitize; class CSS
|
|
335
337
|
|
336
338
|
else
|
337
339
|
return false
|
340
|
+
end
|
338
341
|
end
|
339
|
-
end
|
340
342
|
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
343
|
+
return false if url_string_node.nil?
|
344
|
+
url = url_string_node[:value]
|
345
|
+
elsif type == :url
|
346
|
+
url = node[:value]
|
347
|
+
else
|
348
|
+
return false
|
349
|
+
end
|
348
350
|
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
351
|
+
if url =~ Sanitize::REGEX_PROTOCOL
|
352
|
+
@config[:protocols].include?($1.downcase)
|
353
|
+
else
|
354
|
+
@config[:protocols].include?(:relative)
|
355
|
+
end
|
353
356
|
end
|
354
357
|
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
return false unless node[:node] == :function
|
362
|
-
return false unless node.key?(:name) && image_function?(node[:name].downcase)
|
363
|
-
return false unless Array === node[:value]
|
358
|
+
# Returns `true` if the given node is an image-related function and contains
|
359
|
+
# only strings that use an allowlisted protocol.
|
360
|
+
def valid_image?(node)
|
361
|
+
return false unless node[:node] == :function
|
362
|
+
return false unless node.key?(:name) && image_function?(node[:name].downcase)
|
363
|
+
return false unless Array === node[:value]
|
364
364
|
|
365
|
-
|
365
|
+
node[:value].each do |token|
|
366
366
|
return false unless Hash === token
|
367
367
|
|
368
368
|
case token[:node]
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
else
|
373
|
-
return false unless @config[:protocols].include?(:relative)
|
374
|
-
end
|
369
|
+
when :string
|
370
|
+
if token[:value] =~ Sanitize::REGEX_PROTOCOL
|
371
|
+
return false unless @config[:protocols].include?($1.downcase)
|
375
372
|
else
|
376
|
-
|
373
|
+
return false unless @config[:protocols].include?(:relative)
|
374
|
+
end
|
375
|
+
else
|
376
|
+
next
|
377
377
|
end
|
378
378
|
end
|
379
|
+
end
|
379
380
|
end
|
380
|
-
|
381
|
-
end; end
|
381
|
+
end
|