1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
5 defmodule Pleroma.HTMLTest do
8 alias Pleroma.Web.CommonAPI
11 import Pleroma.Factory
14 <b>this is in bold</b>
15 <p>this is a paragraph</p>
16 this is a linebreak<br />
17 this is a link with allowed "rel" attribute: <a href="http://example.com/" rel="tag">example.com</a>
18 this is a link with not allowed "rel" attribute: <a href="http://example.com/" rel="tag noallowed">example.com</a>
19 this is an image: <img src="http://example.com/image.jpg"><br />
20 <script>alert('hacked')</script>
23 @html_onerror_sample """
24 <img src="http://example.com/image.jpg" onerror="alert('hacked')">
27 @html_span_class_sample """
28 <span class="animate-spin">hi</span>
31 @html_span_microformats_sample """
32 <span class="h-card"><a class="u-url mention">@<span>foo</span></a></span>
35 @html_span_invalid_microformats_sample """
36 <span class="h-card"><a class="u-url mention animate-spin">@<span>foo</span></a></span>
39 describe "StripTags scrubber" do
40 test "works as expected" do
45 this is a link with allowed "rel" attribute: example.com
46 this is a link with not allowed "rel" attribute: example.com
48 alert('hacked')
51 assert expected == HTML.strip_tags(@html_sample)
54 test "does not allow attribute-based XSS" do
57 assert expected == HTML.strip_tags(@html_onerror_sample)
61 describe "TwitterText scrubber" do
62 test "normalizes HTML as expected" do
65 <p>this is a paragraph</p>
66 this is a linebreak<br/>
67 this is a link with allowed "rel" attribute: <a href="http://example.com/" rel="tag">example.com</a>
68 this is a link with not allowed "rel" attribute: <a href="http://example.com/">example.com</a>
69 this is an image: <img src="http://example.com/image.jpg"/><br/>
70 alert('hacked')
73 assert expected == HTML.filter_tags(@html_sample, Pleroma.HTML.Scrubber.TwitterText)
76 test "does not allow attribute-based XSS" do
78 <img src="http://example.com/image.jpg"/>
81 assert expected == HTML.filter_tags(@html_onerror_sample, Pleroma.HTML.Scrubber.TwitterText)
84 test "does not allow spans with invalid classes" do
90 HTML.filter_tags(@html_span_class_sample, Pleroma.HTML.Scrubber.TwitterText)
93 test "does allow microformats" do
95 <span class="h-card"><a class="u-url mention">@<span>foo</span></a></span>
99 HTML.filter_tags(@html_span_microformats_sample, Pleroma.HTML.Scrubber.TwitterText)
102 test "filters invalid microformats markup" do
104 <span class="h-card"><a>@<span>foo</span></a></span>
109 @html_span_invalid_microformats_sample,
110 Pleroma.HTML.Scrubber.TwitterText
115 describe "default scrubber" do
116 test "normalizes HTML as expected" do
118 <b>this is in bold</b>
119 <p>this is a paragraph</p>
120 this is a linebreak<br/>
121 this is a link with allowed "rel" attribute: <a href="http://example.com/" rel="tag">example.com</a>
122 this is a link with not allowed "rel" attribute: <a href="http://example.com/">example.com</a>
123 this is an image: <img src="http://example.com/image.jpg"/><br/>
124 alert('hacked')
127 assert expected == HTML.filter_tags(@html_sample, Pleroma.HTML.Scrubber.Default)
130 test "does not allow attribute-based XSS" do
132 <img src="http://example.com/image.jpg"/>
135 assert expected == HTML.filter_tags(@html_onerror_sample, Pleroma.HTML.Scrubber.Default)
138 test "does not allow spans with invalid classes" do
143 assert expected == HTML.filter_tags(@html_span_class_sample, Pleroma.HTML.Scrubber.Default)
146 test "does allow microformats" do
148 <span class="h-card"><a class="u-url mention">@<span>foo</span></a></span>
152 HTML.filter_tags(@html_span_microformats_sample, Pleroma.HTML.Scrubber.Default)
155 test "filters invalid microformats markup" do
157 <span class="h-card"><a>@<span>foo</span></a></span>
162 @html_span_invalid_microformats_sample,
163 Pleroma.HTML.Scrubber.Default
168 describe "extract_first_external_url_from_object" do
169 test "extracts the url" do
173 CommonAPI.post(user, %{
175 "I think I just found the best github repo https://github.com/komeiji-satori/Dress"
178 object = Object.normalize(activity)
179 {:ok, url} = HTML.extract_first_external_url_from_object(object)
180 assert url == "https://github.com/komeiji-satori/Dress"
183 test "skips mentions" do
185 other_user = insert(:user)
188 CommonAPI.post(user, %{
190 "@#{other_user.nickname} install misskey! https://github.com/syuilo/misskey/blob/develop/docs/setup.en.md"
193 object = Object.normalize(activity)
194 {:ok, url} = HTML.extract_first_external_url_from_object(object)
196 assert url == "https://github.com/syuilo/misskey/blob/develop/docs/setup.en.md"
198 refute url == other_user.ap_id
201 test "skips hashtags" do
205 CommonAPI.post(user, %{
206 status: "#cofe https://www.pixiv.net/member_illust.php?mode=medium&illust_id=72255140"
209 object = Object.normalize(activity)
210 {:ok, url} = HTML.extract_first_external_url_from_object(object)
212 assert url == "https://www.pixiv.net/member_illust.php?mode=medium&illust_id=72255140"
215 test "skips microformats hashtags" do
219 CommonAPI.post(user, %{
221 "<a href=\"https://pleroma.gov/tags/cofe\" rel=\"tag\">#cofe</a> https://www.pixiv.net/member_illust.php?mode=medium&illust_id=72255140",
222 content_type: "text/html"
225 object = Object.normalize(activity)
226 {:ok, url} = HTML.extract_first_external_url_from_object(object)
228 assert url == "https://www.pixiv.net/member_illust.php?mode=medium&illust_id=72255140"
231 test "does not crash when there is an HTML entity in a link" do
234 {:ok, activity} = CommonAPI.post(user, %{status: "\"http://cofe.com/?boomer=ok&foo=bar\""})
236 object = Object.normalize(activity)
238 assert {:ok, nil} = HTML.extract_first_external_url_from_object(object)
241 test "skips attachment links" do
245 CommonAPI.post(user, %{
247 "<a href=\"https://pleroma.gov/media/d24caa3a498e21e0298377a9ca0149a4f4f8b767178aacf837542282e2d94fb1.png?name=image.png\" class=\"attachment\">image.png</a>"
250 object = Object.normalize(activity)
252 assert {:ok, nil} = HTML.extract_first_external_url_from_object(object)