1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
5 defmodule Pleroma.HTMLTest do
8 alias Pleroma.Web.CommonAPI
9 use Pleroma.DataCase, async: true
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 this is an inline emoji: <img class="emoji" src="http://example.com/image.jpg"><br />
21 <script>alert('hacked')</script>
24 @html_onerror_sample """
25 <img src="http://example.com/image.jpg" onerror="alert('hacked')">
28 @html_stillimage_sample """
29 <img class="still-image" src="http://example.com/image.jpg">
32 @html_span_class_sample """
33 <span class="animate-spin">hi</span>
36 @html_span_microformats_sample """
37 <span class="h-card"><a class="u-url mention">@<span>foo</span></a></span>
40 @html_span_invalid_microformats_sample """
41 <span class="h-card"><a class="u-url mention animate-spin">@<span>foo</span></a></span>
44 describe "StripTags scrubber" do
45 test "works as expected" do
50 this is a link with allowed "rel" attribute: example.com
51 this is a link with not allowed "rel" attribute: example.com
53 this is an inline emoji:
54 alert('hacked')
57 assert expected == HTML.strip_tags(@html_sample)
60 test "does not allow attribute-based XSS" do
63 assert expected == HTML.strip_tags(@html_onerror_sample)
67 describe "TwitterText scrubber" do
68 test "normalizes HTML as expected" do
71 <p>this is a paragraph</p>
72 this is a linebreak<br/>
73 this is a link with allowed "rel" attribute: <a href="http://example.com/" rel="tag">example.com</a>
74 this is a link with not allowed "rel" attribute: <a href="http://example.com/">example.com</a>
75 this is an image: <img src="http://example.com/image.jpg"/><br/>
76 this is an inline emoji: <img class="emoji" src="http://example.com/image.jpg"/><br/>
77 alert('hacked')
80 assert expected == HTML.filter_tags(@html_sample, Pleroma.HTML.Scrubber.TwitterText)
83 test "does not allow attribute-based XSS" do
85 <img src="http://example.com/image.jpg"/>
88 assert expected == HTML.filter_tags(@html_onerror_sample, Pleroma.HTML.Scrubber.TwitterText)
91 test "does not allow spans with invalid classes" do
97 HTML.filter_tags(@html_span_class_sample, Pleroma.HTML.Scrubber.TwitterText)
100 test "does not allow images with invalid classes" do
102 <img src="http://example.com/image.jpg"/>
106 HTML.filter_tags(@html_stillimage_sample, Pleroma.HTML.Scrubber.TwitterText)
109 test "does allow microformats" do
111 <span class="h-card"><a class="u-url mention">@<span>foo</span></a></span>
115 HTML.filter_tags(@html_span_microformats_sample, Pleroma.HTML.Scrubber.TwitterText)
118 test "filters invalid microformats markup" do
120 <span class="h-card"><a>@<span>foo</span></a></span>
125 @html_span_invalid_microformats_sample,
126 Pleroma.HTML.Scrubber.TwitterText
131 describe "default scrubber" do
132 test "normalizes HTML as expected" do
134 <b>this is in bold</b>
135 <p>this is a paragraph</p>
136 this is a linebreak<br/>
137 this is a link with allowed "rel" attribute: <a href="http://example.com/" rel="tag">example.com</a>
138 this is a link with not allowed "rel" attribute: <a href="http://example.com/">example.com</a>
139 this is an image: <img src="http://example.com/image.jpg"/><br/>
140 this is an inline emoji: <img class="emoji" src="http://example.com/image.jpg"/><br/>
141 alert('hacked')
144 assert expected == HTML.filter_tags(@html_sample, Pleroma.HTML.Scrubber.Default)
147 test "does not allow attribute-based XSS" do
149 <img src="http://example.com/image.jpg"/>
152 assert expected == HTML.filter_tags(@html_onerror_sample, Pleroma.HTML.Scrubber.Default)
155 test "does not allow spans with invalid classes" do
160 assert expected == HTML.filter_tags(@html_span_class_sample, Pleroma.HTML.Scrubber.Default)
163 test "does not allow images with invalid classes" do
165 <img src="http://example.com/image.jpg"/>
169 HTML.filter_tags(@html_stillimage_sample, Pleroma.HTML.Scrubber.TwitterText)
172 test "does allow microformats" do
174 <span class="h-card"><a class="u-url mention">@<span>foo</span></a></span>
178 HTML.filter_tags(@html_span_microformats_sample, Pleroma.HTML.Scrubber.Default)
181 test "filters invalid microformats markup" do
183 <span class="h-card"><a>@<span>foo</span></a></span>
188 @html_span_invalid_microformats_sample,
189 Pleroma.HTML.Scrubber.Default
194 describe "extract_first_external_url_from_object" do
195 test "extracts the url" do
199 CommonAPI.post(user, %{
201 "I think I just found the best github repo https://github.com/komeiji-satori/Dress"
204 object = Object.normalize(activity, fetch: false)
205 url = HTML.extract_first_external_url_from_object(object)
206 assert url == "https://github.com/komeiji-satori/Dress"
209 test "skips mentions" do
211 other_user = insert(:user)
214 CommonAPI.post(user, %{
216 "@#{other_user.nickname} install misskey! https://github.com/syuilo/misskey/blob/develop/docs/setup.en.md"
219 object = Object.normalize(activity, fetch: false)
220 url = HTML.extract_first_external_url_from_object(object)
222 assert url == "https://github.com/syuilo/misskey/blob/develop/docs/setup.en.md"
224 refute url == other_user.ap_id
227 test "skips hashtags" do
231 CommonAPI.post(user, %{
232 status: "#cofe https://www.pixiv.net/member_illust.php?mode=medium&illust_id=72255140"
235 object = Object.normalize(activity, fetch: false)
236 url = HTML.extract_first_external_url_from_object(object)
238 assert url == "https://www.pixiv.net/member_illust.php?mode=medium&illust_id=72255140"
241 test "skips microformats hashtags" do
245 CommonAPI.post(user, %{
247 "<a href=\"https://pleroma.gov/tags/cofe\" rel=\"tag\">#cofe</a> https://www.pixiv.net/member_illust.php?mode=medium&illust_id=72255140",
248 content_type: "text/html"
251 object = Object.normalize(activity, fetch: false)
252 url = HTML.extract_first_external_url_from_object(object)
254 assert url == "https://www.pixiv.net/member_illust.php?mode=medium&illust_id=72255140"
257 test "does not crash when there is an HTML entity in a link" do
260 {:ok, activity} = CommonAPI.post(user, %{status: "\"http://cofe.com/?boomer=ok&foo=bar\""})
262 object = Object.normalize(activity, fetch: false)
264 assert nil == HTML.extract_first_external_url_from_object(object)
267 test "skips attachment links" do
271 CommonAPI.post(user, %{
273 "<a href=\"https://pleroma.gov/media/d24caa3a498e21e0298377a9ca0149a4f4f8b767178aacf837542282e2d94fb1.png?name=image.png\" class=\"attachment\">image.png</a>"
276 object = Object.normalize(activity, fetch: false)
278 assert nil == HTML.extract_first_external_url_from_object(object)