loofah 2.25.2 → 2.25.3
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/CHANGELOG.md +27 -0
- data/lib/loofah/html5/safelist.rb +2 -1
- data/lib/loofah/html5/scrub.rb +97 -38
- data/lib/loofah/scrubbers.rb +22 -21
- data/lib/loofah/version.rb +1 -1
- data/lib/loofah.rb +1 -7
- metadata +5 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ab3a16b00807f63098be8c67dbd785edea496daf292dc063299d84d25ae56133
|
|
4
|
+
data.tar.gz: d9ba643579b0190c1f66aa4cdfeb79ec5ade635032172c805be58bd0d67fc5fc
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b9b7dc5b9f7e3c0ac798a7e70f6257bb65827baadae3861d17cf0a24e1bf5cab48c909cd463ecc51ae5ba59acd91ca73c004faa7d1c489bbd0e91b7245132c79
|
|
7
|
+
data.tar.gz: 7e17e0f6e9cec7edd72c0f254aa406e510531297af9acc10714cd7277ec1641d48ab369a0bfeeef585104c314a79cdfb795f112e84591a5cf81f4cb8eac46486
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,32 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 2.25.3 / 2026-11-01
|
|
4
|
+
|
|
5
|
+
### Security
|
|
6
|
+
|
|
7
|
+
* Ensure `Loofah::HTML5::Scrub.allowed_uri?` rejects schemes split by a numeric character reference without a semicolon that ends at a character `allowed_uri?` strips (e.g. `javascript: alert(1)`) or at another character reference (e.g. `javascript:alert(1)`). Character references are now decoded before anything is stripped, as browsers do. See [GHSA-p9wj-w8xq-vjx6](https://github.com/flavorjones/loofah/security/advisories/GHSA-p9wj-w8xq-vjx6). @flavorjones
|
|
8
|
+
* Ensure `Loofah::HTML5::Scrub.allowed_uri?` recognizes the named character reference `/`, which `CGI.unescapeHTML` does not decode and browsers read as `/`, and rejects `data:` URIs that disguise a disallowed mediatype with it (e.g. `data:text/html,<script>alert(1)</script>`). Previously that mediatype was treated as malformed and fell back to the allowed `text/plain`. Only v2.25.2 is affected. See [GHSA-jcff-cj73-4w4f](https://github.com/flavorjones/loofah/security/advisories/GHSA-jcff-cj73-4w4f). @flavorjones
|
|
9
|
+
* Ensure `Loofah::HTML5::Scrub.scrub_css` does not decode CSS escapes in identifiers. Previously, escaped parentheses (e.g. `url\28 http://example.com/\29`) came out as a live `url()`, which could make the viewer's browser fetch an attacker-chosen URL. See [GHSA-m3x9-938q-5767](https://github.com/flavorjones/loofah/security/advisories/GHSA-m3x9-938q-5767). @flavorjones
|
|
10
|
+
* Ensure that `href` and `xlink:href` on SVG `textPath` and `mpath` elements are restricted to local (same-document) references. Previously `SVG_ALLOW_LOCAL_HREF` spelled `textPath` in lowercase, which never matched the name the HTML5 parser assigns inside `<svg>`, and omitted `mpath` entirely. See [GHSA-345x-3rq2-jfrm](https://github.com/flavorjones/loofah/security/advisories/GHSA-345x-3rq2-jfrm). @flavorjones
|
|
11
|
+
* Fix quadratic-time scrubbing of the `href` and `xlink:href` attributes of SVG elements. Only Ruby 3.1 and earlier are affected, because Ruby 3.2 introduced linear-time regexp matching. See [GHSA-mf2j-8f84-cc9q](https://github.com/flavorjones/loofah/security/advisories/GHSA-mf2j-8f84-cc9q). @flavorjones
|
|
12
|
+
* Ensure HTML5 scrubber output does not turn into new markup when a browser parses it again. The fix is in Nokogiri v1.19.5, which escapes `<` and `>` in attribute values as the HTML standard requires, so Loofah now requires Nokogiri >= 1.19.5. See [GHSA-h6v6-rg66-fqj4](https://github.com/flavorjones/loofah/security/advisories/GHSA-h6v6-rg66-fqj4). @flavorjones @hackvertor
|
|
13
|
+
|
|
14
|
+
### Dependencies
|
|
15
|
+
|
|
16
|
+
* Loofah now requires Ruby >= 3.2, the minimum that Nokogiri v1.19.x supports. We don't like dropping Ruby versions in a patch release, and we're sorry for the inconvenience, but it's the only way to make sure everyone gets the security fix in [GHSA-h6v6-rg66-fqj4](https://github.com/flavorjones/loofah/security/advisories/GHSA-h6v6-rg66-fqj4).
|
|
17
|
+
|
|
18
|
+
### Added
|
|
19
|
+
|
|
20
|
+
* `Loofah::HTML5::Scrub.prefixed_attribute_name` and `Loofah::HTML5::Scrub.scrub_attribute_value` expose the html5lib attribute name and value rules, so downstream consumers can apply them instead of carrying their own copy. @flavorjones
|
|
21
|
+
|
|
22
|
+
### Fixed
|
|
23
|
+
|
|
24
|
+
* Fix `:double_breakpoint`, which left document fragments unchanged despite its documented example. The scrubber now changes only paragraphs inside the scrubbed node, and its run time on a document grows linearly, not quadratically, with the paragraph count. #309 @OskarEichler
|
|
25
|
+
* Fix `:whitewash`, which passed CDATA sections through unchanged. Markup inside a CDATA section in `Loofah.xml_fragment` or `Loofah.xml_document` survived scrubbing and became live elements if the output was later parsed as HTML. The scrubber now escapes CDATA content, as `:strip`, `:prune` and `:escape` already do. On JRuby, every sanitizer now emits escaped CDATA content as a CDATA section, as on CRuby, instead of a text node that double-escaped it. #313 @flavorjones (reported by @MegaManSec)
|
|
26
|
+
* Fix `:double_breakpoint`, which raised `NoMethodError` on a paragraph that ends with a `<br>` after a double break. #314 @flavorjones (reported by @MegaManSec)
|
|
27
|
+
* Fix `Loofah::HTML5::Scrub.allowed_uri?`, which raised `Encoding::CompatibilityError` on a string in an encoding other than UTF-8 and `ArgumentError` on invalid UTF-8. It now transcodes the input to UTF-8 and returns `false` when the input has an invalid byte sequence or no UTF-8 representation. #315 @flavorjones
|
|
28
|
+
|
|
29
|
+
|
|
3
30
|
## 2.25.2 / 2026-07-15
|
|
4
31
|
|
|
5
32
|
### Security
|
data/lib/loofah/html5/scrub.rb
CHANGED
|
@@ -13,6 +13,7 @@ module Loofah
|
|
|
13
13
|
CSS_IMPORTANT = "!important"
|
|
14
14
|
CSS_WHITESPACE = " "
|
|
15
15
|
CSS_PROPERTY_STRING_WITHOUT_EMBEDDED_QUOTES = /\A(["'])?[^"']+\1\z/
|
|
16
|
+
CSS_URLISH_NODES = [:url, :bad_url, :bad_string]
|
|
16
17
|
DATA_ATTRIBUTE_NAME = /\Adata-[\w-]+\z/
|
|
17
18
|
|
|
18
19
|
# Decimal (`:`) or hexadecimal (`:`) form, with or without the trailing semicolon that
|
|
@@ -52,6 +53,15 @@ module Loofah
|
|
|
52
53
|
# URIs. CGI.unescapeHTML does not decode these, so they are handled explicitly.
|
|
53
54
|
WHITESPACE_CHARACTER_REFERENCES = /&(Tab|NewLine);/
|
|
54
55
|
|
|
56
|
+
# HTML5 named character references for the delimiters that decide how a URI parses:
|
|
57
|
+
# ":" ends the scheme, and "/" separates the type and subtype of a data: mediatype.
|
|
58
|
+
# CGI.unescapeHTML does not decode these either. Together with
|
|
59
|
+
# WHITESPACE_CHARACTER_REFERENCES these are every named character reference that
|
|
60
|
+
# decodes to U+0009, U+000A, U+002F, or U+003A, so no other named reference can
|
|
61
|
+
# change how a URI parses.
|
|
62
|
+
DELIMITER_CHARACTER_REFERENCES = { ":" => ":", "/" => "/" }.freeze
|
|
63
|
+
DELIMITER_CHARACTER_REFERENCE = Regexp.union(DELIMITER_CHARACTER_REFERENCES.keys)
|
|
64
|
+
|
|
55
65
|
class << self
|
|
56
66
|
def allowed_element?(element_name)
|
|
57
67
|
::Loofah::HTML5::SafeList::ALLOWED_ELEMENTS_WITH_LIBXML2.include?(element_name)
|
|
@@ -60,11 +70,7 @@ module Loofah
|
|
|
60
70
|
# alternative implementation of the html5lib attribute scrubbing algorithm
|
|
61
71
|
def scrub_attributes(node)
|
|
62
72
|
node.attribute_nodes.each do |attr_node|
|
|
63
|
-
attr_name =
|
|
64
|
-
"#{attr_node.namespace.prefix}:#{attr_node.node_name}"
|
|
65
|
-
else
|
|
66
|
-
attr_node.node_name
|
|
67
|
-
end
|
|
73
|
+
attr_name = prefixed_attribute_name(attr_node)
|
|
68
74
|
|
|
69
75
|
if DATA_ATTRIBUTE_NAME.match?(attr_name)
|
|
70
76
|
next
|
|
@@ -75,20 +81,7 @@ module Loofah
|
|
|
75
81
|
next
|
|
76
82
|
end
|
|
77
83
|
|
|
78
|
-
|
|
79
|
-
next if scrub_uri_attribute(attr_node)
|
|
80
|
-
end
|
|
81
|
-
|
|
82
|
-
if SafeList::SVG_ATTR_VAL_ALLOWS_REF.include?(attr_name)
|
|
83
|
-
scrub_attribute_that_allows_local_ref(attr_node)
|
|
84
|
-
end
|
|
85
|
-
|
|
86
|
-
next unless SafeList::SVG_ALLOW_LOCAL_HREF.include?(node.name) &&
|
|
87
|
-
SafeList::SVG_HREF_ATTRIBUTES.include?(attr_name) &&
|
|
88
|
-
attr_node.value =~ /^\s*[^#\s].*/m
|
|
89
|
-
|
|
90
|
-
attr_node.remove
|
|
91
|
-
next
|
|
84
|
+
scrub_attribute_value(node, attr_node, attr_name)
|
|
92
85
|
end
|
|
93
86
|
|
|
94
87
|
scrub_css_attribute(node)
|
|
@@ -102,28 +95,58 @@ module Loofah
|
|
|
102
95
|
force_correct_attribute_escaping!(node)
|
|
103
96
|
end
|
|
104
97
|
|
|
98
|
+
# The name an attribute is matched by in the safelists, qualified by its namespace prefix.
|
|
99
|
+
def prefixed_attribute_name(attr_node)
|
|
100
|
+
if attr_node.namespace
|
|
101
|
+
"#{attr_node.namespace.prefix}:#{attr_node.node_name}"
|
|
102
|
+
else
|
|
103
|
+
attr_node.node_name
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# Apply the html5lib value rules to an attribute that is already permitted by name.
|
|
108
|
+
# Returns true when `scrub_uri_attribute` removed the attribute, in which case the caller
|
|
109
|
+
# should stop scrubbing it.
|
|
110
|
+
def scrub_attribute_value(node, attr_node, attr_name = prefixed_attribute_name(attr_node))
|
|
111
|
+
if SafeList::ATTR_VAL_IS_URI.include?(attr_name)
|
|
112
|
+
return true if scrub_uri_attribute(attr_node)
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
if SafeList::SVG_ATTR_VAL_ALLOWS_REF.include?(attr_name)
|
|
116
|
+
scrub_attribute_that_allows_local_ref(attr_node)
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
# The whitespace run is `[^\S\n]*` rather than `\s*` so that it cannot cross a line
|
|
120
|
+
# boundary. With `\s*` the engine rescanned the entire run from every line start,
|
|
121
|
+
# which is quadratic in the length of an all-whitespace value.
|
|
122
|
+
if SafeList::SVG_ALLOW_LOCAL_HREF.include?(node.name) &&
|
|
123
|
+
SafeList::SVG_HREF_ATTRIBUTES.include?(attr_name) &&
|
|
124
|
+
attr_node.value =~ /^[^\S\n]*[^#\s]/
|
|
125
|
+
attr_node.remove
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
false
|
|
129
|
+
end
|
|
130
|
+
|
|
105
131
|
def scrub_css_attribute(node)
|
|
106
132
|
style = node.attributes["style"]
|
|
107
133
|
style.value = scrub_css(style.value) if style
|
|
108
134
|
end
|
|
109
135
|
|
|
110
136
|
def scrub_css(style)
|
|
111
|
-
url_flags = [:url, :bad_url]
|
|
112
137
|
style_tree = Crass.parse_properties(style)
|
|
113
138
|
sanitized_tree = []
|
|
114
139
|
|
|
115
140
|
style_tree.each do |node|
|
|
116
141
|
next unless node[:node] == :property
|
|
117
|
-
next if node[:children].any?
|
|
118
|
-
url_flags.include?(child[:node])
|
|
119
|
-
end
|
|
142
|
+
next if node[:children].any? { |child| CSS_URLISH_NODES.include?(child[:node]) }
|
|
120
143
|
|
|
121
144
|
name = node[:name].downcase
|
|
122
145
|
next unless SafeList::ALLOWED_CSS_PROPERTIES.include?(name) ||
|
|
123
146
|
SafeList::ALLOWED_SVG_PROPERTIES.include?(name) ||
|
|
124
147
|
SafeList::SHORTHAND_CSS_PROPERTIES.include?(name.split("-").first)
|
|
125
148
|
|
|
126
|
-
value = node[:children].
|
|
149
|
+
value = node[:children].filter_map do |child|
|
|
127
150
|
case child[:node]
|
|
128
151
|
when :whitespace
|
|
129
152
|
CSS_WHITESPACE
|
|
@@ -140,30 +163,55 @@ module Loofah
|
|
|
140
163
|
if !SafeList::SHORTHAND_CSS_PROPERTIES.include?(name.split("-").first) ||
|
|
141
164
|
SafeList::ALLOWED_CSS_KEYWORDS.include?(keyword) ||
|
|
142
165
|
(keyword =~ CSS_KEYWORDISH)
|
|
143
|
-
|
|
166
|
+
child[:raw]
|
|
144
167
|
end
|
|
145
168
|
else
|
|
146
169
|
child[:raw]
|
|
147
170
|
end
|
|
148
|
-
end.
|
|
171
|
+
end.join.strip
|
|
149
172
|
|
|
150
173
|
next if value.empty?
|
|
151
174
|
|
|
152
175
|
value << CSS_WHITESPACE << CSS_IMPORTANT if node[:important]
|
|
176
|
+
# Dropping tokens can join the survivors into new ones (`url/(a)*/(b)` becomes `url/*/`),
|
|
177
|
+
# so the rebuilt string may tokenize differently from the original and stringify
|
|
178
|
+
# differently again. Keep a property only if its rebuilt string survives a parse and
|
|
179
|
+
# stringify unchanged, so the tokens checked are exactly the tokens a browser will see.
|
|
153
180
|
propstring = format("%s:%s", name, value)
|
|
154
181
|
sanitized_node = Crass.parse_properties(propstring).first
|
|
182
|
+
next unless Crass::Parser.stringify(sanitized_node) == propstring
|
|
183
|
+
next if css_property_unsafe?(sanitized_node)
|
|
184
|
+
|
|
155
185
|
sanitized_tree << sanitized_node << CRASS_SEMICOLON
|
|
156
186
|
end
|
|
157
187
|
|
|
158
188
|
Crass::Parser.stringify(sanitized_tree)
|
|
159
189
|
end
|
|
160
190
|
|
|
191
|
+
# Dropping a token joins its neighbours, which can build a token that was not in the input:
|
|
192
|
+
# dropping the string from `url\28 "x"linear-gradient(red)` leaves one function token. So the
|
|
193
|
+
# rebuilt property is checked for functions and strings, which the input check never sees.
|
|
194
|
+
def css_property_unsafe?(node)
|
|
195
|
+
node[:children].any? do |child|
|
|
196
|
+
case child[:node]
|
|
197
|
+
when *CSS_URLISH_NODES
|
|
198
|
+
true
|
|
199
|
+
when :string
|
|
200
|
+
!CSS_PROPERTY_STRING_WITHOUT_EMBEDDED_QUOTES.match?(child[:raw])
|
|
201
|
+
when :function
|
|
202
|
+
!SafeList::ALLOWED_CSS_FUNCTIONS.include?(child[:name].downcase(:ascii))
|
|
203
|
+
else
|
|
204
|
+
false
|
|
205
|
+
end
|
|
206
|
+
end
|
|
207
|
+
end
|
|
208
|
+
|
|
161
209
|
def scrub_attribute_that_allows_local_ref(attr_node)
|
|
162
210
|
return unless attr_node.value
|
|
163
211
|
|
|
164
212
|
nodes = Crass::Parser.new(attr_node.value).parse_component_values
|
|
165
213
|
|
|
166
|
-
values = nodes.
|
|
214
|
+
values = nodes.filter_map do |node|
|
|
167
215
|
case node[:node]
|
|
168
216
|
when :url
|
|
169
217
|
if node[:value].start_with?("#")
|
|
@@ -172,7 +220,7 @@ module Loofah
|
|
|
172
220
|
when :hash, :ident, :string
|
|
173
221
|
node[:raw]
|
|
174
222
|
end
|
|
175
|
-
end
|
|
223
|
+
end
|
|
176
224
|
|
|
177
225
|
attr_node.value = values.join(" ")
|
|
178
226
|
end
|
|
@@ -180,16 +228,27 @@ module Loofah
|
|
|
180
228
|
# Returns true if the given URI string is safe, false otherwise. This method can be used to
|
|
181
229
|
# validate URI attribute values without requiring a Nokogiri DOM node.
|
|
182
230
|
def allowed_uri?(uri_string)
|
|
231
|
+
# The character-reference regexps are UTF-8, so the input has to be transcoded before it can
|
|
232
|
+
# be matched against them. String#encode is a no-op on a string already tagged UTF-8, so an
|
|
233
|
+
# invalid byte sequence has to be rejected separately.
|
|
234
|
+
return false unless uri_string.valid_encoding?
|
|
235
|
+
|
|
236
|
+
begin
|
|
237
|
+
uri_string = uri_string.encode(Encoding::UTF_8)
|
|
238
|
+
rescue EncodingError
|
|
239
|
+
return false
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
# Decode character references before stripping anything, as a browser does: an HTML parser
|
|
243
|
+
# decodes the attribute value, then the URL parser strips characters from the result. A
|
|
244
|
+
# semicolon-less numeric reference ends at the first character that cannot extend it, so
|
|
245
|
+
# stripping that character first would let the reference run on into the following text.
|
|
183
246
|
# CGI.unescapeHTML decodes numeric references only when they carry a trailing semicolon, so
|
|
184
|
-
#
|
|
185
|
-
|
|
186
|
-
# stripped both before and after decoding, since decoding can produce them. That strip must
|
|
187
|
-
# precede WHITESPACE_CHARACTER_REFERENCES: removing a control character can reveal a named
|
|
188
|
-
# whitespace reference.
|
|
189
|
-
uri_string = decode_numeric_character_references(CGI.unescapeHTML(uri_string.gsub(CONTROL_CHARACTERS, "")))
|
|
247
|
+
# the semicolon-less ones are decoded separately.
|
|
248
|
+
uri_string = CGI.unescapeHTML(decode_numeric_character_references(uri_string))
|
|
190
249
|
uri_string.gsub!(CONTROL_CHARACTERS, "")
|
|
191
250
|
uri_string.gsub!(WHITESPACE_CHARACTER_REFERENCES, "")
|
|
192
|
-
uri_string.gsub!(
|
|
251
|
+
uri_string.gsub!(DELIMITER_CHARACTER_REFERENCE, DELIMITER_CHARACTER_REFERENCES)
|
|
193
252
|
uri_string.downcase!
|
|
194
253
|
if URI_PROTOCOL_REGEX.match?(uri_string)
|
|
195
254
|
protocol = uri_string.split(SafeList::PROTOCOL_SEPARATOR)[0]
|
|
@@ -265,10 +324,10 @@ module Loofah
|
|
|
265
324
|
|
|
266
325
|
def cdata_escape(node)
|
|
267
326
|
escaped_text = escape_tags(node.text)
|
|
268
|
-
if
|
|
269
|
-
node.document.create_text_node(escaped_text)
|
|
270
|
-
else
|
|
327
|
+
if node.cdata?
|
|
271
328
|
node.document.create_cdata(escaped_text)
|
|
329
|
+
else
|
|
330
|
+
node.document.create_text_node(escaped_text)
|
|
272
331
|
end
|
|
273
332
|
end
|
|
274
333
|
|
data/lib/loofah/scrubbers.rb
CHANGED
|
@@ -201,7 +201,11 @@ module Loofah
|
|
|
201
201
|
return CONTINUE if node.namespaces.empty?
|
|
202
202
|
end
|
|
203
203
|
when Nokogiri::XML::Node::TEXT_NODE, Nokogiri::XML::Node::CDATA_SECTION_NODE
|
|
204
|
-
|
|
204
|
+
if HTML5::Scrub.cdata_needs_escaping?(node)
|
|
205
|
+
node.before(HTML5::Scrub.cdata_escape(node))
|
|
206
|
+
else
|
|
207
|
+
return CONTINUE
|
|
208
|
+
end
|
|
205
209
|
end
|
|
206
210
|
node.remove
|
|
207
211
|
STOP
|
|
@@ -361,37 +365,34 @@ module Loofah
|
|
|
361
365
|
#
|
|
362
366
|
class DoubleBreakpoint < Scrubber
|
|
363
367
|
def initialize # rubocop:disable Lint/MissingSuper
|
|
364
|
-
|
|
368
|
+
# a top-down walk would empty a paragraph before reaching a paragraph nested in it
|
|
369
|
+
@direction = :bottom_up
|
|
365
370
|
end
|
|
366
371
|
|
|
367
372
|
def scrub(node)
|
|
368
373
|
return CONTINUE unless (node.type == Nokogiri::XML::Node::ELEMENT_NODE) && (node.name == "p")
|
|
374
|
+
return CONTINUE unless node.at_xpath("./br[following-sibling::br]")
|
|
369
375
|
|
|
370
|
-
|
|
376
|
+
new_paragraph = node.add_previous_sibling("<p>").first
|
|
371
377
|
|
|
372
|
-
|
|
373
|
-
|
|
378
|
+
node.children.each do |child|
|
|
379
|
+
remove_blank_text_nodes(child)
|
|
380
|
+
end
|
|
374
381
|
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
382
|
+
node.children.each do |child|
|
|
383
|
+
# already unlinked
|
|
384
|
+
next if child.parent.nil?
|
|
378
385
|
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
child.next_sibling.unlink
|
|
386
|
-
child.unlink
|
|
387
|
-
else
|
|
388
|
-
child.parent = new_paragraph
|
|
389
|
-
end
|
|
386
|
+
if child.name == "br" && child.next_sibling&.name == "br"
|
|
387
|
+
new_paragraph = node.add_previous_sibling("<p>").first
|
|
388
|
+
child.next_sibling.unlink
|
|
389
|
+
child.unlink
|
|
390
|
+
else
|
|
391
|
+
child.parent = new_paragraph
|
|
390
392
|
end
|
|
391
|
-
|
|
392
|
-
paragraph_node.unlink
|
|
393
393
|
end
|
|
394
394
|
|
|
395
|
+
node.unlink
|
|
395
396
|
CONTINUE
|
|
396
397
|
end
|
|
397
398
|
|
data/lib/loofah/version.rb
CHANGED
data/lib/loofah.rb
CHANGED
|
@@ -5,13 +5,7 @@ require "nokogiri"
|
|
|
5
5
|
module Loofah
|
|
6
6
|
class << self
|
|
7
7
|
def html5_support?
|
|
8
|
-
|
|
9
|
-
# subclassing fix from https://github.com/sparklemotion/nokogiri/pull/2534
|
|
10
|
-
return @html5_support if defined? @html5_support
|
|
11
|
-
|
|
12
|
-
@html5_support =
|
|
13
|
-
Gem::Version.new(Nokogiri::VERSION) > Gem::Version.new("1.14.0") &&
|
|
14
|
-
Nokogiri.uses_gumbo?
|
|
8
|
+
Nokogiri.uses_gumbo?
|
|
15
9
|
end
|
|
16
10
|
end
|
|
17
11
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: loofah
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.25.
|
|
4
|
+
version: 2.25.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Mike Dalessio
|
|
@@ -30,14 +30,14 @@ dependencies:
|
|
|
30
30
|
requirements:
|
|
31
31
|
- - ">="
|
|
32
32
|
- !ruby/object:Gem::Version
|
|
33
|
-
version: 1.
|
|
33
|
+
version: 1.19.5
|
|
34
34
|
type: :runtime
|
|
35
35
|
prerelease: false
|
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
|
37
37
|
requirements:
|
|
38
38
|
- - ">="
|
|
39
39
|
- !ruby/object:Gem::Version
|
|
40
|
-
version: 1.
|
|
40
|
+
version: 1.19.5
|
|
41
41
|
description: |
|
|
42
42
|
Loofah is a general library for manipulating and transforming HTML/XML documents and fragments,
|
|
43
43
|
built on top of Nokogiri.
|
|
@@ -89,14 +89,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
89
89
|
requirements:
|
|
90
90
|
- - ">="
|
|
91
91
|
- !ruby/object:Gem::Version
|
|
92
|
-
version: 2.
|
|
92
|
+
version: 3.2.0
|
|
93
93
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
94
94
|
requirements:
|
|
95
95
|
- - ">="
|
|
96
96
|
- !ruby/object:Gem::Version
|
|
97
97
|
version: '0'
|
|
98
98
|
requirements: []
|
|
99
|
-
rubygems_version: 4.0.
|
|
99
|
+
rubygems_version: 4.0.20
|
|
100
100
|
specification_version: 4
|
|
101
101
|
summary: Loofah is a general library for manipulating and transforming HTML/XML documents
|
|
102
102
|
and fragments, built on top of Nokogiri.
|