qiita-markdown 0.18.0 → 0.19.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.
Potentially problematic release.
This version of qiita-markdown might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/.travis.yml +0 -2
- data/CHANGELOG.md +6 -0
- data/lib/qiita/markdown.rb +2 -1
- data/lib/qiita/markdown/filters/{sanitize.rb → final_sanitizer.rb} +7 -1
- data/lib/qiita/markdown/filters/simplify.rb +2 -2
- data/lib/qiita/markdown/filters/user_input_sanitizer.rb +102 -0
- data/lib/qiita/markdown/processor.rb +2 -1
- data/lib/qiita/markdown/summary_processor.rb +2 -1
- data/lib/qiita/markdown/version.rb +1 -1
- data/qiita-markdown.gemspec +1 -1
- data/spec/qiita/markdown/processor_spec.rb +931 -792
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 83c8da2e7dd55be72fce627ec6b14cb23362346e
|
4
|
+
data.tar.gz: 5752e8dae6fbbd5c9f9f94dc3e4a4320b9d222fa
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 885e5a8cc2397dfedbe970b5f85e8dc952275986912149c3a24a1068b32b1c0922b7d1fb9a19db93133410d130ccbe4cda3d45218dc15bbc9b43baf5070cf0bb
|
7
|
+
data.tar.gz: f2a6c4b200eb88f247c16a4d87b4807a083042050e862a58fb7392bfbb25428f7a710ba3c1d1bf67fff0c02f1a1ff2e9fc443107f4f2a987e7147d4b5a965ab1
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,11 @@
|
|
1
1
|
## Unreleased
|
2
2
|
|
3
|
+
## 0.19.0
|
4
|
+
|
5
|
+
- Drop 2.0 and 2.1 from support Ruby versions
|
6
|
+
- Rename `Sanitize` as `FinalSanitizer`
|
7
|
+
- Add `:strict` context for stricter sanitization
|
8
|
+
|
3
9
|
## 0.18.0
|
4
10
|
|
5
11
|
- Extract heading decoration logic from Greenmat renderer to `Toc` filter
|
data/lib/qiita/markdown.rb
CHANGED
@@ -11,16 +11,17 @@ require "qiita/markdown/filters/checkbox"
|
|
11
11
|
require "qiita/markdown/filters/code"
|
12
12
|
require "qiita/markdown/filters/emoji"
|
13
13
|
require "qiita/markdown/filters/external_link"
|
14
|
+
require "qiita/markdown/filters/final_sanitizer"
|
14
15
|
require "qiita/markdown/filters/footnote"
|
15
16
|
require "qiita/markdown/filters/greenmat"
|
16
17
|
require "qiita/markdown/filters/group_mention"
|
17
18
|
require "qiita/markdown/filters/image_link"
|
18
19
|
require "qiita/markdown/filters/mention"
|
19
|
-
require "qiita/markdown/filters/sanitize"
|
20
20
|
require "qiita/markdown/filters/simplify"
|
21
21
|
require "qiita/markdown/filters/syntax_highlight"
|
22
22
|
require "qiita/markdown/filters/toc"
|
23
23
|
require "qiita/markdown/filters/truncate"
|
24
|
+
require "qiita/markdown/filters/user_input_sanitizer"
|
24
25
|
require "qiita/markdown/greenmat/heading_rendering"
|
25
26
|
require "qiita/markdown/greenmat/html_renderer"
|
26
27
|
require "qiita/markdown/greenmat/html_toc_renderer"
|
@@ -3,7 +3,13 @@ module Qiita
|
|
3
3
|
module Filters
|
4
4
|
# Sanitizes undesirable elements by whitelist-based rule.
|
5
5
|
# You can pass optional :rule and :script context.
|
6
|
-
|
6
|
+
#
|
7
|
+
# Since this filter is applied at the end of html-pipeline, it's rules
|
8
|
+
# are intentionally weakened to allow elements and attributes which are
|
9
|
+
# generated by other filters.
|
10
|
+
#
|
11
|
+
# @see Qiita::Markdown::Filters::UserInputSanitizerr
|
12
|
+
class FinalSanitizer < HTML::Pipeline::Filter
|
7
13
|
# Wraps a node env to transform invalid node.
|
8
14
|
class TransformableNode
|
9
15
|
def self.call(*args)
|
@@ -4,7 +4,7 @@ module Qiita
|
|
4
4
|
# A filter for simplifying document structure by removing complex markups
|
5
5
|
# (mainly block elements) and complex contents.
|
6
6
|
#
|
7
|
-
# The logic of this filter is similar to the `
|
7
|
+
# The logic of this filter is similar to the `FinalSanitizer` filter, but this
|
8
8
|
# does not use the `sanitize` gem internally for the following reasons:
|
9
9
|
#
|
10
10
|
# * Each filter should do only its own responsibility, and this filter is
|
@@ -12,7 +12,7 @@ module Qiita
|
|
12
12
|
#
|
13
13
|
# * The `sanitize` gem automatically adds extra transformers even if we
|
14
14
|
# want to clean up only some elements, and they would be run in the
|
15
|
-
# `
|
15
|
+
# `FinalSanitizer` filter later.
|
16
16
|
# https://github.com/rgrove/sanitize/blob/v3.1.2/lib/sanitize.rb#L77-L100
|
17
17
|
class Simplify < HTML::Pipeline::Filter
|
18
18
|
SIMPLE_ELEMENTS = %w[a b code em i ins q s samp span strike strong sub sup var]
|
@@ -0,0 +1,102 @@
|
|
1
|
+
module Qiita
|
2
|
+
module Markdown
|
3
|
+
module Filters
|
4
|
+
# Sanitizes user input if :strict context is given.
|
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
|
+
"sup" => {
|
14
|
+
"id" => /\Afnref\d+\z/,
|
15
|
+
},
|
16
|
+
"li" => {
|
17
|
+
"id" => /\Afn\d+\z/,
|
18
|
+
},
|
19
|
+
}.freeze
|
20
|
+
|
21
|
+
DELIMITER = " ".freeze
|
22
|
+
|
23
|
+
def self.call(*args)
|
24
|
+
new(*args).transform
|
25
|
+
end
|
26
|
+
|
27
|
+
def initialize(env)
|
28
|
+
@env = env
|
29
|
+
end
|
30
|
+
|
31
|
+
def transform
|
32
|
+
return unless FILTERS.key?(name)
|
33
|
+
FILTERS[name].each_pair do |attr, pattern|
|
34
|
+
filter_attribute(attr, pattern) if node.attributes.key?(attr)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def filter_attribute(attr, pattern)
|
41
|
+
node[attr] = node[attr].split(DELIMITER).select do |value|
|
42
|
+
pattern.is_a?(Array) ? pattern.include?(value) : (pattern =~ value)
|
43
|
+
end.join(DELIMITER)
|
44
|
+
end
|
45
|
+
|
46
|
+
def name
|
47
|
+
@env[:node_name]
|
48
|
+
end
|
49
|
+
|
50
|
+
def node
|
51
|
+
@env[:node]
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
RULE = {
|
56
|
+
elements: %w[
|
57
|
+
a b blockquote br code dd del details div dl dt em font h1 h2 h3 h4 h5 h6
|
58
|
+
hr i img input ins kbd li ol p pre q rp rt ruby s samp strike strong sub
|
59
|
+
summary sup table tbody td tfoot th thead tr ul var
|
60
|
+
],
|
61
|
+
attributes: {
|
62
|
+
"a" => %w[class href rel title],
|
63
|
+
"blockquote" => %w[cite],
|
64
|
+
"code" => %w[data-metadata],
|
65
|
+
"div" => %w[class],
|
66
|
+
"font" => %w[color],
|
67
|
+
"h1" => %w[id],
|
68
|
+
"h2" => %w[id],
|
69
|
+
"h3" => %w[id],
|
70
|
+
"h4" => %w[id],
|
71
|
+
"h5" => %w[id],
|
72
|
+
"h6" => %w[id],
|
73
|
+
"img" => %w[alt height src title width],
|
74
|
+
"ins" => %w[cite datetime],
|
75
|
+
"li" => %w[id],
|
76
|
+
"q" => %w[cite],
|
77
|
+
"sup" => %w[id],
|
78
|
+
"td" => %w[colspan rowspan style],
|
79
|
+
"th" => %w[colspan rowspan style],
|
80
|
+
},
|
81
|
+
protocols: {
|
82
|
+
"a" => { "href" => ["http", "https", "mailto", :relative] },
|
83
|
+
"blockquote" => { "cite" => ["http", "https", :relative] },
|
84
|
+
"q" => { "cite" => ["http", "https", :relative] },
|
85
|
+
},
|
86
|
+
css: {
|
87
|
+
properties: %w[text-align],
|
88
|
+
},
|
89
|
+
remove_contents: %w[
|
90
|
+
script
|
91
|
+
],
|
92
|
+
transformers: AttributeFilter,
|
93
|
+
}.freeze
|
94
|
+
|
95
|
+
def call
|
96
|
+
::Sanitize.clean_node!(doc, RULE) if context[:strict]
|
97
|
+
doc
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
@@ -10,6 +10,7 @@ module Qiita
|
|
10
10
|
def self.default_filters
|
11
11
|
[
|
12
12
|
Filters::Greenmat,
|
13
|
+
Filters::UserInputSanitizer,
|
13
14
|
Filters::ImageLink,
|
14
15
|
Filters::Footnote,
|
15
16
|
Filters::Code,
|
@@ -20,7 +21,7 @@ module Qiita
|
|
20
21
|
Filters::Mention,
|
21
22
|
Filters::GroupMention,
|
22
23
|
Filters::ExternalLink,
|
23
|
-
Filters::
|
24
|
+
Filters::FinalSanitizer,
|
24
25
|
]
|
25
26
|
end
|
26
27
|
end
|
@@ -16,11 +16,12 @@ module Qiita
|
|
16
16
|
def self.default_filters
|
17
17
|
[
|
18
18
|
Filters::Greenmat,
|
19
|
+
Filters::UserInputSanitizer,
|
19
20
|
Filters::Simplify,
|
20
21
|
Filters::Emoji,
|
21
22
|
Filters::Mention,
|
22
23
|
Filters::ExternalLink,
|
23
|
-
Filters::
|
24
|
+
Filters::FinalSanitizer,
|
24
25
|
Filters::Truncate,
|
25
26
|
]
|
26
27
|
end
|
data/qiita-markdown.gemspec
CHANGED
@@ -16,7 +16,7 @@ Gem::Specification.new do |spec|
|
|
16
16
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
17
17
|
spec.require_paths = ["lib"]
|
18
18
|
|
19
|
-
spec.required_ruby_version = ">= 2.
|
19
|
+
spec.required_ruby_version = ">= 2.2.0"
|
20
20
|
|
21
21
|
spec.add_dependency "gemoji"
|
22
22
|
spec.add_dependency "github-linguist", "~> 4.0"
|
@@ -18,1025 +18,1164 @@ describe Qiita::Markdown::Processor do
|
|
18
18
|
described_class.new(context).call(markdown)
|
19
19
|
end
|
20
20
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
21
|
+
shared_examples_for "basic markdown syntax" do
|
22
|
+
context "with valid condition" do
|
23
|
+
let(:markdown) do
|
24
|
+
<<-EOS.strip_heredoc
|
25
|
+
example
|
26
|
+
EOS
|
27
|
+
end
|
27
28
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
29
|
+
it "returns a Hash with HTML output and other metadata" do
|
30
|
+
expect(result[:codes]).to be_an Array
|
31
|
+
expect(result[:mentioned_usernames]).to be_an Array
|
32
|
+
expect(result[:output]).to be_a Nokogiri::HTML::DocumentFragment
|
33
|
+
end
|
32
34
|
end
|
33
|
-
end
|
34
35
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
36
|
+
context "with HTML-characters" do
|
37
|
+
let(:markdown) do
|
38
|
+
"<>&"
|
39
|
+
end
|
39
40
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
41
|
+
it "sanitizes them" do
|
42
|
+
should eq <<-EOS.strip_heredoc
|
43
|
+
<p><>&</p>
|
44
|
+
EOS
|
45
|
+
end
|
44
46
|
end
|
45
|
-
end
|
46
47
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
48
|
+
context "with email address" do
|
49
|
+
let(:markdown) do
|
50
|
+
"test@example.com"
|
51
|
+
end
|
51
52
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
53
|
+
it "replaces with mailto link" do
|
54
|
+
should eq <<-EOS.strip_heredoc
|
55
|
+
<p><a href="mailto:test@example.com" class="autolink">test@example.com</a></p>
|
56
|
+
EOS
|
57
|
+
end
|
56
58
|
end
|
57
|
-
end
|
58
59
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
60
|
+
context "with headings" do
|
61
|
+
let(:markdown) do
|
62
|
+
<<-EOS.strip_heredoc
|
63
|
+
# a
|
64
|
+
## a
|
65
|
+
### a
|
66
|
+
### a
|
67
|
+
EOS
|
68
|
+
end
|
68
69
|
|
69
|
-
|
70
|
-
|
70
|
+
it "adds ID for ToC" do
|
71
|
+
should eq <<-EOS.strip_heredoc
|
71
72
|
|
72
|
-
|
73
|
-
|
73
|
+
<h1>
|
74
|
+
<span id="a" class="fragment"></span><a href="#a"><i class="fa fa-link"></i></a>a</h1>
|
74
75
|
|
75
|
-
|
76
|
-
|
76
|
+
<h2>
|
77
|
+
<span id="a-1" class="fragment"></span><a href="#a-1"><i class="fa fa-link"></i></a>a</h2>
|
77
78
|
|
78
|
-
|
79
|
-
|
79
|
+
<h3>
|
80
|
+
<span id="a-2" class="fragment"></span><a href="#a-2"><i class="fa fa-link"></i></a>a</h3>
|
80
81
|
|
81
|
-
|
82
|
-
|
83
|
-
|
82
|
+
<h3>
|
83
|
+
<span id="a-3" class="fragment"></span><a href="#a-3"><i class="fa fa-link"></i></a>a</h3>
|
84
|
+
EOS
|
85
|
+
end
|
84
86
|
end
|
85
|
-
end
|
86
87
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
88
|
+
context "with heading whose title includes special HTML characters" do
|
89
|
+
let(:markdown) do
|
90
|
+
<<-EOS.strip_heredoc
|
91
|
+
# <b>R&B</b>
|
92
|
+
EOS
|
93
|
+
end
|
93
94
|
|
94
|
-
|
95
|
-
|
95
|
+
it "generates fragment identifier by sanitizing the special characters in the title" do
|
96
|
+
should eq <<-EOS.strip_heredoc
|
96
97
|
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
98
|
+
<h1>
|
99
|
+
<span id="rb" class="fragment"></span><a href="#rb"><i class="fa fa-link"></i></a><b>R&B</b>
|
100
|
+
</h1>
|
101
|
+
EOS
|
102
|
+
end
|
101
103
|
end
|
102
|
-
end
|
103
104
|
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
105
|
+
context "with manually inputted heading HTML tags without id attribute" do
|
106
|
+
let(:markdown) do
|
107
|
+
<<-EOS.strip_heredoc
|
108
|
+
<h1>foo</h1>
|
109
|
+
EOS
|
110
|
+
end
|
110
111
|
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
112
|
+
it "does nothing" do
|
113
|
+
should eq <<-EOS.strip_heredoc
|
114
|
+
<h1>foo</h1>
|
115
|
+
EOS
|
116
|
+
end
|
115
117
|
end
|
116
|
-
end
|
117
118
|
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
119
|
+
context "with code" do
|
120
|
+
let(:markdown) do
|
121
|
+
<<-EOS.strip_heredoc
|
122
|
+
```foo.rb
|
123
|
+
puts 'hello world'
|
124
|
+
```
|
125
|
+
EOS
|
126
|
+
end
|
126
127
|
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
128
|
+
it "returns detected codes" do
|
129
|
+
expect(result[:codes]).to eq [
|
130
|
+
{
|
131
|
+
code: "puts 'hello world'\n",
|
132
|
+
filename: "foo.rb",
|
133
|
+
language: "ruby",
|
134
|
+
},
|
135
|
+
]
|
136
|
+
end
|
135
137
|
end
|
136
|
-
end
|
137
138
|
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
139
|
+
context "with code & filename" do
|
140
|
+
let(:markdown) do
|
141
|
+
<<-EOS.strip_heredoc
|
142
|
+
```example.rb
|
143
|
+
1
|
144
|
+
```
|
145
|
+
EOS
|
146
|
+
end
|
146
147
|
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
148
|
+
it "returns code-frame, code-lang, and highlighted pre element" do
|
149
|
+
should eq <<-EOS.strip_heredoc
|
150
|
+
<div class="code-frame" data-lang="ruby">
|
151
|
+
<div class="code-lang"><span class="bold">example.rb</span></div>
|
152
|
+
<div class="highlight"><pre><span></span><span class="mi">1</span>
|
153
|
+
</pre></div>
|
154
|
+
</div>
|
155
|
+
EOS
|
156
|
+
end
|
155
157
|
end
|
156
|
-
end
|
157
158
|
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
159
|
+
context "with code & filename with .php" do
|
160
|
+
let(:markdown) do
|
161
|
+
<<-EOS.strip_heredoc
|
162
|
+
```example.php
|
163
|
+
1
|
164
|
+
```
|
165
|
+
EOS
|
166
|
+
end
|
166
167
|
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
168
|
+
it "returns PHP code-frame" do
|
169
|
+
should eq <<-EOS.strip_heredoc
|
170
|
+
<div class="code-frame" data-lang="php">
|
171
|
+
<div class="code-lang"><span class="bold">example.php</span></div>
|
172
|
+
<div class="highlight"><pre><span></span><span class="mi">1</span>
|
173
|
+
</pre></div>
|
174
|
+
</div>
|
175
|
+
EOS
|
176
|
+
end
|
175
177
|
end
|
176
|
-
end
|
177
178
|
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
179
|
+
context "with code & no filename" do
|
180
|
+
let(:markdown) do
|
181
|
+
<<-EOS.strip_heredoc
|
182
|
+
```ruby
|
183
|
+
1
|
184
|
+
```
|
185
|
+
EOS
|
186
|
+
end
|
186
187
|
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
</div>
|
194
|
-
EOS
|
188
|
+
it "returns code-frame and highlighted pre element" do
|
189
|
+
should eq <<-EOS.strip_heredoc
|
190
|
+
<div class="code-frame" data-lang="ruby"><div class="highlight"><pre><span></span><span class="mi">1</span>
|
191
|
+
</pre></div></div>
|
192
|
+
EOS
|
193
|
+
end
|
195
194
|
end
|
196
|
-
end
|
197
195
|
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
196
|
+
context "with undefined but aliased language" do
|
197
|
+
let(:markdown) do
|
198
|
+
<<-EOS.strip_heredoc
|
199
|
+
```zsh
|
200
|
+
true
|
201
|
+
```
|
202
|
+
EOS
|
203
|
+
end
|
206
204
|
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
205
|
+
it "returns aliased language name" do
|
206
|
+
expect(result[:codes]).to eq [
|
207
|
+
{
|
208
|
+
code: "true\n",
|
209
|
+
filename: nil,
|
210
|
+
language: "bash",
|
211
|
+
},
|
212
|
+
]
|
213
|
+
end
|
212
214
|
end
|
213
|
-
end
|
214
215
|
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
true
|
220
|
-
```
|
221
|
-
EOS
|
222
|
-
end
|
216
|
+
context "with code with leading and trailing newlines" do
|
217
|
+
let(:markdown) do
|
218
|
+
<<-EOS.strip_heredoc
|
219
|
+
```
|
223
220
|
|
224
|
-
|
225
|
-
expect(result[:codes]).to eq [
|
226
|
-
{
|
227
|
-
code: "true\n",
|
228
|
-
filename: nil,
|
229
|
-
language: "bash",
|
230
|
-
},
|
231
|
-
]
|
232
|
-
end
|
233
|
-
end
|
221
|
+
foo
|
234
222
|
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
```
|
223
|
+
```
|
224
|
+
EOS
|
225
|
+
end
|
239
226
|
|
240
|
-
|
227
|
+
it "does not strip the newlines" do
|
228
|
+
should eq <<-EOS.strip_heredoc
|
229
|
+
<div class="code-frame" data-lang="text"><div class="highlight"><pre><span></span>
|
230
|
+
foo
|
241
231
|
|
242
|
-
|
243
|
-
|
232
|
+
</pre></div></div>
|
233
|
+
EOS
|
234
|
+
end
|
244
235
|
end
|
245
236
|
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
237
|
+
context "with mention" do
|
238
|
+
let(:markdown) do
|
239
|
+
"@alice"
|
240
|
+
end
|
250
241
|
|
251
|
-
|
252
|
-
|
242
|
+
it "replaces mention with link" do
|
243
|
+
should include(<<-EOS.strip_heredoc.rstrip)
|
244
|
+
<a href="/alice" class="user-mention js-hovercard" title="alice" data-hovercard-target-type="user" data-hovercard-target-name="alice">@alice</a>
|
245
|
+
EOS
|
246
|
+
end
|
253
247
|
end
|
254
|
-
end
|
255
248
|
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
EOS
|
261
|
-
end
|
249
|
+
context "with mention to short name user" do
|
250
|
+
let(:markdown) do
|
251
|
+
"@al"
|
252
|
+
end
|
262
253
|
|
263
|
-
|
264
|
-
|
254
|
+
it "replaces mention with link" do
|
255
|
+
should include(<<-EOS.strip_heredoc.rstrip)
|
256
|
+
<a href="/al" class="user-mention js-hovercard" title="al" data-hovercard-target-type="user" data-hovercard-target-name="al">@al</a>
|
257
|
+
EOS
|
258
|
+
end
|
265
259
|
end
|
266
|
-
end
|
267
260
|
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
261
|
+
context "with mentions in complex patterns" do
|
262
|
+
let(:markdown) do
|
263
|
+
<<-EOS.strip_heredoc
|
264
|
+
@alice
|
265
|
+
|
266
|
+
```
|
267
|
+
@bob
|
268
|
+
```
|
269
|
+
|
270
|
+
@charlie/@dave
|
271
|
+
@ell_en
|
272
|
+
@fran-k
|
273
|
+
@Isaac
|
274
|
+
@justin
|
275
|
+
@justin
|
276
|
+
@mallory@github
|
277
|
+
@#{'o' * 33}
|
278
|
+
@o
|
279
|
+
@o-
|
280
|
+
@-o
|
281
|
+
@o_
|
282
|
+
@_o
|
283
|
+
EOS
|
284
|
+
end
|
272
285
|
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
286
|
+
it "extracts mentions correctly" do
|
287
|
+
expect(result[:mentioned_usernames]).to eq %w[
|
288
|
+
alice
|
289
|
+
dave
|
290
|
+
ell_en
|
291
|
+
fran-k
|
292
|
+
Isaac
|
293
|
+
justin
|
294
|
+
mallory@github
|
295
|
+
o_
|
296
|
+
_o
|
297
|
+
]
|
298
|
+
end
|
277
299
|
end
|
278
300
|
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
301
|
+
context "with mention-like filename on code block" do
|
302
|
+
let(:markdown) do
|
303
|
+
<<-EOS.strip_heredoc
|
304
|
+
```ruby:@alice
|
305
|
+
1
|
306
|
+
```
|
307
|
+
EOS
|
308
|
+
end
|
283
309
|
|
284
|
-
|
285
|
-
|
286
|
-
|
310
|
+
it "does not treat it as mention" do
|
311
|
+
should include(<<-EOS.strip_heredoc.rstrip)
|
312
|
+
<div class="code-frame" data-lang="ruby">
|
313
|
+
<div class="code-lang"><span class="bold">@alice</span></div>
|
314
|
+
<div class="highlight"><pre><span></span><span class="mi">1</span>
|
315
|
+
</pre></div>
|
316
|
+
</div>
|
317
|
+
EOS
|
318
|
+
end
|
287
319
|
end
|
288
320
|
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
end
|
321
|
+
context "with mention in blockquote" do
|
322
|
+
let(:markdown) do
|
323
|
+
"> @alice"
|
324
|
+
end
|
294
325
|
|
295
|
-
|
296
|
-
|
326
|
+
it "does not replace mention with link" do
|
327
|
+
should include(<<-EOS.strip_heredoc.rstrip)
|
328
|
+
<blockquote>
|
329
|
+
<p>@alice</p>
|
330
|
+
</blockquote>
|
331
|
+
EOS
|
332
|
+
end
|
297
333
|
end
|
298
|
-
end
|
299
334
|
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
335
|
+
context "with mention to user whose name starts and ends with underscore" do
|
336
|
+
let(:markdown) do
|
337
|
+
"@_alice_"
|
338
|
+
end
|
304
339
|
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
340
|
+
it "does not emphasize the name" do
|
341
|
+
should include(<<-EOS.strip_heredoc.rstrip)
|
342
|
+
<a href="/_alice_" class="user-mention js-hovercard" title="_alice_" data-hovercard-target-type="user" data-hovercard-target-name="_alice_">@_alice_</a>
|
343
|
+
EOS
|
344
|
+
end
|
309
345
|
end
|
310
346
|
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
347
|
+
context "with allowed_usernames context" do
|
348
|
+
before do
|
349
|
+
context[:allowed_usernames] = ["alice"]
|
350
|
+
end
|
315
351
|
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
352
|
+
let(:markdown) do
|
353
|
+
<<-EOS.strip_heredoc
|
354
|
+
@alice
|
355
|
+
@bob
|
356
|
+
EOS
|
357
|
+
end
|
320
358
|
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
EOS
|
359
|
+
it "limits mentions to allowed usernames" do
|
360
|
+
expect(result[:mentioned_usernames]).to eq ["alice"]
|
361
|
+
end
|
325
362
|
end
|
326
|
-
end
|
327
363
|
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
364
|
+
context "with @all and allowed_usernames context" do
|
365
|
+
before do
|
366
|
+
context[:allowed_usernames] = ["alice", "bob"]
|
367
|
+
end
|
368
|
+
|
369
|
+
let(:markdown) do
|
370
|
+
"@all"
|
371
|
+
end
|
332
372
|
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
373
|
+
it "links it and reports all allowed users as mentioned user names" do
|
374
|
+
should include(<<-EOS.strip_heredoc.rstrip)
|
375
|
+
<a href="/" class="user-mention" title="all">@all</a>
|
376
|
+
EOS
|
377
|
+
expect(result[:mentioned_usernames]).to eq context[:allowed_usernames]
|
378
|
+
end
|
337
379
|
end
|
338
|
-
end
|
339
380
|
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
381
|
+
context "with @all and @alice" do
|
382
|
+
before do
|
383
|
+
context[:allowed_usernames] = ["alice", "bob"]
|
384
|
+
end
|
344
385
|
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
@charlie/@dave
|
350
|
-
@ell_en
|
351
|
-
@fran-k
|
352
|
-
@Isaac
|
353
|
-
@justin
|
354
|
-
@justin
|
355
|
-
@mallory@github
|
356
|
-
@#{'o' * 33}
|
357
|
-
@o
|
358
|
-
@o-
|
359
|
-
@-o
|
360
|
-
@o_
|
361
|
-
@_o
|
362
|
-
EOS
|
363
|
-
end
|
364
|
-
|
365
|
-
it "extracts mentions correctly" do
|
366
|
-
expect(result[:mentioned_usernames]).to eq %w[
|
367
|
-
alice
|
368
|
-
dave
|
369
|
-
ell_en
|
370
|
-
fran-k
|
371
|
-
Isaac
|
372
|
-
justin
|
373
|
-
mallory@github
|
374
|
-
o_
|
375
|
-
_o
|
376
|
-
]
|
377
|
-
end
|
378
|
-
end
|
386
|
+
let(:markdown) do
|
387
|
+
"@all @alice"
|
388
|
+
end
|
379
389
|
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
```ruby:@alice
|
384
|
-
1
|
385
|
-
```
|
386
|
-
EOS
|
390
|
+
it "does not duplicate mentioned user names" do
|
391
|
+
expect(result[:mentioned_usernames]).to eq context[:allowed_usernames]
|
392
|
+
end
|
387
393
|
end
|
388
394
|
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
<div class="highlight"><pre><span></span><span class="mi">1</span>
|
394
|
-
</pre></div>
|
395
|
-
</div>
|
396
|
-
EOS
|
397
|
-
end
|
398
|
-
end
|
395
|
+
context "with @all and no allowed_usernames context" do
|
396
|
+
let(:markdown) do
|
397
|
+
"@all"
|
398
|
+
end
|
399
399
|
|
400
|
-
|
401
|
-
|
402
|
-
|
400
|
+
it "does not repond to @all" do
|
401
|
+
should eq "<p>@all</p>\n"
|
402
|
+
expect(result[:mentioned_usernames]).to eq []
|
403
|
+
end
|
403
404
|
end
|
404
405
|
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
</blockquote>
|
410
|
-
EOS
|
411
|
-
end
|
412
|
-
end
|
406
|
+
context "with group mention without group_memberion_url_generator" do
|
407
|
+
let(:markdown) do
|
408
|
+
"@alice/bob"
|
409
|
+
end
|
413
410
|
|
414
|
-
|
415
|
-
|
416
|
-
|
411
|
+
it "does not replace it" do
|
412
|
+
is_expected.to eq <<-EOS.strip_heredoc
|
413
|
+
<p>@alice/bob</p>
|
414
|
+
EOS
|
415
|
+
end
|
417
416
|
end
|
418
417
|
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
418
|
+
context "with group mention" do
|
419
|
+
let(:context) do
|
420
|
+
super().merge(group_mention_url_generator: lambda do |group|
|
421
|
+
"https://#{group[:team_url_name]}.example.com/groups/#{group[:group_url_name]}"
|
422
|
+
end)
|
423
|
+
end
|
425
424
|
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
end
|
425
|
+
let(:markdown) do
|
426
|
+
"@alice/bob"
|
427
|
+
end
|
430
428
|
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
429
|
+
it "replaces it with preferred link and updates :mentioned_groups" do
|
430
|
+
is_expected.to eq <<-EOS.strip_heredoc
|
431
|
+
<p><a href="https://alice.example.com/groups/bob" rel="nofollow noopener" target="_blank">@alice/bob</a></p>
|
432
|
+
EOS
|
433
|
+
expect(result[:mentioned_groups]).to eq [{
|
434
|
+
group_url_name: "bob",
|
435
|
+
team_url_name: "alice",
|
436
|
+
}]
|
437
|
+
end
|
436
438
|
end
|
437
439
|
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
440
|
+
context "with group mention following another text" do
|
441
|
+
let(:context) do
|
442
|
+
super().merge(group_mention_url_generator: lambda do |group|
|
443
|
+
"https://#{group[:team_url_name]}.example.com/groups/#{group[:group_url_name]}"
|
444
|
+
end)
|
445
|
+
end
|
442
446
|
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
end
|
447
|
+
let(:markdown) do
|
448
|
+
"FYI @alice/bob"
|
449
|
+
end
|
447
450
|
|
448
|
-
|
449
|
-
|
451
|
+
it "preserves space after preceding text" do
|
452
|
+
is_expected.to start_with("<p>FYI <a ")
|
453
|
+
end
|
450
454
|
end
|
451
455
|
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
expect(result[:mentioned_usernames]).to eq context[:allowed_usernames]
|
457
|
-
end
|
458
|
-
end
|
456
|
+
context "with normal link" do
|
457
|
+
let(:markdown) do
|
458
|
+
"[](/example)"
|
459
|
+
end
|
459
460
|
|
460
|
-
|
461
|
-
|
462
|
-
|
461
|
+
it "creates link for that" do
|
462
|
+
should eq <<-EOS.strip_heredoc
|
463
|
+
<p><a href="/example"></a></p>
|
464
|
+
EOS
|
465
|
+
end
|
463
466
|
end
|
464
467
|
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
+
context "with anchor link" do
|
469
|
+
let(:markdown) do
|
470
|
+
"[](#example)"
|
471
|
+
end
|
468
472
|
|
469
|
-
|
470
|
-
|
473
|
+
it "creates link for that" do
|
474
|
+
should eq <<-EOS.strip_heredoc
|
475
|
+
<p><a href="#example"></a></p>
|
476
|
+
EOS
|
477
|
+
end
|
471
478
|
end
|
472
|
-
end
|
473
479
|
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
480
|
+
context "with link with title" do
|
481
|
+
let(:markdown) do
|
482
|
+
'[](/example "Title")'
|
483
|
+
end
|
478
484
|
|
479
|
-
|
480
|
-
|
481
|
-
|
485
|
+
it "creates link for that with the title" do
|
486
|
+
should eq <<-EOS.strip_heredoc
|
487
|
+
<p><a href="/example" title="Title"></a></p>
|
488
|
+
EOS
|
489
|
+
end
|
482
490
|
end
|
483
|
-
end
|
484
491
|
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
492
|
+
context "with raw URL" do
|
493
|
+
let(:markdown) do
|
494
|
+
"http://example.com/search?q=日本語"
|
495
|
+
end
|
489
496
|
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
497
|
+
it "creates link for that with .autolink class" do
|
498
|
+
should eq(
|
499
|
+
'<p><a href="http://example.com/search?q=%E6%97%A5%E6%9C%AC%E8%AA%9E" class="autolink">' \
|
500
|
+
"http://example.com/search?q=日本語</a></p>\n"
|
501
|
+
)
|
502
|
+
end
|
494
503
|
end
|
495
|
-
end
|
496
504
|
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
end)
|
502
|
-
end
|
505
|
+
context "with javascript: link" do
|
506
|
+
let(:markdown) do
|
507
|
+
"[](javascript:alert(1))"
|
508
|
+
end
|
503
509
|
|
504
|
-
|
505
|
-
|
510
|
+
it "removes that link by creating empty a element" do
|
511
|
+
should eq <<-EOS.strip_heredoc
|
512
|
+
<p><a></a></p>
|
513
|
+
EOS
|
514
|
+
end
|
506
515
|
end
|
507
516
|
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
expect(result[:mentioned_groups]).to eq [{
|
513
|
-
group_url_name: "bob",
|
514
|
-
team_url_name: "alice",
|
515
|
-
}]
|
516
|
-
end
|
517
|
-
end
|
517
|
+
context "with emoji" do
|
518
|
+
let(:markdown) do
|
519
|
+
":+1:"
|
520
|
+
end
|
518
521
|
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
"https://#{group[:team_url_name]}.example.com/groups/#{group[:group_url_name]}"
|
523
|
-
end)
|
522
|
+
it "replaces it with img element" do
|
523
|
+
should include("img")
|
524
|
+
end
|
524
525
|
end
|
525
526
|
|
526
|
-
|
527
|
-
|
528
|
-
|
527
|
+
context "with emoji in pre or code element" do
|
528
|
+
let(:markdown) do
|
529
|
+
<<-EOS.strip_heredoc
|
530
|
+
```
|
531
|
+
:+1:
|
532
|
+
```
|
533
|
+
EOS
|
534
|
+
end
|
529
535
|
|
530
|
-
|
531
|
-
|
536
|
+
it "does not replace it" do
|
537
|
+
should_not include("img")
|
538
|
+
end
|
532
539
|
end
|
533
|
-
end
|
534
540
|
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
541
|
+
context "with image notation" do
|
542
|
+
let(:markdown) do
|
543
|
+
""
|
544
|
+
end
|
539
545
|
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
|
546
|
+
it "wraps it in a element" do
|
547
|
+
should eq %(<p><a href="http://example.com/b.png" target="_blank">) +
|
548
|
+
%(<img src="http://example.com/b.png" alt="a"></a></p>\n)
|
549
|
+
end
|
544
550
|
end
|
545
|
-
end
|
546
551
|
|
547
|
-
|
548
|
-
|
549
|
-
|
550
|
-
|
552
|
+
context "with image notation with title" do
|
553
|
+
let(:markdown) do
|
554
|
+
''
|
555
|
+
end
|
551
556
|
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
|
557
|
+
it "generates <img> tag with the title" do
|
558
|
+
should eq %(<p><a href="http://example.com/b.png" target="_blank">) +
|
559
|
+
%(<img src="http://example.com/b.png" alt="a" title="Title"></a></p>\n)
|
560
|
+
end
|
556
561
|
end
|
557
|
-
end
|
558
562
|
|
559
|
-
|
560
|
-
|
561
|
-
|
562
|
-
|
563
|
+
context "with <img> tag with width and height attribute (for Retina image)" do
|
564
|
+
let(:markdown) do
|
565
|
+
'<img width="80" height="48" alt="a" src="http://example.com/b.png">'
|
566
|
+
end
|
563
567
|
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
|
568
|
-
)
|
568
|
+
it "wraps it in a element while keeping the width attribute" do
|
569
|
+
should eq %(<p><a href="http://example.com/b.png" target="_blank">) +
|
570
|
+
%(<img width="80" height="48" alt="a" src="http://example.com/b.png"></a></p>\n)
|
571
|
+
end
|
569
572
|
end
|
570
|
-
end
|
571
573
|
|
572
|
-
|
573
|
-
|
574
|
-
|
575
|
-
|
574
|
+
context "with colon-only label" do
|
575
|
+
let(:markdown) do
|
576
|
+
<<-EOS.strip_heredoc
|
577
|
+
```:
|
578
|
+
1
|
579
|
+
```
|
580
|
+
EOS
|
581
|
+
end
|
576
582
|
|
577
|
-
|
578
|
-
|
579
|
-
|
580
|
-
|
583
|
+
it "does not replace it" do
|
584
|
+
expect(result[:codes]).to eq [
|
585
|
+
{
|
586
|
+
code: "1\n",
|
587
|
+
filename: nil,
|
588
|
+
language: nil,
|
589
|
+
},
|
590
|
+
]
|
591
|
+
end
|
581
592
|
end
|
582
|
-
end
|
583
593
|
|
584
|
-
|
585
|
-
|
586
|
-
|
587
|
-
|
594
|
+
context "with font element with color attribute" do
|
595
|
+
let(:markdown) do
|
596
|
+
%[<font color="red">test</font>]
|
597
|
+
end
|
588
598
|
|
589
|
-
|
590
|
-
|
599
|
+
it "allows font element with color attribute" do
|
600
|
+
should eq <<-EOS.strip_heredoc
|
601
|
+
<p>#{markdown}</p>
|
602
|
+
EOS
|
603
|
+
end
|
591
604
|
end
|
592
|
-
end
|
593
605
|
|
594
|
-
|
595
|
-
|
596
|
-
|
597
|
-
|
598
|
-
|
599
|
-
|
600
|
-
|
601
|
-
end
|
606
|
+
context "with task list" do
|
607
|
+
let(:markdown) do
|
608
|
+
<<-EOS.strip_heredoc
|
609
|
+
- [ ] a
|
610
|
+
- [x] b
|
611
|
+
EOS
|
612
|
+
end
|
602
613
|
|
603
|
-
|
604
|
-
|
614
|
+
it "inserts checkbox" do
|
615
|
+
should eq <<-EOS.strip_heredoc
|
616
|
+
<ul>
|
617
|
+
<li class="task-list-item">
|
618
|
+
<input type="checkbox" class="task-list-item-checkbox" disabled>a</li>
|
619
|
+
<li class="task-list-item">
|
620
|
+
<input type="checkbox" class="task-list-item-checkbox" checked disabled>b</li>
|
621
|
+
</ul>
|
622
|
+
EOS
|
623
|
+
end
|
605
624
|
end
|
606
|
-
end
|
607
625
|
|
608
|
-
|
609
|
-
|
610
|
-
|
611
|
-
|
626
|
+
context "with nested task list" do
|
627
|
+
let(:markdown) do
|
628
|
+
<<-EOS.strip_heredoc
|
629
|
+
- [ ] a
|
630
|
+
- [ ] b
|
631
|
+
EOS
|
632
|
+
end
|
612
633
|
|
613
|
-
|
614
|
-
|
615
|
-
|
634
|
+
it "inserts checkbox" do
|
635
|
+
should eq <<-EOS.strip_heredoc
|
636
|
+
<ul>
|
637
|
+
<li class="task-list-item">
|
638
|
+
<input type="checkbox" class="task-list-item-checkbox" disabled>a
|
639
|
+
|
640
|
+
<ul>
|
641
|
+
<li class="task-list-item">
|
642
|
+
<input type="checkbox" class="task-list-item-checkbox" disabled>b</li>
|
643
|
+
</ul>
|
644
|
+
</li>
|
645
|
+
</ul>
|
646
|
+
EOS
|
647
|
+
end
|
616
648
|
end
|
617
|
-
end
|
618
649
|
|
619
|
-
|
620
|
-
|
621
|
-
|
622
|
-
|
623
|
-
|
624
|
-
|
625
|
-
|
626
|
-
|
650
|
+
context "with task list in code block" do
|
651
|
+
let(:markdown) do
|
652
|
+
<<-EOS.strip_heredoc
|
653
|
+
```
|
654
|
+
- [ ] a
|
655
|
+
- [x] b
|
656
|
+
```
|
657
|
+
EOS
|
658
|
+
end
|
627
659
|
|
628
|
-
|
629
|
-
|
630
|
-
|
631
|
-
|
632
|
-
|
633
|
-
|
634
|
-
|
635
|
-
]
|
660
|
+
it "does not replace checkbox" do
|
661
|
+
should eq <<-EOS.strip_heredoc
|
662
|
+
<div class="code-frame" data-lang="text"><div class="highlight"><pre><span></span>- [ ] a
|
663
|
+
- [x] b
|
664
|
+
</pre></div></div>
|
665
|
+
EOS
|
666
|
+
end
|
636
667
|
end
|
637
|
-
end
|
638
668
|
|
639
|
-
|
640
|
-
|
641
|
-
|
642
|
-
|
669
|
+
context "with empty line between task list" do
|
670
|
+
let(:markdown) do
|
671
|
+
<<-EOS.strip_heredoc
|
672
|
+
- [ ] a
|
643
673
|
|
644
|
-
|
645
|
-
|
646
|
-
|
647
|
-
EOS
|
648
|
-
end
|
649
|
-
end
|
674
|
+
- [x] b
|
675
|
+
EOS
|
676
|
+
end
|
650
677
|
|
651
|
-
|
652
|
-
|
653
|
-
|
654
|
-
|
655
|
-
|
656
|
-
|
678
|
+
it "inserts checkbox" do
|
679
|
+
should eq <<-EOS.strip_heredoc
|
680
|
+
<ul>
|
681
|
+
<li class="task-list-item"><p><input type="checkbox" class="task-list-item-checkbox" disabled>a</p></li>
|
682
|
+
<li class="task-list-item"><p><input type="checkbox" class="task-list-item-checkbox" checked disabled>b</p></li>
|
683
|
+
</ul>
|
684
|
+
EOS
|
685
|
+
end
|
657
686
|
end
|
658
687
|
|
659
|
-
|
660
|
-
|
661
|
-
|
662
|
-
|
663
|
-
<input type="checkbox" class="task-list-item-checkbox" disabled>a</li>
|
664
|
-
<li class="task-list-item">
|
665
|
-
<input type="checkbox" class="task-list-item-checkbox" checked disabled>b</li>
|
666
|
-
</ul>
|
667
|
-
EOS
|
668
|
-
end
|
669
|
-
end
|
688
|
+
context "with empty list" do
|
689
|
+
let(:markdown) do
|
690
|
+
"- \n"
|
691
|
+
end
|
670
692
|
|
671
|
-
|
672
|
-
|
673
|
-
|
674
|
-
|
675
|
-
|
676
|
-
|
693
|
+
it "inserts checkbox" do
|
694
|
+
should eq <<-EOS.strip_heredoc
|
695
|
+
<ul>
|
696
|
+
<li>
|
697
|
+
</ul>
|
698
|
+
EOS
|
699
|
+
end
|
677
700
|
end
|
678
701
|
|
679
|
-
|
680
|
-
|
681
|
-
|
682
|
-
|
683
|
-
|
702
|
+
context "with text-aligned table" do
|
703
|
+
let(:markdown) do
|
704
|
+
<<-EOS.strip_heredoc
|
705
|
+
| a | b | c |
|
706
|
+
|:---|---:|:---:|
|
707
|
+
| a | b | c |
|
708
|
+
EOS
|
709
|
+
end
|
684
710
|
|
685
|
-
|
686
|
-
|
687
|
-
|
688
|
-
|
689
|
-
|
690
|
-
|
691
|
-
|
711
|
+
it "creates table element with text-align style" do
|
712
|
+
should eq <<-EOS.strip_heredoc
|
713
|
+
<table>
|
714
|
+
<thead>
|
715
|
+
<tr>
|
716
|
+
<th style="text-align: left">a</th>
|
717
|
+
<th style="text-align: right">b</th>
|
718
|
+
<th style="text-align: center">c</th>
|
719
|
+
</tr>
|
720
|
+
</thead>
|
721
|
+
<tbody>
|
722
|
+
<tr>
|
723
|
+
<td style="text-align: left">a</td>
|
724
|
+
<td style="text-align: right">b</td>
|
725
|
+
<td style="text-align: center">c</td>
|
726
|
+
</tr>
|
727
|
+
</tbody>
|
728
|
+
</table>
|
729
|
+
EOS
|
730
|
+
end
|
692
731
|
end
|
693
|
-
end
|
694
732
|
|
695
|
-
|
696
|
-
|
697
|
-
|
698
|
-
|
699
|
-
|
700
|
-
|
701
|
-
|
702
|
-
EOS
|
703
|
-
end
|
733
|
+
context "with footenotes syntax" do
|
734
|
+
let(:markdown) do
|
735
|
+
<<-EOS.strip_heredoc
|
736
|
+
[^1]
|
737
|
+
[^1]: test
|
738
|
+
EOS
|
739
|
+
end
|
704
740
|
|
705
|
-
|
706
|
-
|
707
|
-
|
708
|
-
- [x] b
|
709
|
-
</pre></div></div>
|
710
|
-
EOS
|
711
|
-
end
|
712
|
-
end
|
741
|
+
it "generates footnotes elements" do
|
742
|
+
should eq <<-EOS.strip_heredoc
|
743
|
+
<p><sup id="fnref1"><a href="#fn1" rel="footnote" title="test">1</a></sup></p>
|
713
744
|
|
714
|
-
|
715
|
-
|
716
|
-
|
717
|
-
- [ ] a
|
745
|
+
<div class="footnotes">
|
746
|
+
<hr>
|
747
|
+
<ol>
|
718
748
|
|
719
|
-
|
720
|
-
|
721
|
-
|
749
|
+
<li id="fn1">
|
750
|
+
<p>test <a href="#fnref1">↩</a></p>
|
751
|
+
</li>
|
722
752
|
|
723
|
-
|
724
|
-
|
725
|
-
|
726
|
-
|
727
|
-
<li class="task-list-item"><p><input type="checkbox" class="task-list-item-checkbox" checked disabled>b</p></li>
|
728
|
-
</ul>
|
729
|
-
EOS
|
753
|
+
</ol>
|
754
|
+
</div>
|
755
|
+
EOS
|
756
|
+
end
|
730
757
|
end
|
731
|
-
end
|
732
758
|
|
733
|
-
|
734
|
-
|
735
|
-
|
736
|
-
|
759
|
+
context "with manually written link inside of <sup> tag" do
|
760
|
+
let(:markdown) do
|
761
|
+
<<-EOS.strip_heredoc
|
762
|
+
<sup>[Example](http://example.com/)</sup>
|
763
|
+
EOS
|
764
|
+
end
|
737
765
|
|
738
|
-
|
739
|
-
|
740
|
-
|
741
|
-
|
742
|
-
|
743
|
-
EOS
|
766
|
+
it "does not confuse the structure with automatically generated footnote reference" do
|
767
|
+
should eq <<-EOS.strip_heredoc
|
768
|
+
<p><sup><a href="http://example.com/">Example</a></sup></p>
|
769
|
+
EOS
|
770
|
+
end
|
744
771
|
end
|
745
|
-
end
|
746
772
|
|
747
|
-
|
748
|
-
|
749
|
-
|
750
|
-
|
751
|
-
|
752
|
-
|
753
|
-
|
754
|
-
|
755
|
-
|
756
|
-
|
757
|
-
|
758
|
-
|
759
|
-
<thead>
|
760
|
-
<tr>
|
761
|
-
<th style="text-align: left">a</th>
|
762
|
-
<th style="text-align: right">b</th>
|
763
|
-
<th style="text-align: center">c</th>
|
764
|
-
</tr>
|
765
|
-
</thead>
|
766
|
-
<tbody>
|
767
|
-
<tr>
|
768
|
-
<td style="text-align: left">a</td>
|
769
|
-
<td style="text-align: right">b</td>
|
770
|
-
<td style="text-align: center">c</td>
|
771
|
-
</tr>
|
772
|
-
</tbody>
|
773
|
-
</table>
|
774
|
-
EOS
|
773
|
+
context "with manually written <a> tag with strange href inside of <sup> tag" do
|
774
|
+
let(:markdown) do
|
775
|
+
<<-EOS.strip_heredoc
|
776
|
+
<sup><a href="#foo.1">Link</a></sup>
|
777
|
+
EOS
|
778
|
+
end
|
779
|
+
|
780
|
+
it "does not confuse the structure with automatically generated footnote reference" do
|
781
|
+
should eq <<-EOS.strip_heredoc
|
782
|
+
<p><sup><a href="#foo.1">Link</a></sup></p>
|
783
|
+
EOS
|
784
|
+
end
|
775
785
|
end
|
776
|
-
end
|
777
786
|
|
778
|
-
|
779
|
-
|
780
|
-
|
781
|
-
|
782
|
-
|
783
|
-
|
787
|
+
context "with markdown: { footnotes: false } context" do
|
788
|
+
before do
|
789
|
+
context[:markdown] = { footnotes: false }
|
790
|
+
end
|
791
|
+
|
792
|
+
let(:markdown) do
|
793
|
+
<<-EOS.strip_heredoc
|
794
|
+
[^1]
|
795
|
+
[^1]: test
|
796
|
+
EOS
|
797
|
+
end
|
798
|
+
|
799
|
+
it "does not generate footnote elements" do
|
800
|
+
should eq <<-EOS.strip_heredoc
|
801
|
+
<p><a href="test">^1</a></p>
|
802
|
+
EOS
|
803
|
+
end
|
784
804
|
end
|
785
805
|
|
786
|
-
|
787
|
-
|
788
|
-
|
806
|
+
context "with emoji_names and emoji_url_generator context" do
|
807
|
+
before do
|
808
|
+
context[:emoji_names] = %w(foo o)
|
789
809
|
|
790
|
-
|
791
|
-
|
792
|
-
|
810
|
+
context[:emoji_url_generator] = proc do |emoji_name|
|
811
|
+
"https://example.com/foo.png" if emoji_name == "foo"
|
812
|
+
end
|
813
|
+
end
|
793
814
|
|
794
|
-
|
795
|
-
|
796
|
-
|
815
|
+
let(:markdown) do
|
816
|
+
<<-EOS.strip_heredoc
|
817
|
+
:foo: :o: :x:
|
818
|
+
EOS
|
819
|
+
end
|
797
820
|
|
798
|
-
|
799
|
-
|
800
|
-
|
801
|
-
|
802
|
-
|
821
|
+
it "replaces only the specified emoji names with img elements with custom URL" do
|
822
|
+
should include(
|
823
|
+
'<img class="emoji" title=":foo:" alt=":foo:" src="https://example.com/foo.png"',
|
824
|
+
'<img class="emoji" title=":o:" alt=":o:" src="/images/emoji/unicode/2b55.png"'
|
825
|
+
)
|
803
826
|
|
804
|
-
|
805
|
-
|
806
|
-
|
807
|
-
|
808
|
-
EOS
|
827
|
+
should_not include(
|
828
|
+
'<img class="emoji" title=":x:" alt=":x:"'
|
829
|
+
)
|
830
|
+
end
|
809
831
|
end
|
810
832
|
|
811
|
-
|
812
|
-
|
813
|
-
|
814
|
-
|
815
|
-
end
|
816
|
-
end
|
833
|
+
context "with internal URL" do
|
834
|
+
let(:markdown) do
|
835
|
+
"http://qiita.com/?a=b"
|
836
|
+
end
|
817
837
|
|
818
|
-
|
819
|
-
|
820
|
-
|
821
|
-
<sup><a href="#foo.1">Link</a></sup>
|
822
|
-
EOS
|
823
|
-
end
|
838
|
+
let(:context) do
|
839
|
+
{ hostname: "qiita.com" }
|
840
|
+
end
|
824
841
|
|
825
|
-
|
826
|
-
|
827
|
-
|
828
|
-
|
842
|
+
it "creates link which does not have rel='nofollow noopener' and target='_blank'" do
|
843
|
+
should eq(
|
844
|
+
'<p><a href="http://qiita.com/?a=b" class="autolink">' \
|
845
|
+
"http://qiita.com/?a=b</a></p>\n"
|
846
|
+
)
|
847
|
+
end
|
829
848
|
end
|
830
|
-
end
|
831
849
|
|
832
|
-
|
833
|
-
|
834
|
-
|
835
|
-
|
850
|
+
context "with external URL" do
|
851
|
+
let(:markdown) do
|
852
|
+
"http://external.com/?a=b"
|
853
|
+
end
|
836
854
|
|
837
|
-
|
838
|
-
|
839
|
-
|
840
|
-
[^1]: test
|
841
|
-
EOS
|
842
|
-
end
|
855
|
+
let(:context) do
|
856
|
+
{ hostname: "qiita.com" }
|
857
|
+
end
|
843
858
|
|
844
|
-
|
845
|
-
|
846
|
-
|
847
|
-
|
859
|
+
it "creates link which has rel='nofollow noopener' and target='_blank'" do
|
860
|
+
should eq(
|
861
|
+
'<p><a href="http://external.com/?a=b" class="autolink" rel="nofollow noopener" target="_blank">' \
|
862
|
+
"http://external.com/?a=b</a></p>\n"
|
863
|
+
)
|
864
|
+
end
|
848
865
|
end
|
849
|
-
end
|
850
866
|
|
851
|
-
|
852
|
-
|
853
|
-
|
854
|
-
|
855
|
-
EOS
|
856
|
-
end
|
867
|
+
context "with internal anchor tag" do
|
868
|
+
let(:markdown) do
|
869
|
+
'<a href="http://qiita.com/?a=b">foobar</a>'
|
870
|
+
end
|
857
871
|
|
858
|
-
|
859
|
-
|
860
|
-
|
861
|
-
EOS
|
862
|
-
end
|
863
|
-
end
|
872
|
+
let(:context) do
|
873
|
+
{ hostname: "qiita.com" }
|
874
|
+
end
|
864
875
|
|
865
|
-
|
866
|
-
|
867
|
-
|
876
|
+
it "creates link which does not have rel='nofollow noopener' and target='_blank'" do
|
877
|
+
should eq(
|
878
|
+
"<p><a href=\"http://qiita.com/?a=b\">foobar</a></p>\n"
|
879
|
+
)
|
880
|
+
end
|
868
881
|
end
|
869
882
|
|
870
|
-
|
871
|
-
|
872
|
-
<
|
873
|
-
|
874
|
-
|
883
|
+
context "with external anchor tag" do
|
884
|
+
let(:markdown) do
|
885
|
+
'<a href="http://external.com/?a=b">foobar</a>'
|
886
|
+
end
|
887
|
+
|
888
|
+
let(:context) do
|
889
|
+
{ hostname: "qiita.com" }
|
890
|
+
end
|
875
891
|
|
876
|
-
|
877
|
-
|
878
|
-
|
879
|
-
|
892
|
+
it "creates link which has rel='nofollow noopener' and target='_blank'" do
|
893
|
+
should eq(
|
894
|
+
"<p><a href=\"http://external.com/?a=b\" rel=\"nofollow noopener\" target=\"_blank\">foobar</a></p>\n"
|
895
|
+
)
|
896
|
+
end
|
880
897
|
end
|
881
|
-
end
|
882
898
|
|
883
|
-
|
884
|
-
|
885
|
-
|
899
|
+
context "with external URL which ends with the hostname parameter" do
|
900
|
+
let(:markdown) do
|
901
|
+
"http://qqqqqqiita.com/?a=b"
|
902
|
+
end
|
886
903
|
|
887
|
-
context
|
888
|
-
"
|
904
|
+
let(:context) do
|
905
|
+
{ hostname: "qiita.com" }
|
889
906
|
end
|
890
|
-
end
|
891
907
|
|
892
|
-
|
893
|
-
|
894
|
-
|
895
|
-
|
908
|
+
it "creates link which has rel='nofollow noopener' and target='_blank'" do
|
909
|
+
should eq(
|
910
|
+
'<p><a href="http://qqqqqqiita.com/?a=b" class="autolink" rel="nofollow noopener" target="_blank">' \
|
911
|
+
"http://qqqqqqiita.com/?a=b</a></p>\n"
|
912
|
+
)
|
913
|
+
end
|
896
914
|
end
|
897
915
|
|
898
|
-
|
899
|
-
|
900
|
-
'<
|
901
|
-
|
902
|
-
)
|
916
|
+
context "with external anchor tag which ends with the hostname parameter" do
|
917
|
+
let(:markdown) do
|
918
|
+
'<a href="http://qqqqqqiita.com/?a=b">foobar</a>'
|
919
|
+
end
|
903
920
|
|
904
|
-
|
905
|
-
|
906
|
-
|
907
|
-
end
|
908
|
-
end
|
921
|
+
let(:context) do
|
922
|
+
{ hostname: "qiita.com" }
|
923
|
+
end
|
909
924
|
|
910
|
-
|
911
|
-
|
912
|
-
|
925
|
+
it "creates link which has rel='nofollow noopener' and target='_blank'" do
|
926
|
+
should eq(
|
927
|
+
"<p><a href=\"http://qqqqqqiita.com/?a=b\" rel=\"nofollow noopener\" target=\"_blank\">foobar</a></p>\n"
|
928
|
+
)
|
929
|
+
end
|
913
930
|
end
|
914
931
|
|
915
|
-
|
916
|
-
|
917
|
-
|
932
|
+
context "with sub-domain URL of hostname parameter" do
|
933
|
+
let(:markdown) do
|
934
|
+
"http://sub.qiita.com/?a=b"
|
935
|
+
end
|
918
936
|
|
919
|
-
|
920
|
-
|
921
|
-
|
922
|
-
"http://qiita.com/?a=b</a></p>\n"
|
923
|
-
)
|
924
|
-
end
|
925
|
-
end
|
937
|
+
let(:context) do
|
938
|
+
{ hostname: "qiita.com" }
|
939
|
+
end
|
926
940
|
|
927
|
-
|
928
|
-
|
929
|
-
|
941
|
+
it "creates link which has rel='nofollow noopener' and target='_blank'" do
|
942
|
+
should eq(
|
943
|
+
'<p><a href="http://sub.qiita.com/?a=b" class="autolink" rel="nofollow noopener" target="_blank">' \
|
944
|
+
"http://sub.qiita.com/?a=b</a></p>\n"
|
945
|
+
)
|
946
|
+
end
|
930
947
|
end
|
931
948
|
|
932
|
-
|
933
|
-
|
934
|
-
|
949
|
+
context "with external anchor tag which has rel attribute" do
|
950
|
+
let(:markdown) do
|
951
|
+
'<a href="http://external.com/?a=b" rel="url">foobar</a>'
|
952
|
+
end
|
935
953
|
|
936
|
-
|
937
|
-
|
938
|
-
|
939
|
-
"http://external.com/?a=b</a></p>\n"
|
940
|
-
)
|
941
|
-
end
|
942
|
-
end
|
954
|
+
let(:context) do
|
955
|
+
{ hostname: "qiita.com" }
|
956
|
+
end
|
943
957
|
|
944
|
-
|
945
|
-
|
946
|
-
|
958
|
+
it "creates link which has rel='nofollow noopener' and target='_blank', and rel value is overwritten" do
|
959
|
+
should eq(
|
960
|
+
"<p><a href=\"http://external.com/?a=b\" rel=\"nofollow noopener\" target=\"_blank\">foobar</a></p>\n"
|
961
|
+
)
|
962
|
+
end
|
947
963
|
end
|
948
964
|
|
949
|
-
|
950
|
-
|
951
|
-
|
965
|
+
context "with blockquote syntax" do
|
966
|
+
let(:markdown) do
|
967
|
+
"> foo"
|
968
|
+
end
|
952
969
|
|
953
|
-
|
954
|
-
|
955
|
-
|
956
|
-
)
|
970
|
+
it "does not confuse it with HTML tag angle brackets" do
|
971
|
+
should eq "<blockquote>\n<p>foo</p>\n</blockquote>\n"
|
972
|
+
end
|
957
973
|
end
|
958
974
|
end
|
959
975
|
|
960
|
-
|
961
|
-
|
962
|
-
|
963
|
-
|
976
|
+
shared_examples_for "script element" do |allowed:|
|
977
|
+
context "with script element" do
|
978
|
+
let(:markdown) do
|
979
|
+
<<-EOS.strip_heredoc
|
980
|
+
<script>alert(1)</script>
|
981
|
+
EOS
|
982
|
+
end
|
964
983
|
|
965
|
-
|
966
|
-
|
984
|
+
if allowed
|
985
|
+
it "allows script element" do
|
986
|
+
should eq markdown
|
987
|
+
end
|
988
|
+
|
989
|
+
context "and allowed attributes" do
|
990
|
+
let(:markdown) do
|
991
|
+
<<-EOS.strip_heredoc
|
992
|
+
<p><script async data-a="b" type="text/javascript">alert(1)</script></p>
|
993
|
+
EOS
|
994
|
+
end
|
995
|
+
|
996
|
+
it "allows data-attributes" do
|
997
|
+
should eq markdown
|
998
|
+
end
|
999
|
+
end
|
1000
|
+
else
|
1001
|
+
it "removes script element" do
|
1002
|
+
should eq "\n"
|
1003
|
+
end
|
1004
|
+
end
|
967
1005
|
end
|
1006
|
+
end
|
968
1007
|
|
969
|
-
|
970
|
-
|
971
|
-
|
972
|
-
|
1008
|
+
shared_examples_for "malicious script in filename" do |allowed:|
|
1009
|
+
context "with malicious script in filename" do
|
1010
|
+
let(:markdown) do
|
1011
|
+
<<-EOS.strip_heredoc
|
1012
|
+
```js:test<script>alert(1)</script>
|
1013
|
+
1
|
1014
|
+
```
|
1015
|
+
EOS
|
1016
|
+
end
|
1017
|
+
|
1018
|
+
if allowed
|
1019
|
+
it "does not sanitize script element" do
|
1020
|
+
should eq <<-EOS.strip_heredoc
|
1021
|
+
<div class="code-frame" data-lang="js">
|
1022
|
+
<div class="code-lang"><span class="bold">test<script>alert(1)</script></span></div>
|
1023
|
+
<div class="highlight"><pre><span></span><span class="mi">1</span>
|
1024
|
+
</pre></div>
|
1025
|
+
</div>
|
1026
|
+
EOS
|
1027
|
+
end
|
1028
|
+
else
|
1029
|
+
it "sanitizes script element" do
|
1030
|
+
should eq <<-EOS.strip_heredoc
|
1031
|
+
<div class="code-frame" data-lang="js">
|
1032
|
+
<div class="code-lang"><span class="bold">test</span></div>
|
1033
|
+
<div class="highlight"><pre><span></span><span class="mi">1</span>
|
1034
|
+
</pre></div>
|
1035
|
+
</div>
|
1036
|
+
EOS
|
1037
|
+
end
|
1038
|
+
end
|
973
1039
|
end
|
974
1040
|
end
|
975
1041
|
|
976
|
-
|
977
|
-
|
978
|
-
|
979
|
-
|
1042
|
+
shared_examples_for "iframe element" do |allowed:|
|
1043
|
+
context "with iframe" do
|
1044
|
+
let(:markdown) do
|
1045
|
+
<<-EOS.strip_heredoc
|
1046
|
+
<iframe width="1" height="2" src="//example.com" frameborder="0" allowfullscreen></iframe>
|
1047
|
+
EOS
|
1048
|
+
end
|
980
1049
|
|
981
|
-
|
982
|
-
|
1050
|
+
if allowed
|
1051
|
+
it "allows iframe with some attributes" do
|
1052
|
+
should eq markdown
|
1053
|
+
end
|
1054
|
+
else
|
1055
|
+
it "sanitizes iframe element" do
|
1056
|
+
should eq "\n"
|
1057
|
+
end
|
1058
|
+
end
|
983
1059
|
end
|
1060
|
+
end
|
1061
|
+
|
1062
|
+
shared_examples_for "data-attributes" do |allowed:|
|
1063
|
+
context "with data-attributes" do
|
1064
|
+
let(:markdown) do
|
1065
|
+
<<-EOS.strip_heredoc
|
1066
|
+
<div data-a="b"></div>
|
1067
|
+
EOS
|
1068
|
+
end
|
984
1069
|
|
985
|
-
|
986
|
-
|
987
|
-
|
988
|
-
|
989
|
-
|
1070
|
+
if allowed
|
1071
|
+
it "does not sanitize data-attributes" do
|
1072
|
+
should eq <<-EOS.strip_heredoc
|
1073
|
+
<div data-a="b"></div>
|
1074
|
+
EOS
|
1075
|
+
end
|
1076
|
+
else
|
1077
|
+
it "sanitizes data-attributes" do
|
1078
|
+
should eq <<-EOS.strip_heredoc
|
1079
|
+
<div></div>
|
1080
|
+
EOS
|
1081
|
+
end
|
1082
|
+
end
|
990
1083
|
end
|
991
1084
|
end
|
992
1085
|
|
993
|
-
|
994
|
-
|
995
|
-
|
996
|
-
|
1086
|
+
shared_examples_for "class attribute" do |allowed:|
|
1087
|
+
context "with class attribute for general tags" do
|
1088
|
+
let(:markdown) do
|
1089
|
+
'<i class="fa fa-user"></i>user'
|
1090
|
+
end
|
997
1091
|
|
998
|
-
|
999
|
-
|
1092
|
+
if allowed
|
1093
|
+
it "does not sanitize the attribute" do
|
1094
|
+
should eq "<p><i class=\"fa fa-user\"></i>user</p>\n"
|
1095
|
+
end
|
1096
|
+
else
|
1097
|
+
it "sanitizes the attribute" do
|
1098
|
+
should eq "<p><i></i>user</p>\n"
|
1099
|
+
end
|
1100
|
+
end
|
1000
1101
|
end
|
1001
1102
|
|
1002
|
-
|
1003
|
-
|
1004
|
-
|
1005
|
-
|
1103
|
+
context "with class attribute for <a> tag" do
|
1104
|
+
let(:markdown) do
|
1105
|
+
<<-EOS.strip_heredoc
|
1106
|
+
<a href="foo" class="malicious-class">foo</a>
|
1107
|
+
http://qiita.com/
|
1108
|
+
EOS
|
1109
|
+
end
|
1110
|
+
|
1111
|
+
if allowed
|
1112
|
+
it "does not sanitize the classes" do
|
1113
|
+
should eq <<-EOS.strip_heredoc
|
1114
|
+
<p><a href="foo" class="malicious-class">foo</a><br>
|
1115
|
+
<a href="http://qiita.com/" class="autolink" rel="nofollow noopener" target="_blank">http://qiita.com/</a></p>
|
1116
|
+
EOS
|
1117
|
+
end
|
1118
|
+
else
|
1119
|
+
it "sanitizes classes except `autolink`" do
|
1120
|
+
should eq <<-EOS.strip_heredoc
|
1121
|
+
<p><a href="foo" class="">foo</a><br>
|
1122
|
+
<a href="http://qiita.com/" class="autolink" rel="nofollow noopener" target="_blank">http://qiita.com/</a></p>
|
1123
|
+
EOS
|
1124
|
+
end
|
1125
|
+
end
|
1006
1126
|
end
|
1007
1127
|
end
|
1008
1128
|
|
1009
|
-
context "
|
1010
|
-
let(:
|
1011
|
-
|
1129
|
+
context "without script and strict context" do
|
1130
|
+
let(:context) do
|
1131
|
+
super().merge(script: false, strict: false)
|
1012
1132
|
end
|
1013
1133
|
|
1134
|
+
include_examples "basic markdown syntax"
|
1135
|
+
include_examples "script element", allowed: false
|
1136
|
+
include_examples "malicious script in filename", allowed: false
|
1137
|
+
include_examples "iframe element", allowed: false
|
1138
|
+
include_examples "data-attributes", allowed: false
|
1139
|
+
include_examples "class attribute", allowed: true
|
1140
|
+
end
|
1141
|
+
|
1142
|
+
context "with script context" do
|
1014
1143
|
let(:context) do
|
1015
|
-
|
1144
|
+
super().merge(script: true, strict: false)
|
1016
1145
|
end
|
1017
1146
|
|
1018
|
-
|
1019
|
-
|
1020
|
-
|
1021
|
-
|
1022
|
-
|
1023
|
-
|
1147
|
+
include_examples "basic markdown syntax"
|
1148
|
+
include_examples "script element", allowed: true
|
1149
|
+
include_examples "malicious script in filename", allowed: true
|
1150
|
+
include_examples "iframe element", allowed: true
|
1151
|
+
include_examples "data-attributes", allowed: true
|
1152
|
+
include_examples "class attribute", allowed: true
|
1024
1153
|
end
|
1025
1154
|
|
1026
|
-
context "with
|
1027
|
-
let(:
|
1028
|
-
|
1155
|
+
context "with strict context" do
|
1156
|
+
let(:context) do
|
1157
|
+
super().merge(script: false, strict: true)
|
1029
1158
|
end
|
1030
1159
|
|
1160
|
+
include_examples "basic markdown syntax"
|
1161
|
+
include_examples "script element", allowed: false
|
1162
|
+
include_examples "malicious script in filename", allowed: false
|
1163
|
+
include_examples "iframe element", allowed: false
|
1164
|
+
include_examples "data-attributes", allowed: false
|
1165
|
+
include_examples "class attribute", allowed: false
|
1166
|
+
end
|
1167
|
+
|
1168
|
+
context "with script and strict context" do
|
1031
1169
|
let(:context) do
|
1032
|
-
|
1170
|
+
super().merge(script: true, strict: true)
|
1033
1171
|
end
|
1034
1172
|
|
1035
|
-
|
1036
|
-
|
1037
|
-
|
1038
|
-
|
1039
|
-
|
1173
|
+
include_examples "basic markdown syntax"
|
1174
|
+
include_examples "script element", allowed: false
|
1175
|
+
include_examples "malicious script in filename", allowed: true
|
1176
|
+
include_examples "iframe element", allowed: false
|
1177
|
+
include_examples "data-attributes", allowed: false
|
1178
|
+
include_examples "class attribute", allowed: false
|
1040
1179
|
end
|
1041
1180
|
end
|
1042
1181
|
end
|