loofah 2.7.0 → 2.19.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +107 -0
- data/README.md +13 -12
- data/lib/loofah/elements.rb +5 -2
- data/lib/loofah/html5/safelist.rb +262 -27
- data/lib/loofah/html5/scrub.rb +135 -40
- data/lib/loofah/instance_methods.rb +9 -5
- data/lib/loofah/scrubber.rb +4 -0
- data/lib/loofah/scrubbers.rb +9 -8
- data/lib/loofah/version.rb +5 -0
- data/lib/loofah.rb +13 -15
- metadata +40 -137
- data/Gemfile +0 -24
- data/Manifest.txt +0 -25
- data/Rakefile +0 -97
- data/benchmark/benchmark.rb +0 -154
- data/benchmark/fragment.html +0 -96
- data/benchmark/helper.rb +0 -73
- data/benchmark/www.slashdot.com.html +0 -2560
data/lib/loofah/html5/scrub.rb
CHANGED
@@ -7,24 +7,26 @@ module Loofah
|
|
7
7
|
module Scrub
|
8
8
|
CONTROL_CHARACTERS = /[`\u0000-\u0020\u007f\u0080-\u0101]/
|
9
9
|
CSS_KEYWORDISH = /\A(#[0-9a-fA-F]+|rgb\(\d+%?,\d*%?,?\d*%?\)?|-?\d{0,3}\.?\d{0,10}(ch|cm|r?em|ex|in|lh|mm|pc|pt|px|Q|vmax|vmin|vw|vh|%|,|\))?)\z/
|
10
|
-
CRASS_SEMICOLON = { :
|
10
|
+
CRASS_SEMICOLON = { node: :semicolon, raw: ";" }
|
11
11
|
CSS_IMPORTANT = '!important'
|
12
|
+
CSS_PROPERTY_STRING_WITHOUT_EMBEDDED_QUOTES = /\A(["'])?[^"']+\1\z/
|
13
|
+
DATA_ATTRIBUTE_NAME = /\Adata-[\w-]+\z/
|
12
14
|
|
13
15
|
class << self
|
14
16
|
def allowed_element?(element_name)
|
15
|
-
::Loofah::HTML5::SafeList::ALLOWED_ELEMENTS_WITH_LIBXML2.include?
|
17
|
+
::Loofah::HTML5::SafeList::ALLOWED_ELEMENTS_WITH_LIBXML2.include?(element_name)
|
16
18
|
end
|
17
19
|
|
18
20
|
# alternative implementation of the html5lib attribute scrubbing algorithm
|
19
21
|
def scrub_attributes(node)
|
20
22
|
node.attribute_nodes.each do |attr_node|
|
21
23
|
attr_name = if attr_node.namespace
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
24
|
+
"#{attr_node.namespace.prefix}:#{attr_node.node_name}"
|
25
|
+
else
|
26
|
+
attr_node.node_name
|
27
|
+
end
|
26
28
|
|
27
|
-
if attr_name =~
|
29
|
+
if attr_name =~ DATA_ATTRIBUTE_NAME
|
28
30
|
next
|
29
31
|
end
|
30
32
|
|
@@ -34,37 +36,28 @@ module Loofah
|
|
34
36
|
end
|
35
37
|
|
36
38
|
if SafeList::ATTR_VAL_IS_URI.include?(attr_name)
|
37
|
-
|
38
|
-
val_unescaped = CGI.unescapeHTML(attr_node.value).gsub(CONTROL_CHARACTERS, "").downcase
|
39
|
-
if val_unescaped =~ /^[a-z0-9][-+.a-z0-9]*:/ && !SafeList::ALLOWED_PROTOCOLS.include?(val_unescaped.split(SafeList::PROTOCOL_SEPARATOR)[0])
|
40
|
-
attr_node.remove
|
41
|
-
next
|
42
|
-
elsif val_unescaped.split(SafeList::PROTOCOL_SEPARATOR)[0] == "data"
|
43
|
-
# permit only allowed data mediatypes
|
44
|
-
mediatype = val_unescaped.split(SafeList::PROTOCOL_SEPARATOR)[1]
|
45
|
-
mediatype, _ = mediatype.split(";")[0..1] if mediatype
|
46
|
-
if mediatype && !SafeList::ALLOWED_URI_DATA_MEDIATYPES.include?(mediatype)
|
47
|
-
attr_node.remove
|
48
|
-
next
|
49
|
-
end
|
50
|
-
end
|
39
|
+
next if scrub_uri_attribute(attr_node)
|
51
40
|
end
|
41
|
+
|
52
42
|
if SafeList::SVG_ATTR_VAL_ALLOWS_REF.include?(attr_name)
|
53
|
-
attr_node
|
43
|
+
scrub_attribute_that_allows_local_ref(attr_node)
|
54
44
|
end
|
45
|
+
|
55
46
|
if SafeList::SVG_ALLOW_LOCAL_HREF.include?(node.name) && attr_name == "xlink:href" && attr_node.value =~ /^\s*[^#\s].*/m
|
56
47
|
attr_node.remove
|
57
48
|
next
|
58
49
|
end
|
59
50
|
end
|
60
51
|
|
61
|
-
scrub_css_attribute
|
52
|
+
scrub_css_attribute(node)
|
62
53
|
|
63
54
|
node.attribute_nodes.each do |attr_node|
|
64
|
-
|
55
|
+
if attr_node.value !~ /[^[:space:]]/ && attr_node.name !~ DATA_ATTRIBUTE_NAME
|
56
|
+
node.remove_attribute(attr_node.name)
|
57
|
+
end
|
65
58
|
end
|
66
59
|
|
67
|
-
force_correct_attribute_escaping!
|
60
|
+
force_correct_attribute_escaping!(node)
|
68
61
|
end
|
69
62
|
|
70
63
|
def scrub_css_attribute(node)
|
@@ -73,33 +66,95 @@ module Loofah
|
|
73
66
|
end
|
74
67
|
|
75
68
|
def scrub_css(style)
|
76
|
-
style_tree = Crass.parse_properties
|
69
|
+
style_tree = Crass.parse_properties(style)
|
77
70
|
sanitized_tree = []
|
78
71
|
|
79
72
|
style_tree.each do |node|
|
80
73
|
next unless node[:node] == :property
|
81
74
|
next if node[:children].any? do |child|
|
82
|
-
[:url, :bad_url].include?(child[:node])
|
75
|
+
[:url, :bad_url].include?(child[:node])
|
83
76
|
end
|
77
|
+
|
84
78
|
name = node[:name].downcase
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
79
|
+
next unless SafeList::ALLOWED_CSS_PROPERTIES.include?(name) ||
|
80
|
+
SafeList::ALLOWED_SVG_PROPERTIES.include?(name) ||
|
81
|
+
SafeList::SHORTHAND_CSS_PROPERTIES.include?(name.split("-").first)
|
82
|
+
|
83
|
+
value = node[:children].map do |child|
|
84
|
+
case child[:node]
|
85
|
+
when :whitespace
|
86
|
+
nil
|
87
|
+
when :string
|
88
|
+
if child[:raw] =~ CSS_PROPERTY_STRING_WITHOUT_EMBEDDED_QUOTES
|
89
|
+
Crass::Parser.stringify(child)
|
90
|
+
else
|
91
|
+
nil
|
92
|
+
end
|
93
|
+
when :function
|
94
|
+
if SafeList::ALLOWED_CSS_FUNCTIONS.include?(child[:name].downcase)
|
95
|
+
Crass::Parser.stringify(child)
|
96
|
+
end
|
97
|
+
when :ident
|
98
|
+
keyword = child[:value]
|
99
|
+
if !SafeList::SHORTHAND_CSS_PROPERTIES.include?(name.split("-").first) ||
|
100
|
+
SafeList::ALLOWED_CSS_KEYWORDS.include?(keyword) ||
|
101
|
+
(keyword =~ CSS_KEYWORDISH)
|
90
102
|
keyword
|
91
103
|
end
|
92
|
-
|
93
|
-
|
94
|
-
value << CSS_IMPORTANT if node[:important]
|
95
|
-
propstring = sprintf "%s:%s", name, value.join(" ")
|
96
|
-
sanitized_node = Crass.parse_properties(propstring).first
|
97
|
-
sanitized_tree << sanitized_node << CRASS_SEMICOLON
|
104
|
+
else
|
105
|
+
child[:raw]
|
98
106
|
end
|
99
|
-
end
|
107
|
+
end.compact
|
108
|
+
|
109
|
+
next if value.empty?
|
110
|
+
value << CSS_IMPORTANT if node[:important]
|
111
|
+
propstring = format("%s:%s", name, value.join(" "))
|
112
|
+
sanitized_node = Crass.parse_properties(propstring).first
|
113
|
+
sanitized_tree << sanitized_node << CRASS_SEMICOLON
|
100
114
|
end
|
101
115
|
|
102
|
-
Crass::Parser.stringify
|
116
|
+
Crass::Parser.stringify(sanitized_tree)
|
117
|
+
end
|
118
|
+
|
119
|
+
def scrub_attribute_that_allows_local_ref(attr_node)
|
120
|
+
return unless attr_node.value
|
121
|
+
|
122
|
+
nodes = Crass::Parser.new(attr_node.value).parse_component_values
|
123
|
+
|
124
|
+
values = nodes.map do |node|
|
125
|
+
case node[:node]
|
126
|
+
when :url
|
127
|
+
if node[:value].start_with?("#")
|
128
|
+
node[:raw]
|
129
|
+
else
|
130
|
+
nil
|
131
|
+
end
|
132
|
+
when :hash, :ident, :string
|
133
|
+
node[:raw]
|
134
|
+
else
|
135
|
+
nil
|
136
|
+
end
|
137
|
+
end.compact
|
138
|
+
|
139
|
+
attr_node.value = values.join(" ")
|
140
|
+
end
|
141
|
+
|
142
|
+
def scrub_uri_attribute(attr_node)
|
143
|
+
# this block lifted nearly verbatim from HTML5 sanitization
|
144
|
+
val_unescaped = CGI.unescapeHTML(attr_node.value).gsub(CONTROL_CHARACTERS, "").downcase
|
145
|
+
if val_unescaped =~ /^[a-z0-9][-+.a-z0-9]*:/ && !SafeList::ALLOWED_PROTOCOLS.include?(val_unescaped.split(SafeList::PROTOCOL_SEPARATOR)[0])
|
146
|
+
attr_node.remove
|
147
|
+
return true
|
148
|
+
elsif val_unescaped.split(SafeList::PROTOCOL_SEPARATOR)[0] == "data"
|
149
|
+
# permit only allowed data mediatypes
|
150
|
+
mediatype = val_unescaped.split(SafeList::PROTOCOL_SEPARATOR)[1]
|
151
|
+
mediatype, _ = mediatype.split(";")[0..1] if mediatype
|
152
|
+
if mediatype && !SafeList::ALLOWED_URI_DATA_MEDIATYPES.include?(mediatype)
|
153
|
+
attr_node.remove
|
154
|
+
return true
|
155
|
+
end
|
156
|
+
end
|
157
|
+
false
|
103
158
|
end
|
104
159
|
|
105
160
|
#
|
@@ -127,6 +182,46 @@ module Loofah
|
|
127
182
|
end.force_encoding(encoding)
|
128
183
|
end
|
129
184
|
end
|
185
|
+
|
186
|
+
def cdata_needs_escaping?(node)
|
187
|
+
# Nokogiri's HTML4 parser on JRuby doesn't flag the child of a `style` or `script` tag as cdata, but it acts that way
|
188
|
+
node.cdata? || (Nokogiri.jruby? && node.text? && (node.parent.name == "style" || node.parent.name == "script"))
|
189
|
+
end
|
190
|
+
|
191
|
+
def cdata_escape(node)
|
192
|
+
escaped_text = escape_tags(node.text)
|
193
|
+
if Nokogiri.jruby?
|
194
|
+
node.document.create_text_node(escaped_text)
|
195
|
+
else
|
196
|
+
node.document.create_cdata(escaped_text)
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
TABLE_FOR_ESCAPE_HTML__ = {
|
201
|
+
'<' => '<',
|
202
|
+
'>' => '>',
|
203
|
+
'&' => '&',
|
204
|
+
}
|
205
|
+
|
206
|
+
def escape_tags(string)
|
207
|
+
# modified version of CGI.escapeHTML from ruby 3.1
|
208
|
+
enc = string.encoding
|
209
|
+
unless enc.ascii_compatible?
|
210
|
+
if enc.dummy?
|
211
|
+
origenc = enc
|
212
|
+
enc = Encoding::Converter.asciicompat_encoding(enc)
|
213
|
+
string = enc ? string.encode(enc) : string.b
|
214
|
+
end
|
215
|
+
table = Hash[TABLE_FOR_ESCAPE_HTML__.map {|pair|pair.map {|s|s.encode(enc)}}]
|
216
|
+
string = string.gsub(/#{"[<>&]".encode(enc)}/, table)
|
217
|
+
string.encode!(origenc) if origenc
|
218
|
+
string
|
219
|
+
else
|
220
|
+
string = string.b
|
221
|
+
string.gsub!(/[<>&]/, TABLE_FOR_ESCAPE_HTML__)
|
222
|
+
string.force_encoding(enc)
|
223
|
+
end
|
224
|
+
end
|
130
225
|
end
|
131
226
|
end
|
132
227
|
end
|
@@ -93,7 +93,11 @@ module Loofah
|
|
93
93
|
# frag.text(:encode_special_chars => false) # => "<script>alert('EVIL');</script>"
|
94
94
|
#
|
95
95
|
def text(options = {})
|
96
|
-
result = serialize_root
|
96
|
+
result = if serialize_root
|
97
|
+
serialize_root.children.reject(&:comment?).map(&:inner_text).join("")
|
98
|
+
else
|
99
|
+
""
|
100
|
+
end
|
97
101
|
if options[:encode_special_chars] == false
|
98
102
|
result # possibly dangerous if rendered in a browser
|
99
103
|
else
|
@@ -108,11 +112,11 @@ module Loofah
|
|
108
112
|
# Returns a plain-text version of the markup contained by the
|
109
113
|
# fragment, with HTML entities encoded.
|
110
114
|
#
|
111
|
-
# This method is slower than #
|
112
|
-
# whitespace around block elements.
|
115
|
+
# This method is slower than #text, but is clever about
|
116
|
+
# whitespace around block elements and line break elements.
|
113
117
|
#
|
114
|
-
# Loofah.document("<h1>Title</h1><div>Content</div>").to_text
|
115
|
-
# # => "\nTitle\n\nContent\n"
|
118
|
+
# Loofah.document("<h1>Title</h1><div>Content<br>Next line</div>").to_text
|
119
|
+
# # => "\nTitle\n\nContent\nNext line\n"
|
116
120
|
#
|
117
121
|
def to_text(options = {})
|
118
122
|
Loofah.remove_extraneous_whitespace self.dup.scrub!(:newline_block_elements).text(options)
|
data/lib/loofah/scrubber.rb
CHANGED
@@ -108,6 +108,10 @@ module Loofah
|
|
108
108
|
return Scrubber::CONTINUE
|
109
109
|
end
|
110
110
|
when Nokogiri::XML::Node::TEXT_NODE, Nokogiri::XML::Node::CDATA_SECTION_NODE
|
111
|
+
if HTML5::Scrub.cdata_needs_escaping?(node)
|
112
|
+
node.before(HTML5::Scrub.cdata_escape(node))
|
113
|
+
return Scrubber::STOP
|
114
|
+
end
|
111
115
|
return Scrubber::CONTINUE
|
112
116
|
end
|
113
117
|
Scrubber::STOP
|
data/lib/loofah/scrubbers.rb
CHANGED
@@ -100,13 +100,9 @@ module Loofah
|
|
100
100
|
|
101
101
|
def scrub(node)
|
102
102
|
return CONTINUE if html5lib_sanitize(node) == CONTINUE
|
103
|
-
|
104
|
-
sanitized_text = Loofah.fragment(node.children.first.to_html).scrub!(:strip).to_html
|
105
|
-
node.before Nokogiri::XML::Text.new(sanitized_text, node.document)
|
106
|
-
else
|
107
|
-
node.before node.children
|
108
|
-
end
|
103
|
+
node.before(node.children)
|
109
104
|
node.remove
|
105
|
+
return STOP
|
110
106
|
end
|
111
107
|
end
|
112
108
|
|
@@ -240,8 +236,13 @@ module Loofah
|
|
240
236
|
end
|
241
237
|
|
242
238
|
def scrub(node)
|
243
|
-
return CONTINUE unless Loofah::Elements::
|
244
|
-
|
239
|
+
return CONTINUE unless Loofah::Elements::LINEBREAKERS.include?(node.name)
|
240
|
+
replacement = if Loofah::Elements::INLINE_LINE_BREAK.include?(node.name)
|
241
|
+
"\n"
|
242
|
+
else
|
243
|
+
"\n#{node.content}\n"
|
244
|
+
end
|
245
|
+
node.add_next_sibling Nokogiri::XML::Text.new(replacement, node.document)
|
245
246
|
node.remove
|
246
247
|
end
|
247
248
|
end
|
data/lib/loofah.rb
CHANGED
@@ -3,21 +3,22 @@ $LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__))) unless $LOAD_PATH.i
|
|
3
3
|
|
4
4
|
require "nokogiri"
|
5
5
|
|
6
|
-
|
7
|
-
|
6
|
+
require_relative "loofah/version"
|
7
|
+
require_relative "loofah/metahelpers"
|
8
|
+
require_relative "loofah/elements"
|
8
9
|
|
9
|
-
|
10
|
-
|
11
|
-
|
10
|
+
require_relative "loofah/html5/safelist"
|
11
|
+
require_relative "loofah/html5/libxml2_workarounds"
|
12
|
+
require_relative "loofah/html5/scrub"
|
12
13
|
|
13
|
-
|
14
|
-
|
14
|
+
require_relative "loofah/scrubber"
|
15
|
+
require_relative "loofah/scrubbers"
|
15
16
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
17
|
+
require_relative "loofah/instance_methods"
|
18
|
+
require_relative "loofah/xml/document"
|
19
|
+
require_relative "loofah/xml/document_fragment"
|
20
|
+
require_relative "loofah/html/document"
|
21
|
+
require_relative "loofah/html/document_fragment"
|
21
22
|
|
22
23
|
# == Strings and IO Objects as Input
|
23
24
|
#
|
@@ -28,9 +29,6 @@ require "loofah/html/document_fragment"
|
|
28
29
|
# quantities of docs.
|
29
30
|
#
|
30
31
|
module Loofah
|
31
|
-
# The version of Loofah you are using
|
32
|
-
VERSION = "2.7.0"
|
33
|
-
|
34
32
|
class << self
|
35
33
|
# Shortcut for Loofah::HTML::Document.parse
|
36
34
|
# This method accepts the same parameters as Nokogiri::HTML::Document.parse
|
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.
|
4
|
+
version: 2.19.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mike Dalessio
|
@@ -9,22 +9,8 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2022-12-12 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
|
-
- !ruby/object:Gem::Dependency
|
15
|
-
name: nokogiri
|
16
|
-
requirement: !ruby/object:Gem::Requirement
|
17
|
-
requirements:
|
18
|
-
- - ">="
|
19
|
-
- !ruby/object:Gem::Version
|
20
|
-
version: 1.5.9
|
21
|
-
type: :runtime
|
22
|
-
prerelease: false
|
23
|
-
version_requirements: !ruby/object:Gem::Requirement
|
24
|
-
requirements:
|
25
|
-
- - ">="
|
26
|
-
- !ruby/object:Gem::Version
|
27
|
-
version: 1.5.9
|
28
14
|
- !ruby/object:Gem::Dependency
|
29
15
|
name: crass
|
30
16
|
requirement: !ruby/object:Gem::Requirement
|
@@ -40,193 +26,123 @@ dependencies:
|
|
40
26
|
- !ruby/object:Gem::Version
|
41
27
|
version: 1.0.2
|
42
28
|
- !ruby/object:Gem::Dependency
|
43
|
-
name:
|
44
|
-
requirement: !ruby/object:Gem::Requirement
|
45
|
-
requirements:
|
46
|
-
- - "~>"
|
47
|
-
- !ruby/object:Gem::Version
|
48
|
-
version: '12.3'
|
49
|
-
type: :development
|
50
|
-
prerelease: false
|
51
|
-
version_requirements: !ruby/object:Gem::Requirement
|
52
|
-
requirements:
|
53
|
-
- - "~>"
|
54
|
-
- !ruby/object:Gem::Version
|
55
|
-
version: '12.3'
|
56
|
-
- !ruby/object:Gem::Dependency
|
57
|
-
name: minitest
|
29
|
+
name: nokogiri
|
58
30
|
requirement: !ruby/object:Gem::Requirement
|
59
31
|
requirements:
|
60
|
-
- - "
|
32
|
+
- - ">="
|
61
33
|
- !ruby/object:Gem::Version
|
62
|
-
version:
|
63
|
-
type: :
|
34
|
+
version: 1.5.9
|
35
|
+
type: :runtime
|
64
36
|
prerelease: false
|
65
37
|
version_requirements: !ruby/object:Gem::Requirement
|
66
38
|
requirements:
|
67
|
-
- - "
|
39
|
+
- - ">="
|
68
40
|
- !ruby/object:Gem::Version
|
69
|
-
version:
|
41
|
+
version: 1.5.9
|
70
42
|
- !ruby/object:Gem::Dependency
|
71
|
-
name:
|
43
|
+
name: hoe-markdown
|
72
44
|
requirement: !ruby/object:Gem::Requirement
|
73
45
|
requirements:
|
74
46
|
- - "~>"
|
75
47
|
- !ruby/object:Gem::Version
|
76
|
-
version: 1.
|
48
|
+
version: '1.3'
|
77
49
|
type: :development
|
78
50
|
prerelease: false
|
79
51
|
version_requirements: !ruby/object:Gem::Requirement
|
80
52
|
requirements:
|
81
53
|
- - "~>"
|
82
54
|
- !ruby/object:Gem::Version
|
83
|
-
version: 1.
|
55
|
+
version: '1.3'
|
84
56
|
- !ruby/object:Gem::Dependency
|
85
57
|
name: json
|
86
58
|
requirement: !ruby/object:Gem::Requirement
|
87
59
|
requirements:
|
88
60
|
- - "~>"
|
89
61
|
- !ruby/object:Gem::Version
|
90
|
-
version: 2.
|
62
|
+
version: '2.2'
|
91
63
|
type: :development
|
92
64
|
prerelease: false
|
93
65
|
version_requirements: !ruby/object:Gem::Requirement
|
94
66
|
requirements:
|
95
67
|
- - "~>"
|
96
68
|
- !ruby/object:Gem::Version
|
97
|
-
version: 2.
|
69
|
+
version: '2.2'
|
98
70
|
- !ruby/object:Gem::Dependency
|
99
|
-
name:
|
71
|
+
name: minitest
|
100
72
|
requirement: !ruby/object:Gem::Requirement
|
101
73
|
requirements:
|
102
74
|
- - "~>"
|
103
75
|
- !ruby/object:Gem::Version
|
104
|
-
version: '
|
76
|
+
version: '5.14'
|
105
77
|
type: :development
|
106
78
|
prerelease: false
|
107
79
|
version_requirements: !ruby/object:Gem::Requirement
|
108
80
|
requirements:
|
109
81
|
- - "~>"
|
110
82
|
- !ruby/object:Gem::Version
|
111
|
-
version: '
|
83
|
+
version: '5.14'
|
112
84
|
- !ruby/object:Gem::Dependency
|
113
|
-
name:
|
85
|
+
name: rake
|
114
86
|
requirement: !ruby/object:Gem::Requirement
|
115
87
|
requirements:
|
116
88
|
- - "~>"
|
117
89
|
- !ruby/object:Gem::Version
|
118
|
-
version: '
|
90
|
+
version: '13.0'
|
119
91
|
type: :development
|
120
92
|
prerelease: false
|
121
93
|
version_requirements: !ruby/object:Gem::Requirement
|
122
94
|
requirements:
|
123
95
|
- - "~>"
|
124
96
|
- !ruby/object:Gem::Version
|
125
|
-
version: '
|
97
|
+
version: '13.0'
|
126
98
|
- !ruby/object:Gem::Dependency
|
127
|
-
name:
|
99
|
+
name: rdoc
|
128
100
|
requirement: !ruby/object:Gem::Requirement
|
129
101
|
requirements:
|
130
|
-
- - "
|
131
|
-
- !ruby/object:Gem::Version
|
132
|
-
version: '1.5'
|
133
|
-
type: :development
|
134
|
-
prerelease: false
|
135
|
-
version_requirements: !ruby/object:Gem::Requirement
|
136
|
-
requirements:
|
137
|
-
- - "~>"
|
102
|
+
- - ">="
|
138
103
|
- !ruby/object:Gem::Version
|
139
|
-
version: '
|
140
|
-
-
|
141
|
-
name: hoe-git
|
142
|
-
requirement: !ruby/object:Gem::Requirement
|
143
|
-
requirements:
|
144
|
-
- - "~>"
|
104
|
+
version: '4.0'
|
105
|
+
- - "<"
|
145
106
|
- !ruby/object:Gem::Version
|
146
|
-
version: '
|
107
|
+
version: '7'
|
147
108
|
type: :development
|
148
109
|
prerelease: false
|
149
110
|
version_requirements: !ruby/object:Gem::Requirement
|
150
111
|
requirements:
|
151
|
-
- - "
|
112
|
+
- - ">="
|
113
|
+
- !ruby/object:Gem::Version
|
114
|
+
version: '4.0'
|
115
|
+
- - "<"
|
152
116
|
- !ruby/object:Gem::Version
|
153
|
-
version: '
|
117
|
+
version: '7'
|
154
118
|
- !ruby/object:Gem::Dependency
|
155
|
-
name:
|
119
|
+
name: rr
|
156
120
|
requirement: !ruby/object:Gem::Requirement
|
157
121
|
requirements:
|
158
122
|
- - "~>"
|
159
123
|
- !ruby/object:Gem::Version
|
160
|
-
version:
|
124
|
+
version: 1.2.0
|
161
125
|
type: :development
|
162
126
|
prerelease: false
|
163
127
|
version_requirements: !ruby/object:Gem::Requirement
|
164
128
|
requirements:
|
165
129
|
- - "~>"
|
166
130
|
- !ruby/object:Gem::Version
|
167
|
-
version:
|
168
|
-
- !ruby/object:Gem::Dependency
|
169
|
-
name: concourse
|
170
|
-
requirement: !ruby/object:Gem::Requirement
|
171
|
-
requirements:
|
172
|
-
- - ">="
|
173
|
-
- !ruby/object:Gem::Version
|
174
|
-
version: 0.26.0
|
175
|
-
type: :development
|
176
|
-
prerelease: false
|
177
|
-
version_requirements: !ruby/object:Gem::Requirement
|
178
|
-
requirements:
|
179
|
-
- - ">="
|
180
|
-
- !ruby/object:Gem::Version
|
181
|
-
version: 0.26.0
|
131
|
+
version: 1.2.0
|
182
132
|
- !ruby/object:Gem::Dependency
|
183
133
|
name: rubocop
|
184
|
-
requirement: !ruby/object:Gem::Requirement
|
185
|
-
requirements:
|
186
|
-
- - ">="
|
187
|
-
- !ruby/object:Gem::Version
|
188
|
-
version: 0.76.0
|
189
|
-
type: :development
|
190
|
-
prerelease: false
|
191
|
-
version_requirements: !ruby/object:Gem::Requirement
|
192
|
-
requirements:
|
193
|
-
- - ">="
|
194
|
-
- !ruby/object:Gem::Version
|
195
|
-
version: 0.76.0
|
196
|
-
- !ruby/object:Gem::Dependency
|
197
|
-
name: rdoc
|
198
|
-
requirement: !ruby/object:Gem::Requirement
|
199
|
-
requirements:
|
200
|
-
- - ">="
|
201
|
-
- !ruby/object:Gem::Version
|
202
|
-
version: '4.0'
|
203
|
-
- - "<"
|
204
|
-
- !ruby/object:Gem::Version
|
205
|
-
version: '7'
|
206
|
-
type: :development
|
207
|
-
prerelease: false
|
208
|
-
version_requirements: !ruby/object:Gem::Requirement
|
209
|
-
requirements:
|
210
|
-
- - ">="
|
211
|
-
- !ruby/object:Gem::Version
|
212
|
-
version: '4.0'
|
213
|
-
- - "<"
|
214
|
-
- !ruby/object:Gem::Version
|
215
|
-
version: '7'
|
216
|
-
- !ruby/object:Gem::Dependency
|
217
|
-
name: hoe
|
218
134
|
requirement: !ruby/object:Gem::Requirement
|
219
135
|
requirements:
|
220
136
|
- - "~>"
|
221
137
|
- !ruby/object:Gem::Version
|
222
|
-
version: '
|
138
|
+
version: '1.1'
|
223
139
|
type: :development
|
224
140
|
prerelease: false
|
225
141
|
version_requirements: !ruby/object:Gem::Requirement
|
226
142
|
requirements:
|
227
143
|
- - "~>"
|
228
144
|
- !ruby/object:Gem::Version
|
229
|
-
version: '
|
145
|
+
version: '1.1'
|
230
146
|
description: |-
|
231
147
|
Loofah is a general library for manipulating and transforming HTML/XML documents and fragments, built on top of Nokogiri.
|
232
148
|
|
@@ -238,24 +154,12 @@ email:
|
|
238
154
|
- bryan@brynary.com
|
239
155
|
executables: []
|
240
156
|
extensions: []
|
241
|
-
extra_rdoc_files:
|
242
|
-
- CHANGELOG.md
|
243
|
-
- MIT-LICENSE.txt
|
244
|
-
- Manifest.txt
|
245
|
-
- README.md
|
246
|
-
- SECURITY.md
|
157
|
+
extra_rdoc_files: []
|
247
158
|
files:
|
248
159
|
- CHANGELOG.md
|
249
|
-
- Gemfile
|
250
160
|
- MIT-LICENSE.txt
|
251
|
-
- Manifest.txt
|
252
161
|
- README.md
|
253
|
-
- Rakefile
|
254
162
|
- SECURITY.md
|
255
|
-
- benchmark/benchmark.rb
|
256
|
-
- benchmark/fragment.html
|
257
|
-
- benchmark/helper.rb
|
258
|
-
- benchmark/www.slashdot.com.html
|
259
163
|
- lib/loofah.rb
|
260
164
|
- lib/loofah/elements.rb
|
261
165
|
- lib/loofah/helpers.rb
|
@@ -268,6 +172,7 @@ files:
|
|
268
172
|
- lib/loofah/metahelpers.rb
|
269
173
|
- lib/loofah/scrubber.rb
|
270
174
|
- lib/loofah/scrubbers.rb
|
175
|
+
- lib/loofah/version.rb
|
271
176
|
- lib/loofah/xml/document.rb
|
272
177
|
- lib/loofah/xml/document_fragment.rb
|
273
178
|
homepage: https://github.com/flavorjones/loofah
|
@@ -275,14 +180,12 @@ licenses:
|
|
275
180
|
- MIT
|
276
181
|
metadata:
|
277
182
|
homepage_uri: https://github.com/flavorjones/loofah
|
183
|
+
source_code_uri: https://github.com/flavorjones/loofah
|
278
184
|
bug_tracker_uri: https://github.com/flavorjones/loofah/issues
|
185
|
+
changelog_uri: https://github.com/flavorjones/loofah/blob/main/CHANGELOG.md
|
279
186
|
documentation_uri: https://www.rubydoc.info/gems/loofah/
|
280
|
-
changelog_uri: https://github.com/flavorjones/loofah/blob/master/CHANGELOG.md
|
281
|
-
source_code_uri: https://github.com/flavorjones/loofah
|
282
187
|
post_install_message:
|
283
|
-
rdoc_options:
|
284
|
-
- "--main"
|
285
|
-
- README.md
|
188
|
+
rdoc_options: []
|
286
189
|
require_paths:
|
287
190
|
- lib
|
288
191
|
required_ruby_version: !ruby/object:Gem::Requirement
|
@@ -296,7 +199,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
296
199
|
- !ruby/object:Gem::Version
|
297
200
|
version: '0'
|
298
201
|
requirements: []
|
299
|
-
rubygems_version: 3.
|
202
|
+
rubygems_version: 3.3.7
|
300
203
|
signing_key:
|
301
204
|
specification_version: 4
|
302
205
|
summary: Loofah is a general library for manipulating and transforming HTML/XML documents
|