First
[anni] / lib / pleroma / html.ex
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
4
5 defmodule Pleroma.HTML do
6   # Scrubbers are compiled on boot so they can be configured in OTP releases
7   #  @on_load :compile_scrubbers
8
9   @cachex Pleroma.Config.get([:cachex, :provider], Cachex)
10
11   def compile_scrubbers do
12     dir = Path.join(:code.priv_dir(:pleroma), "scrubbers")
13
14     dir
15     |> Pleroma.Utils.compile_dir()
16     |> case do
17       {:error, _errors, _warnings} ->
18         raise "Compiling scrubbers failed"
19
20       {:ok, _modules, _warnings} ->
21         :ok
22     end
23   end
24
25   defp get_scrubbers(scrubber) when is_atom(scrubber), do: [scrubber]
26   defp get_scrubbers(scrubbers) when is_list(scrubbers), do: scrubbers
27   defp get_scrubbers(_), do: [Pleroma.HTML.Scrubber.Default]
28
29   def get_scrubbers do
30     Pleroma.Config.get([:markup, :scrub_policy])
31     |> get_scrubbers
32   end
33
34   def filter_tags(html, nil) do
35     filter_tags(html, get_scrubbers())
36   end
37
38   def filter_tags(html, scrubbers) when is_list(scrubbers) do
39     Enum.reduce(scrubbers, html, fn scrubber, html ->
40       filter_tags(html, scrubber)
41     end)
42   end
43
44   def filter_tags(html, scrubber) do
45     {:ok, content} = FastSanitize.Sanitizer.scrub(html, scrubber)
46     content
47   end
48
49   def filter_tags(html), do: filter_tags(html, nil)
50   def strip_tags(html), do: filter_tags(html, FastSanitize.Sanitizer.StripTags)
51
52   def ensure_scrubbed_html(
53         content,
54         scrubbers,
55         fake,
56         callback
57       ) do
58     content =
59       content
60       |> filter_tags(scrubbers)
61       |> callback.()
62
63     if fake do
64       {:ignore, content}
65     else
66       {:commit, content}
67     end
68   end
69
70   def extract_first_external_url_from_object(%{data: %{"content" => content}} = object)
71       when is_binary(content) do
72     unless object.data["fake"] do
73       key = "URL|#{object.id}"
74
75       @cachex.fetch!(:scrubber_cache, key, fn _key ->
76         {:commit, {:ok, extract_first_external_url(content)}}
77       end)
78     else
79       {:ok, extract_first_external_url(content)}
80     end
81   end
82
83   def extract_first_external_url_from_object(_), do: {:error, :no_content}
84
85   def extract_first_external_url(content) do
86     content
87     |> Floki.parse_fragment!()
88     |> Floki.find("a:not(.mention,.hashtag,.attachment,[rel~=\"tag\"])")
89     |> Enum.take(1)
90     |> Floki.attribute("href")
91     |> Enum.at(0)
92   end
93 end