qiita-markdown 0.21.0 → 0.22.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of qiita-markdown might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/.rubocop_todo.yml +1 -0
- data/CHANGELOG.md +4 -0
- data/lib/qiita/markdown.rb +4 -0
- data/lib/qiita/markdown/code_pen.rb +8 -0
- data/lib/qiita/markdown/filters/final_sanitizer.rb +6 -43
- data/lib/qiita/markdown/filters/user_input_sanitizer.rb +6 -59
- data/lib/qiita/markdown/transformers/filter_attributes.rb +63 -0
- data/lib/qiita/markdown/transformers/filter_script.rb +40 -0
- data/lib/qiita/markdown/transformers/strip_invalid_node.rb +44 -0
- data/lib/qiita/markdown/version.rb +1 -1
- data/spec/qiita/markdown/processor_spec.rb +76 -1
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ff3cf2974ec3776febf776a8509ea134b7e6b94d
|
4
|
+
data.tar.gz: 16d059b677b0cf622be0d3c85e4ad9e293ffcdef
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ab3e0be1351d8872fc3cc8d75502a3f8bd95f24a9870af84c164676b52e84f0ee34a72f7321db28c3889d133d167ec14c1ef32d0de9da3a918b0f4cdc4c43496
|
7
|
+
data.tar.gz: b91800c0d2282cc001d7fa9df45f1688f721c47d82ab5a01b62e656a10f2ccb7d8a18901076261bbc0ef88945ba87f042fd9cc54001b2638def2ebbfa83fe47e
|
data/.rubocop_todo.yml
CHANGED
@@ -57,6 +57,7 @@ Style/MutableConstant:
|
|
57
57
|
- 'lib/qiita/markdown/filters/sanitize.rb'
|
58
58
|
- 'lib/qiita/markdown/filters/simplify.rb'
|
59
59
|
- 'lib/qiita/markdown/filters/syntax_highlight.rb'
|
60
|
+
- 'lib/qiita/markdown/code_pen.rb'
|
60
61
|
- 'lib/qiita/markdown/processor.rb'
|
61
62
|
- 'lib/qiita/markdown/summary_processor.rb'
|
62
63
|
- 'lib/qiita/markdown/version.rb'
|
data/CHANGELOG.md
CHANGED
data/lib/qiita/markdown.rb
CHANGED
@@ -7,6 +7,10 @@ require "nokogiri"
|
|
7
7
|
require "pygments"
|
8
8
|
require "sanitize"
|
9
9
|
|
10
|
+
require "qiita/markdown/code_pen"
|
11
|
+
require "qiita/markdown/transformers/filter_attributes"
|
12
|
+
require "qiita/markdown/transformers/filter_script"
|
13
|
+
require "qiita/markdown/transformers/strip_invalid_node"
|
10
14
|
require "qiita/markdown/filters/checkbox"
|
11
15
|
require "qiita/markdown/filters/code_block"
|
12
16
|
require "qiita/markdown/filters/emoji"
|
@@ -10,45 +10,6 @@ module Qiita
|
|
10
10
|
#
|
11
11
|
# @see Qiita::Markdown::Filters::UserInputSanitizerr
|
12
12
|
class FinalSanitizer < HTML::Pipeline::Filter
|
13
|
-
# Wraps a node env to transform invalid node.
|
14
|
-
class TransformableNode
|
15
|
-
def self.call(*args)
|
16
|
-
new(*args).transform
|
17
|
-
end
|
18
|
-
|
19
|
-
def initialize(env)
|
20
|
-
@env = env
|
21
|
-
end
|
22
|
-
|
23
|
-
def transform
|
24
|
-
if has_invalid_list_node? || has_invalid_table_node?
|
25
|
-
node.replace(node.children)
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
private
|
30
|
-
|
31
|
-
def has_invalid_list_node?
|
32
|
-
name == "li" && node.ancestors.none? do |ancestor|
|
33
|
-
%w[ol ul].include?(ancestor.name)
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
def has_invalid_table_node?
|
38
|
-
%w[thead tbody tfoot tr td th].include?(name) && node.ancestors.none? do |ancestor|
|
39
|
-
ancestor.name == "table"
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
def name
|
44
|
-
@env[:node_name]
|
45
|
-
end
|
46
|
-
|
47
|
-
def node
|
48
|
-
@env[:node]
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
13
|
RULE = {
|
53
14
|
attributes: {
|
54
15
|
"a" => [
|
@@ -80,6 +41,7 @@ module Qiita
|
|
80
41
|
"itemscope",
|
81
42
|
"itemtype",
|
82
43
|
],
|
44
|
+
"p" => CodePen::ATTRIBUTES,
|
83
45
|
"script" => [
|
84
46
|
"async",
|
85
47
|
"src",
|
@@ -173,6 +135,7 @@ module Qiita
|
|
173
135
|
"ruby",
|
174
136
|
"s",
|
175
137
|
"samp",
|
138
|
+
"script",
|
176
139
|
"span",
|
177
140
|
"strike",
|
178
141
|
"strong",
|
@@ -219,17 +182,17 @@ module Qiita
|
|
219
182
|
],
|
220
183
|
},
|
221
184
|
},
|
222
|
-
|
223
|
-
|
185
|
+
transformers: [
|
186
|
+
Transformers::StripInvalidNode,
|
187
|
+
Transformers::FilterScript,
|
224
188
|
],
|
225
|
-
transformers: TransformableNode,
|
226
189
|
}.freeze
|
227
190
|
|
228
191
|
SCRIPTABLE_RULE = RULE.dup.tap do |rule|
|
229
192
|
rule[:attributes] = RULE[:attributes].dup
|
230
193
|
rule[:attributes][:all] = rule[:attributes][:all] + [:data]
|
231
194
|
rule[:elements] = RULE[:elements] + ["iframe", "script", "video"]
|
232
|
-
rule[:
|
195
|
+
rule[:transformers] = rule[:transformers] - [Transformers::FilterScript]
|
233
196
|
end
|
234
197
|
|
235
198
|
def call
|
@@ -3,65 +3,10 @@ module Qiita
|
|
3
3
|
module Filters
|
4
4
|
# Sanitizes user input if :strict context is given.
|
5
5
|
class UserInputSanitizer < HTML::Pipeline::Filter
|
6
|
-
class AttributeFilter
|
7
|
-
FILTERS = {
|
8
|
-
"a" => {
|
9
|
-
"class" => %w[autolink],
|
10
|
-
"rel" => %w[footnote url],
|
11
|
-
"rev" => %w[footnote],
|
12
|
-
},
|
13
|
-
"blockquote" => {
|
14
|
-
"class" => %w[twitter-tweet],
|
15
|
-
},
|
16
|
-
"div" => {
|
17
|
-
"class" => %w[footnotes],
|
18
|
-
},
|
19
|
-
"sup" => {
|
20
|
-
"id" => /\Afnref\d+\z/,
|
21
|
-
},
|
22
|
-
"li" => {
|
23
|
-
"id" => /\Afn\d+\z/,
|
24
|
-
},
|
25
|
-
}.freeze
|
26
|
-
|
27
|
-
DELIMITER = " ".freeze
|
28
|
-
|
29
|
-
def self.call(*args)
|
30
|
-
new(*args).transform
|
31
|
-
end
|
32
|
-
|
33
|
-
def initialize(env)
|
34
|
-
@env = env
|
35
|
-
end
|
36
|
-
|
37
|
-
def transform
|
38
|
-
return unless FILTERS.key?(name)
|
39
|
-
FILTERS[name].each_pair do |attr, pattern|
|
40
|
-
filter_attribute(attr, pattern) if node.attributes.key?(attr)
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
private
|
45
|
-
|
46
|
-
def filter_attribute(attr, pattern)
|
47
|
-
node[attr] = node[attr].split(DELIMITER).select do |value|
|
48
|
-
pattern.is_a?(Array) ? pattern.include?(value) : (pattern =~ value)
|
49
|
-
end.join(DELIMITER)
|
50
|
-
end
|
51
|
-
|
52
|
-
def name
|
53
|
-
@env[:node_name]
|
54
|
-
end
|
55
|
-
|
56
|
-
def node
|
57
|
-
@env[:node]
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
6
|
RULE = {
|
62
7
|
elements: %w[
|
63
8
|
a b blockquote br code dd del details div dl dt em font h1 h2 h3 h4 h5 h6
|
64
|
-
hr i img ins kbd li ol p pre q rp rt ruby s samp strike strong sub
|
9
|
+
hr i img ins kbd li ol p pre q rp rt ruby s samp script strike strong sub
|
65
10
|
summary sup table tbody td tfoot th thead tr ul var
|
66
11
|
],
|
67
12
|
attributes: {
|
@@ -79,7 +24,9 @@ module Qiita
|
|
79
24
|
"img" => %w[alt height src title width],
|
80
25
|
"ins" => %w[cite datetime],
|
81
26
|
"li" => %w[id],
|
27
|
+
"p" => CodePen::ATTRIBUTES,
|
82
28
|
"q" => %w[cite],
|
29
|
+
"script" => %w[async src],
|
83
30
|
"sup" => %w[id],
|
84
31
|
"td" => %w[colspan rowspan style],
|
85
32
|
"th" => %w[colspan rowspan style],
|
@@ -92,10 +39,10 @@ module Qiita
|
|
92
39
|
css: {
|
93
40
|
properties: %w[text-align],
|
94
41
|
},
|
95
|
-
|
96
|
-
|
42
|
+
transformers: [
|
43
|
+
Transformers::FilterAttributes,
|
44
|
+
Transformers::FilterScript,
|
97
45
|
],
|
98
|
-
transformers: AttributeFilter,
|
99
46
|
}.freeze
|
100
47
|
|
101
48
|
def call
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module Qiita
|
2
|
+
module Markdown
|
3
|
+
module Transformers
|
4
|
+
class FilterAttributes
|
5
|
+
FILTERS = {
|
6
|
+
"a" => {
|
7
|
+
"class" => %w[autolink],
|
8
|
+
"rel" => %w[footnote url],
|
9
|
+
"rev" => %w[footnote],
|
10
|
+
},
|
11
|
+
"blockquote" => {
|
12
|
+
"class" => %w[twitter-tweet],
|
13
|
+
},
|
14
|
+
"div" => {
|
15
|
+
"class" => %w[footnotes],
|
16
|
+
},
|
17
|
+
"p" => {
|
18
|
+
"class" => %w[codepen],
|
19
|
+
},
|
20
|
+
"sup" => {
|
21
|
+
"id" => /\Afnref\d+\z/,
|
22
|
+
},
|
23
|
+
"li" => {
|
24
|
+
"id" => /\Afn\d+\z/,
|
25
|
+
},
|
26
|
+
}.freeze
|
27
|
+
|
28
|
+
DELIMITER = " ".freeze
|
29
|
+
|
30
|
+
def self.call(*args)
|
31
|
+
new(*args).transform
|
32
|
+
end
|
33
|
+
|
34
|
+
def initialize(env)
|
35
|
+
@env = env
|
36
|
+
end
|
37
|
+
|
38
|
+
def transform
|
39
|
+
return unless FILTERS.key?(name)
|
40
|
+
FILTERS[name].each_pair do |attr, pattern|
|
41
|
+
filter_attribute(attr, pattern) if node.attributes.key?(attr)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def filter_attribute(attr, pattern)
|
48
|
+
node[attr] = node[attr].split(DELIMITER).select do |value|
|
49
|
+
pattern.is_a?(Array) ? pattern.include?(value) : (pattern =~ value)
|
50
|
+
end.join(DELIMITER)
|
51
|
+
end
|
52
|
+
|
53
|
+
def name
|
54
|
+
@env[:node_name]
|
55
|
+
end
|
56
|
+
|
57
|
+
def node
|
58
|
+
@env[:node]
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Qiita
|
2
|
+
module Markdown
|
3
|
+
module Transformers
|
4
|
+
class FilterScript
|
5
|
+
WHITE_LIST = [
|
6
|
+
CodePen::SCRIPT_URL,
|
7
|
+
].freeze
|
8
|
+
|
9
|
+
def self.call(*args)
|
10
|
+
new(*args).transform
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize(env)
|
14
|
+
@env = env
|
15
|
+
end
|
16
|
+
|
17
|
+
def transform
|
18
|
+
if name == "script"
|
19
|
+
if WHITE_LIST.include?(node["src"])
|
20
|
+
node["async"] = "async" unless node.attributes.key?("async")
|
21
|
+
node.children.unlink
|
22
|
+
else
|
23
|
+
node.unlink
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def name
|
31
|
+
@env[:node_name]
|
32
|
+
end
|
33
|
+
|
34
|
+
def node
|
35
|
+
@env[:node]
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Qiita
|
2
|
+
module Markdown
|
3
|
+
module Transformers
|
4
|
+
# Wraps a node env to transform invalid node.
|
5
|
+
class StripInvalidNode
|
6
|
+
def self.call(*args)
|
7
|
+
new(*args).transform
|
8
|
+
end
|
9
|
+
|
10
|
+
def initialize(env)
|
11
|
+
@env = env
|
12
|
+
end
|
13
|
+
|
14
|
+
def transform
|
15
|
+
if has_invalid_list_node? || has_invalid_table_node?
|
16
|
+
node.replace(node.children)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def has_invalid_list_node?
|
23
|
+
name == "li" && node.ancestors.none? do |ancestor|
|
24
|
+
%w[ol ul].include?(ancestor.name)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def has_invalid_table_node?
|
29
|
+
%w[thead tbody tfoot tr td th].include?(name) && node.ancestors.none? do |ancestor|
|
30
|
+
ancestor.name == "table"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def name
|
35
|
+
@env[:node_name]
|
36
|
+
end
|
37
|
+
|
38
|
+
def node
|
39
|
+
@env[:node]
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -1140,7 +1140,7 @@ describe Qiita::Markdown::Processor do
|
|
1140
1140
|
end
|
1141
1141
|
|
1142
1142
|
shared_examples_for "data-attributes" do |allowed:|
|
1143
|
-
context "with data-attributes" do
|
1143
|
+
context "with data-attributes for general tags" do
|
1144
1144
|
let(:markdown) do
|
1145
1145
|
<<-EOS.strip_heredoc
|
1146
1146
|
<div data-a="b"></div>
|
@@ -1161,6 +1161,28 @@ describe Qiita::Markdown::Processor do
|
|
1161
1161
|
end
|
1162
1162
|
end
|
1163
1163
|
end
|
1164
|
+
|
1165
|
+
context "with data-attributes for <p> tag" do
|
1166
|
+
let(:markdown) do
|
1167
|
+
<<-EOS.strip_heredoc
|
1168
|
+
<p data-slug-hash="a" data-malicious="b"></p>
|
1169
|
+
EOS
|
1170
|
+
end
|
1171
|
+
|
1172
|
+
if allowed
|
1173
|
+
it "does not sanitize data-attributes" do
|
1174
|
+
should eq <<-EOS.strip_heredoc
|
1175
|
+
<p data-slug-hash="a" data-malicious="b"></p>
|
1176
|
+
EOS
|
1177
|
+
end
|
1178
|
+
else
|
1179
|
+
it "sanitizes data-attributes except the attributes used by codepen" do
|
1180
|
+
should eq <<-EOS.strip_heredoc
|
1181
|
+
<p data-slug-hash="a"></p>
|
1182
|
+
EOS
|
1183
|
+
end
|
1184
|
+
end
|
1185
|
+
end
|
1164
1186
|
end
|
1165
1187
|
|
1166
1188
|
shared_examples_for "class attribute" do |allowed:|
|
@@ -1248,6 +1270,28 @@ describe Qiita::Markdown::Processor do
|
|
1248
1270
|
end
|
1249
1271
|
end
|
1250
1272
|
end
|
1273
|
+
|
1274
|
+
context "with class attribute for <p> tag" do
|
1275
|
+
let(:markdown) do
|
1276
|
+
<<-EOS.strip_heredoc
|
1277
|
+
<p class="codepen malicious-class">foo</p>
|
1278
|
+
EOS
|
1279
|
+
end
|
1280
|
+
|
1281
|
+
if allowed
|
1282
|
+
it "does not sanitize the classes" do
|
1283
|
+
should eq <<-EOS.strip_heredoc
|
1284
|
+
<p class="codepen malicious-class">foo</p>
|
1285
|
+
EOS
|
1286
|
+
end
|
1287
|
+
else
|
1288
|
+
it "sanitizes classes except `codepen`" do
|
1289
|
+
should eq <<-EOS.strip_heredoc
|
1290
|
+
<p class="codepen">foo</p>
|
1291
|
+
EOS
|
1292
|
+
end
|
1293
|
+
end
|
1294
|
+
end
|
1251
1295
|
end
|
1252
1296
|
|
1253
1297
|
shared_examples_for "background-color" do |allowed:|
|
@@ -1268,6 +1312,33 @@ describe Qiita::Markdown::Processor do
|
|
1268
1312
|
end
|
1269
1313
|
end
|
1270
1314
|
|
1315
|
+
shared_examples_for "override codepen attributes" do |allowed:|
|
1316
|
+
context "with HTML embed code for CodePen" do
|
1317
|
+
let(:markdown) do
|
1318
|
+
<<-EOS.strip_heredoc
|
1319
|
+
<p data-height="1" data-theme-id="0" data-slug-hash="foo" data-default-tab="bar" data-user="baz" data-embed-version="2" data-pen-title="qux" class="codepen"></p>
|
1320
|
+
<script src="https://production-assets.codepen.io/assets/embed/ei.js"></script>
|
1321
|
+
EOS
|
1322
|
+
end
|
1323
|
+
|
1324
|
+
if allowed
|
1325
|
+
it "does not sanitize embed code" do
|
1326
|
+
should eq <<-EOS.strip_heredoc
|
1327
|
+
<p data-height="1" data-theme-id="0" data-slug-hash="foo" data-default-tab="bar" data-user="baz" data-embed-version="2" data-pen-title="qux" class="codepen"></p>\n
|
1328
|
+
<script src="https://production-assets.codepen.io/assets/embed/ei.js"></script>
|
1329
|
+
EOS
|
1330
|
+
end
|
1331
|
+
else
|
1332
|
+
it "sanitizes data-attributes except the minimum attributes and force async attribute" do
|
1333
|
+
should eq <<-EOS.strip_heredoc
|
1334
|
+
<p data-slug-hash="foo" data-embed-version="2" class="codepen"></p>\n
|
1335
|
+
<script src="https://production-assets.codepen.io/assets/embed/ei.js" async="async"></script>
|
1336
|
+
EOS
|
1337
|
+
end
|
1338
|
+
end
|
1339
|
+
end
|
1340
|
+
end
|
1341
|
+
|
1271
1342
|
context "without script and strict context" do
|
1272
1343
|
let(:context) do
|
1273
1344
|
super().merge(script: false, strict: false)
|
@@ -1281,6 +1352,7 @@ describe Qiita::Markdown::Processor do
|
|
1281
1352
|
include_examples "data-attributes", allowed: false
|
1282
1353
|
include_examples "class attribute", allowed: true
|
1283
1354
|
include_examples "background-color", allowed: true
|
1355
|
+
include_examples "override codepen attributes", allowed: false
|
1284
1356
|
end
|
1285
1357
|
|
1286
1358
|
context "with script context" do
|
@@ -1296,6 +1368,7 @@ describe Qiita::Markdown::Processor do
|
|
1296
1368
|
include_examples "data-attributes", allowed: true
|
1297
1369
|
include_examples "class attribute", allowed: true
|
1298
1370
|
include_examples "background-color", allowed: true
|
1371
|
+
include_examples "override codepen attributes", allowed: true
|
1299
1372
|
end
|
1300
1373
|
|
1301
1374
|
context "with strict context" do
|
@@ -1311,6 +1384,7 @@ describe Qiita::Markdown::Processor do
|
|
1311
1384
|
include_examples "data-attributes", allowed: false
|
1312
1385
|
include_examples "class attribute", allowed: false
|
1313
1386
|
include_examples "background-color", allowed: false
|
1387
|
+
include_examples "override codepen attributes", allowed: false
|
1314
1388
|
end
|
1315
1389
|
|
1316
1390
|
context "with script and strict context" do
|
@@ -1326,6 +1400,7 @@ describe Qiita::Markdown::Processor do
|
|
1326
1400
|
include_examples "data-attributes", allowed: false
|
1327
1401
|
include_examples "class attribute", allowed: false
|
1328
1402
|
include_examples "background-color", allowed: false
|
1403
|
+
include_examples "override codepen attributes", allowed: false
|
1329
1404
|
end
|
1330
1405
|
end
|
1331
1406
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: qiita-markdown
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.22.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ryo Nakamura
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-11-
|
11
|
+
date: 2017-11-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: gemoji
|
@@ -256,6 +256,7 @@ files:
|
|
256
256
|
- lib/qiita-markdown.rb
|
257
257
|
- lib/qiita/markdown.rb
|
258
258
|
- lib/qiita/markdown/base_processor.rb
|
259
|
+
- lib/qiita/markdown/code_pen.rb
|
259
260
|
- lib/qiita/markdown/filters/checkbox.rb
|
260
261
|
- lib/qiita/markdown/filters/code_block.rb
|
261
262
|
- lib/qiita/markdown/filters/emoji.rb
|
@@ -277,6 +278,9 @@ files:
|
|
277
278
|
- lib/qiita/markdown/greenmat/html_toc_renderer.rb
|
278
279
|
- lib/qiita/markdown/processor.rb
|
279
280
|
- lib/qiita/markdown/summary_processor.rb
|
281
|
+
- lib/qiita/markdown/transformers/filter_attributes.rb
|
282
|
+
- lib/qiita/markdown/transformers/filter_script.rb
|
283
|
+
- lib/qiita/markdown/transformers/strip_invalid_node.rb
|
280
284
|
- lib/qiita/markdown/version.rb
|
281
285
|
- qiita-markdown.gemspec
|
282
286
|
- spec/qiita/markdown/filters/greenmat_spec.rb
|