loofah 2.6.0 → 2.19.1
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 +119 -0
- data/README.md +13 -12
- data/lib/loofah/elements.rb +5 -2
- data/lib/loofah/html5/safelist.rb +265 -27
- data/lib/loofah/html5/scrub.rb +136 -39
- 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,23 +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
|
+
CSS_IMPORTANT = '!important'
|
12
|
+
CSS_PROPERTY_STRING_WITHOUT_EMBEDDED_QUOTES = /\A(["'])?[^"']+\1\z/
|
13
|
+
DATA_ATTRIBUTE_NAME = /\Adata-[\w-]+\z/
|
11
14
|
|
12
15
|
class << self
|
13
16
|
def allowed_element?(element_name)
|
14
|
-
::Loofah::HTML5::SafeList::ALLOWED_ELEMENTS_WITH_LIBXML2.include?
|
17
|
+
::Loofah::HTML5::SafeList::ALLOWED_ELEMENTS_WITH_LIBXML2.include?(element_name)
|
15
18
|
end
|
16
19
|
|
17
20
|
# alternative implementation of the html5lib attribute scrubbing algorithm
|
18
21
|
def scrub_attributes(node)
|
19
22
|
node.attribute_nodes.each do |attr_node|
|
20
23
|
attr_name = if attr_node.namespace
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
24
|
+
"#{attr_node.namespace.prefix}:#{attr_node.node_name}"
|
25
|
+
else
|
26
|
+
attr_node.node_name
|
27
|
+
end
|
25
28
|
|
26
|
-
if attr_name =~
|
29
|
+
if attr_name =~ DATA_ATTRIBUTE_NAME
|
27
30
|
next
|
28
31
|
end
|
29
32
|
|
@@ -33,37 +36,28 @@ module Loofah
|
|
33
36
|
end
|
34
37
|
|
35
38
|
if SafeList::ATTR_VAL_IS_URI.include?(attr_name)
|
36
|
-
|
37
|
-
val_unescaped = CGI.unescapeHTML(attr_node.value).gsub(CONTROL_CHARACTERS, "").downcase
|
38
|
-
if val_unescaped =~ /^[a-z0-9][-+.a-z0-9]*:/ && !SafeList::ALLOWED_PROTOCOLS.include?(val_unescaped.split(SafeList::PROTOCOL_SEPARATOR)[0])
|
39
|
-
attr_node.remove
|
40
|
-
next
|
41
|
-
elsif val_unescaped.split(SafeList::PROTOCOL_SEPARATOR)[0] == "data"
|
42
|
-
# permit only allowed data mediatypes
|
43
|
-
mediatype = val_unescaped.split(SafeList::PROTOCOL_SEPARATOR)[1]
|
44
|
-
mediatype, _ = mediatype.split(";")[0..1] if mediatype
|
45
|
-
if mediatype && !SafeList::ALLOWED_URI_DATA_MEDIATYPES.include?(mediatype)
|
46
|
-
attr_node.remove
|
47
|
-
next
|
48
|
-
end
|
49
|
-
end
|
39
|
+
next if scrub_uri_attribute(attr_node)
|
50
40
|
end
|
41
|
+
|
51
42
|
if SafeList::SVG_ATTR_VAL_ALLOWS_REF.include?(attr_name)
|
52
|
-
attr_node
|
43
|
+
scrub_attribute_that_allows_local_ref(attr_node)
|
53
44
|
end
|
45
|
+
|
54
46
|
if SafeList::SVG_ALLOW_LOCAL_HREF.include?(node.name) && attr_name == "xlink:href" && attr_node.value =~ /^\s*[^#\s].*/m
|
55
47
|
attr_node.remove
|
56
48
|
next
|
57
49
|
end
|
58
50
|
end
|
59
51
|
|
60
|
-
scrub_css_attribute
|
52
|
+
scrub_css_attribute(node)
|
61
53
|
|
62
54
|
node.attribute_nodes.each do |attr_node|
|
63
|
-
|
55
|
+
if attr_node.value !~ /[^[:space:]]/ && attr_node.name !~ DATA_ATTRIBUTE_NAME
|
56
|
+
node.remove_attribute(attr_node.name)
|
57
|
+
end
|
64
58
|
end
|
65
59
|
|
66
|
-
force_correct_attribute_escaping!
|
60
|
+
force_correct_attribute_escaping!(node)
|
67
61
|
end
|
68
62
|
|
69
63
|
def scrub_css_attribute(node)
|
@@ -72,32 +66,95 @@ module Loofah
|
|
72
66
|
end
|
73
67
|
|
74
68
|
def scrub_css(style)
|
75
|
-
style_tree = Crass.parse_properties
|
69
|
+
style_tree = Crass.parse_properties(style)
|
76
70
|
sanitized_tree = []
|
77
71
|
|
78
72
|
style_tree.each do |node|
|
79
73
|
next unless node[:node] == :property
|
80
74
|
next if node[:children].any? do |child|
|
81
|
-
[:url, :bad_url].include?(child[:node])
|
75
|
+
[:url, :bad_url].include?(child[:node])
|
82
76
|
end
|
77
|
+
|
83
78
|
name = node[:name].downcase
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
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)
|
89
102
|
keyword
|
90
103
|
end
|
91
|
-
|
92
|
-
|
93
|
-
propstring = sprintf "%s:%s", name, value.join(" ")
|
94
|
-
sanitized_node = Crass.parse_properties(propstring).first
|
95
|
-
sanitized_tree << sanitized_node << CRASS_SEMICOLON
|
104
|
+
else
|
105
|
+
child[:raw]
|
96
106
|
end
|
97
|
-
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
|
98
114
|
end
|
99
115
|
|
100
|
-
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
|
101
158
|
end
|
102
159
|
|
103
160
|
#
|
@@ -125,6 +182,46 @@ module Loofah
|
|
125
182
|
end.force_encoding(encoding)
|
126
183
|
end
|
127
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
|
128
225
|
end
|
129
226
|
end
|
130
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.6.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.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.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
|