Merge branch 'audit-mix-audit-report' (#69) into release/2.11
This commit is contained in:
commit
52762f9770
54 changed files with 1316 additions and 106 deletions
|
|
@ -31,7 +31,7 @@ variables:
|
||||||
<<: *docker_variables
|
<<: *docker_variables
|
||||||
build_args:
|
build_args:
|
||||||
- ELIXIR_IMG=docker.io/arm32v7/elixir
|
- ELIXIR_IMG=docker.io/arm32v7/elixir
|
||||||
- ELIXIR_TAG=1.17.3-otp-26-alpine
|
- ELIXIR_TAG=1.17.3-otp-27-alpine
|
||||||
- ALPINE_IMG=docker.io/arm32v7/alpine
|
- ALPINE_IMG=docker.io/arm32v7/alpine
|
||||||
- ALPINE_VER=3.20
|
- ALPINE_VER=3.20
|
||||||
extra_opts: --custom-platform=linux/arm/v7
|
extra_opts: --custom-platform=linux/arm/v7
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ variables:
|
||||||
steps:
|
steps:
|
||||||
mix-format:
|
mix-format:
|
||||||
image: &elixir-image
|
image: &elixir-image
|
||||||
docker.io/elixir:1.15-alpine
|
docker.io/elixir:1.17-otp-27-alpine
|
||||||
entrypoint: *script_file_entrypoint
|
entrypoint: *script_file_entrypoint
|
||||||
failure: ignore
|
failure: ignore
|
||||||
commands:
|
commands:
|
||||||
|
|
|
||||||
|
|
@ -19,8 +19,8 @@ variables:
|
||||||
- 'printf "%s" "$CI_SCRIPT" | base64 -d > /tmp/ci-script.sh && /bin/sh -xe /tmp/ci-script.sh'
|
- 'printf "%s" "$CI_SCRIPT" | base64 -d > /tmp/ci-script.sh && /bin/sh -xe /tmp/ci-script.sh'
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
unit-testing-elixir-1.15:
|
unit-testing-elixir-1.17:
|
||||||
image: elixir:1.15-alpine
|
image: elixir:1.17-otp-27-alpine
|
||||||
entrypoint: *script_file_entrypoint
|
entrypoint: *script_file_entrypoint
|
||||||
environment:
|
environment:
|
||||||
MIX_ENV: test
|
MIX_ENV: test
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
# https://hub.docker.com/r/hexpm/elixir/tags
|
# https://hub.docker.com/r/hexpm/elixir/tags
|
||||||
ARG ELIXIR_IMG=hexpm/elixir
|
ARG ELIXIR_IMG=hexpm/elixir
|
||||||
ARG ELIXIR_VER=1.17.3
|
ARG ELIXIR_VER=1.17.3
|
||||||
ARG ERLANG_VER=27.3.4.14
|
ARG ERLANG_VER=27.3.4.17
|
||||||
ARG ALPINE_VER=3.23.5
|
ARG ALPINE_VER=3.23.5
|
||||||
ARG ELIXIR_TAG=${ELIXIR_VER}-erlang-${ERLANG_VER}-alpine-${ALPINE_VER}
|
ARG ELIXIR_TAG=${ELIXIR_VER}-erlang-${ERLANG_VER}-alpine-${ALPINE_VER}
|
||||||
ARG ALPINE_IMG=alpine
|
ARG ALPINE_IMG=alpine
|
||||||
|
|
|
||||||
1
changelog.d/bandit-1.11.1.security
Normal file
1
changelog.d/bandit-1.11.1.security
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Update Bandit to address HTTP request handling vulnerabilities.
|
||||||
1
changelog.d/bandit-1.12.4.security
Normal file
1
changelog.d/bandit-1.12.4.security
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Update bandit to version 1.12.4 to fix a DoS vulnerability
|
||||||
1
changelog.d/bandit-1.12.5.security
Normal file
1
changelog.d/bandit-1.12.5.security
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Update bandit to version 1.12.5 to fix an HTTP/2 DoS vulnerability
|
||||||
1
changelog.d/bandit-1.13.1.security
Normal file
1
changelog.d/bandit-1.13.1.security
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Update bandit to version 1.13.1 resolving a stuck process and unvalidated HTTP/1 headers vulnerabilities
|
||||||
1
changelog.d/castore-1.0.22.security
Normal file
1
changelog.d/castore-1.0.22.security
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Updated CAStore to version 1.0.22 with updated Mozilla certificate bundle after eMudhra Intermediate CA Compromise
|
||||||
1
changelog.d/cowboy-2.16.1.security
Normal file
1
changelog.d/cowboy-2.16.1.security
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Update Cowboy to address multipart header parsing denial-of-service vulnerabilities.
|
||||||
1
changelog.d/cowboy-2.19.0.security
Normal file
1
changelog.d/cowboy-2.19.0.security
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Update cowboy to version 2.19.0 which fixes a memory exhaustion DoS
|
||||||
1
changelog.d/cowlib-2.17.1.security
Normal file
1
changelog.d/cowlib-2.17.1.security
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Update Cowlib to address HTTP parsing resource exhaustion and injection vulnerabilities.
|
||||||
1
changelog.d/cowlib-2.20.0.security
Normal file
1
changelog.d/cowlib-2.20.0.security
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Update cowlib to version 2.20.0 which fixes HTTP splitting and header smuggling/injection vulnerabilities
|
||||||
1
changelog.d/cowlib-2.21.0.security
Normal file
1
changelog.d/cowlib-2.21.0.security
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Update gun to version 2.7.0 and cowlib to version 2.21.0 resolving a header smuggling vulnerability
|
||||||
1
changelog.d/decimal-3.1.1.security
Normal file
1
changelog.d/decimal-3.1.1.security
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Update Decimal and compatible dependencies to address a rounding vulnerability.
|
||||||
1
changelog.d/decimal-3.2.0.security
Normal file
1
changelog.d/decimal-3.2.0.security
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Update Decimal to version 3.2.0 resolving an unbounded allocation vulnerability
|
||||||
1
changelog.d/earmark-mdex.security
Normal file
1
changelog.d/earmark-mdex.security
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Replace retired Earmark rendering with the maintained EarmarkParser and an attribute-escaping renderer.
|
||||||
1
changelog.d/gun-2.6.0.security
Normal file
1
changelog.d/gun-2.6.0.security
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Update gun to version 2.6.0 which fixes an HTTP request/response smuggling vulnerability
|
||||||
1
changelog.d/hackney-4.0.3.security
Normal file
1
changelog.d/hackney-4.0.3.security
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Update Hackney and its HTTPoison integration to address SSRF, timeout, and injection vulnerabilities.
|
||||||
1
changelog.d/hackney-conn-leak.fix
Normal file
1
changelog.d/hackney-conn-leak.fix
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Stop leaking a Hackney connection process, and the response bytes it read, for every unpooled request that ends without a reusable socket
|
||||||
1
changelog.d/jason-1.4.5.change
Normal file
1
changelog.d/jason-1.4.5.change
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Update Jason to support Decimal 3 dependency resolution.
|
||||||
1
changelog.d/max-body-hackney.fix
Normal file
1
changelog.d/max-body-hackney.fix
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Enforce the max_body HTTP option again, which Hackney 4 silently stopped honouring because it always returns a fully read body
|
||||||
1
changelog.d/mint-1.10.0.security
Normal file
1
changelog.d/mint-1.10.0.security
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Update mint to version 1.10.0 which fixes multiple DoS vulnerabilities
|
||||||
1
changelog.d/mint-1.11.0.security
Normal file
1
changelog.d/mint-1.11.0.security
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Update mint to version 1.11.0 resolving multiple vulnerabilities
|
||||||
1
changelog.d/mix-audit.add
Normal file
1
changelog.d/mix-audit.add
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Add mix_audit for dependency vulnerability checks.
|
||||||
1
changelog.d/phoenix-1.8.11.security
Normal file
1
changelog.d/phoenix-1.8.11.security
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Update Phoenix to version 1.8.11 resolving a process-exhaustion DoS vulnerability
|
||||||
1
changelog.d/phoenix-1.8.8.security
Normal file
1
changelog.d/phoenix-1.8.8.security
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Update Phoenix to address long-poll NDJSON request denial-of-service vulnerabilities.
|
||||||
1
changelog.d/phoenix_live_dashboard-1.2.9.security
Normal file
1
changelog.d/phoenix_live_dashboard-1.2.9.security
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Update Phoenix LiveView to version 1.2.9 to fix an open redirect vulnerability
|
||||||
1
changelog.d/plug-1.20.2.security
Normal file
1
changelog.d/plug-1.20.2.security
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Update Plug to address multipart header parsing denial-of-service vulnerabilities.
|
||||||
1
changelog.d/plug-cowboy-2.9.0.security
Normal file
1
changelog.d/plug-cowboy-2.9.0.security
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Update Plug.Cowboy to address HTTP/2 scheme atom exhaustion denial-of-service vulnerabilities.
|
||||||
1
changelog.d/postgrex-0.22.2.security
Normal file
1
changelog.d/postgrex-0.22.2.security
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Update Postgrex to address notification channel-name SQL injection vulnerabilities.
|
||||||
1
changelog.d/postgrex-0.22.4.security
Normal file
1
changelog.d/postgrex-0.22.4.security
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Update postgrex to version 0.22.4 to fix an SQL injection vulnerability
|
||||||
1
changelog.d/quic-unused.security
Normal file
1
changelog.d/quic-unused.security
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Remove unused QUIC lockfile entry with vulnerable TLS verification behavior.
|
||||||
1
changelog.d/reverse-proxy-hackney-leak.fix
Normal file
1
changelog.d/reverse-proxy-hackney-leak.fix
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Stop leaking a Hackney connection process for every media proxy response that was streamed to the end
|
||||||
1
changelog.d/swoosh-1.28.0.security
Normal file
1
changelog.d/swoosh-1.28.0.security
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Update swoosh to version 1.28.0 which fixes a url injection vulnerability for the Microsoft Graph adapter
|
||||||
|
|
@ -3,12 +3,15 @@
|
||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Formatter do
|
defmodule Pleroma.Formatter do
|
||||||
|
require Logger
|
||||||
|
|
||||||
alias Pleroma.HTML
|
alias Pleroma.HTML
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
|
|
||||||
@safe_mention_regex ~r/^(\s*(?<mentions>(@.+?\s+){1,})+)(?<rest>.*)/s
|
@safe_mention_regex ~r/^(\s*(?<mentions>(@.+?\s+){1,})+)(?<rest>.*)/s
|
||||||
@link_regex ~r"((?:http(s)?:\/\/)?[\w.-]+(?:\.[\w\.-]+)+[\w\-\._~%:/?#[\]@!\$&'\(\)\*\+,;=.]+)|[0-9a-z+\-\.]+:[0-9a-z$-_.+!*'(),]+"ui
|
@link_regex ~r"((?:http(s)?:\/\/)?[\w.-]+(?:\.[\w\.-]+)+[\w\-\._~%:/?#[\]@!\$&'\(\)\*\+,;=.]+)|[0-9a-z+\-\.]+:[0-9a-z$-_.+!*'(),]+"ui
|
||||||
@markdown_characters_regex ~r/(`|\*|_|{|}|[|]|\(|\)|#|\+|-|\.|!)/
|
@markdown_characters_regex ~r/(`|\*|_|{|}|[|]|\(|\)|#|\+|-|\.|!)/
|
||||||
|
@markdown_void_elements ~w[area base br col command embed hr img input keygen link meta param source track wbr]
|
||||||
|
|
||||||
defp linkify_opts do
|
defp linkify_opts do
|
||||||
Pleroma.Config.get(Pleroma.Formatter) ++
|
Pleroma.Config.get(Pleroma.Formatter) ++
|
||||||
|
|
@ -124,14 +127,152 @@ defmodule Pleroma.Formatter do
|
||||||
end
|
end
|
||||||
|
|
||||||
def markdown_to_html(text) do
|
def markdown_to_html(text) do
|
||||||
Earmark.as_html!(text, %Earmark.Options{compact_output: true, smartypants: false})
|
markdown_to_html(text, %{})
|
||||||
end
|
end
|
||||||
|
|
||||||
def markdown_to_html(text, opts) do
|
def markdown_to_html(text, opts) do
|
||||||
Earmark.as_html!(
|
breaks = Map.get(opts, :breaks, false)
|
||||||
text,
|
|
||||||
%Earmark.Options{compact_output: true, smartypants: false} |> Map.merge(opts)
|
{_status, ast, _messages} = EarmarkParser.as_ast(text, breaks: breaks, gfm: true)
|
||||||
)
|
|
||||||
|
ast
|
||||||
|
|> maybe_strip_email_autolinks(text, Map.get(opts, :strip_email_autolinks, false))
|
||||||
|
|> render_markdown_nodes(false)
|
||||||
|
|> IO.iodata_to_binary()
|
||||||
|
rescue
|
||||||
|
exception ->
|
||||||
|
Logger.warning("Markdown rendering failed in #{inspect(exception.__struct__)}")
|
||||||
|
markdown_fallback(text, Map.get(opts, :breaks, false))
|
||||||
|
end
|
||||||
|
|
||||||
|
defp markdown_fallback(text, breaks) do
|
||||||
|
text = text |> escape_html() |> IO.iodata_to_binary() |> maybe_render_markdown_breaks(breaks)
|
||||||
|
"<p>" <> text <> "</p>"
|
||||||
|
end
|
||||||
|
|
||||||
|
defp maybe_render_markdown_breaks(text, true), do: String.replace(text, ~r/\r?\n/, "<br>")
|
||||||
|
defp maybe_render_markdown_breaks(text, false), do: text
|
||||||
|
|
||||||
|
defp maybe_strip_email_autolinks(ast, _text, false), do: ast
|
||||||
|
|
||||||
|
defp maybe_strip_email_autolinks(ast, text, true) do
|
||||||
|
emails =
|
||||||
|
~r/<([^ >]+@[^ >]+)>/
|
||||||
|
|> Regex.scan(text, capture: :all_but_first)
|
||||||
|
|> Enum.map(fn [email] -> String.replace_prefix(email, "mailto:", "") end)
|
||||||
|
|> MapSet.new()
|
||||||
|
|
||||||
|
strip_email_autolinks(ast, emails)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp strip_email_autolinks(nodes, emails) when is_list(nodes) do
|
||||||
|
Enum.map(nodes, &strip_email_autolinks(&1, emails))
|
||||||
|
end
|
||||||
|
|
||||||
|
defp strip_email_autolinks({"a", attrs, [email], _meta} = node, emails) do
|
||||||
|
if List.keyfind(attrs, "href", 0) == {"href", "mailto:" <> email} and
|
||||||
|
MapSet.member?(emails, email) do
|
||||||
|
email
|
||||||
|
else
|
||||||
|
node
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp strip_email_autolinks({tag, attrs, children, meta}, emails) do
|
||||||
|
{tag, attrs, strip_email_autolinks(children, emails), meta}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp strip_email_autolinks(text, _emails) when is_binary(text), do: text
|
||||||
|
|
||||||
|
defp render_markdown_nodes(nodes, verbatim) when is_list(nodes) do
|
||||||
|
Enum.map(nodes, &render_markdown_node(&1, verbatim))
|
||||||
|
end
|
||||||
|
|
||||||
|
defp render_markdown_node(text, true) when is_binary(text),
|
||||||
|
do: strip_dangerous_markdown_html(text)
|
||||||
|
|
||||||
|
defp render_markdown_node(text, false) when is_binary(text) do
|
||||||
|
text
|
||||||
|
|> strip_dangerous_markdown_html()
|
||||||
|
|> escape_markdown_html()
|
||||||
|
end
|
||||||
|
|
||||||
|
defp render_markdown_node({:comment, _attrs, _children, _meta}, _verbatim), do: []
|
||||||
|
|
||||||
|
defp render_markdown_node({tag, attrs, children, meta}, verbatim) do
|
||||||
|
normalized_tag = String.downcase(tag)
|
||||||
|
|
||||||
|
if normalized_tag in ["script", "style"] do
|
||||||
|
[]
|
||||||
|
else
|
||||||
|
render_markdown_element(tag, normalized_tag, attrs, children, meta, verbatim)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp render_markdown_element("code", _normalized_tag, attrs, children, _meta, _verbatim) do
|
||||||
|
["<code", render_markdown_attrs(attrs), ">", render_markdown_code(children), "</code>"]
|
||||||
|
end
|
||||||
|
|
||||||
|
defp render_markdown_element(tag, "hr", attrs, _children, _meta, _verbatim) do
|
||||||
|
attrs = Enum.reject(attrs, &match?({"class", class} when class in ~w[thin medium thick], &1))
|
||||||
|
["<", tag, render_markdown_attrs(attrs), "/>"]
|
||||||
|
end
|
||||||
|
|
||||||
|
defp render_markdown_element(tag, normalized_tag, attrs, _children, _meta, _verbatim)
|
||||||
|
when normalized_tag in @markdown_void_elements do
|
||||||
|
["<", tag, render_markdown_attrs(attrs), ">"]
|
||||||
|
end
|
||||||
|
|
||||||
|
defp render_markdown_element(tag, _normalized_tag, attrs, children, meta, verbatim) do
|
||||||
|
children_verbatim = verbatim or Map.get(meta, :verbatim, false)
|
||||||
|
|
||||||
|
[
|
||||||
|
"<",
|
||||||
|
tag,
|
||||||
|
render_markdown_attrs(attrs),
|
||||||
|
">",
|
||||||
|
render_markdown_nodes(children, children_verbatim),
|
||||||
|
"</",
|
||||||
|
tag,
|
||||||
|
">"
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
|
defp render_markdown_attrs(attrs) do
|
||||||
|
Enum.map(attrs, fn {name, value} ->
|
||||||
|
value = value |> to_string() |> HtmlEntities.decode() |> sanitize_markdown_url(name)
|
||||||
|
[" ", to_string(name), "=\"", escape_html(value), "\""]
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp render_markdown_code(children) do
|
||||||
|
Enum.map(children, &escape_html/1)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp sanitize_markdown_url(value, name) do
|
||||||
|
if String.downcase(to_string(name)) in ["href", "src"] do
|
||||||
|
value
|
||||||
|
|> String.split(~r/["<>]/, parts: 2)
|
||||||
|
|> hd()
|
||||||
|
else
|
||||||
|
value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp strip_dangerous_markdown_html(text) do
|
||||||
|
text
|
||||||
|
|> String.replace(~r/<(script|style)\b[^>]*>.*?<\/\1>/is, "")
|
||||||
|
|> String.replace(~r/<(script|style)\b[^>]*>.*\z/is, "")
|
||||||
|
|> String.replace(~r/<\/(script|style)\s*>/i, "")
|
||||||
|
end
|
||||||
|
|
||||||
|
defp escape_markdown_html(text) do
|
||||||
|
text |> HtmlEntities.decode() |> escape_html()
|
||||||
|
end
|
||||||
|
|
||||||
|
defp escape_html(text) do
|
||||||
|
{:safe, escaped} = Phoenix.HTML.html_escape(text)
|
||||||
|
escaped
|
||||||
end
|
end
|
||||||
|
|
||||||
def html_escape({text, mentions, hashtags}, type) do
|
def html_escape({text, mentions, hashtags}, type) do
|
||||||
|
|
|
||||||
|
|
@ -130,5 +130,9 @@ defmodule Pleroma.HTTP do
|
||||||
end
|
end
|
||||||
|
|
||||||
defp default_middleware,
|
defp default_middleware,
|
||||||
do: [Tesla.Middleware.FollowRedirects, Pleroma.Tesla.Middleware.EncodeUrl]
|
do: [
|
||||||
|
Tesla.Middleware.FollowRedirects,
|
||||||
|
Pleroma.Tesla.Middleware.EncodeUrl,
|
||||||
|
Pleroma.Tesla.Middleware.MaxBody
|
||||||
|
]
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,8 @@ defmodule Pleroma.HTTP.AdapterHelper.Hackney do
|
||||||
|> add_scheme_opts(uri)
|
|> add_scheme_opts(uri)
|
||||||
|> maybe_add_with_body()
|
|> maybe_add_with_body()
|
||||||
|> Pleroma.HTTP.AdapterHelper.maybe_add_proxy(proxy)
|
|> Pleroma.HTTP.AdapterHelper.maybe_add_proxy(proxy)
|
||||||
|
|> Keyword.put(:follow_redirect, false)
|
||||||
|
|> Keyword.put(:force_redirect, false)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp add_scheme_opts(opts, _), do: opts
|
defp add_scheme_opts(opts, _), do: opts
|
||||||
|
|
|
||||||
|
|
@ -112,7 +112,13 @@ defmodule Pleroma.ReverseProxy do
|
||||||
def call(_conn, _url, _opts \\ [])
|
def call(_conn, _url, _opts \\ [])
|
||||||
|
|
||||||
def call(conn = %{method: method}, url, opts) when method in @methods do
|
def call(conn = %{method: method}, url, opts) when method in @methods do
|
||||||
client_opts = Keyword.merge(@default_options, Keyword.get(opts, :http, []))
|
client_opts =
|
||||||
|
@default_options
|
||||||
|
|> Keyword.merge(Keyword.get(opts, :http, []))
|
||||||
|
|> Keyword.put_new(
|
||||||
|
:recv_timeout,
|
||||||
|
receive_timeout(opts)
|
||||||
|
)
|
||||||
|
|
||||||
req_headers = build_req_headers(conn.req_headers, opts)
|
req_headers = build_req_headers(conn.req_headers, opts)
|
||||||
|
|
||||||
|
|
@ -506,7 +512,16 @@ defmodule Pleroma.ReverseProxy do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp check_read_duration(_, _), do: {:ok, :no_duration_limit, :no_duration_limit}
|
defp check_read_duration(_, _), do: {:ok, :no_duration_limit}
|
||||||
|
|
||||||
|
defp receive_timeout(opts) do
|
||||||
|
case Keyword.get(opts, :max_read_duration, @max_read_duration) do
|
||||||
|
timeout when is_integer(timeout) and timeout > 0 -> timeout
|
||||||
|
_ -> :infinity
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp increase_read_duration(:no_duration_limit), do: {:ok, :no_duration_limit}
|
||||||
|
|
||||||
defp increase_read_duration({previous_duration, started})
|
defp increase_read_duration({previous_duration, started})
|
||||||
when is_integer(previous_duration) and is_integer(started) do
|
when is_integer(previous_duration) and is_integer(started) do
|
||||||
|
|
|
||||||
|
|
@ -35,10 +35,11 @@ defmodule Pleroma.ReverseProxy.Client.Hackney do
|
||||||
if opts[:follow_redirect] != false do
|
if opts[:follow_redirect] != false do
|
||||||
{_state, req_opts} = Access.get_and_update(opts, :follow_redirect, fn a -> {a, false} end)
|
{_state, req_opts} = Access.get_and_update(opts, :follow_redirect, fn a -> {a, false} end)
|
||||||
env = %{method: method, headers: headers, body: body, req_opts: req_opts}
|
env = %{method: method, headers: headers, body: body, req_opts: req_opts}
|
||||||
res = :hackney.request(method, url, headers, body, req_opts)
|
res = do_request(method, url, headers, body, req_opts)
|
||||||
|
|
||||||
case res do
|
case res do
|
||||||
{:ok, code, resp_headers, _client} when code in @redirect_statuses ->
|
{:ok, code, resp_headers, client} when code in @redirect_statuses ->
|
||||||
|
close(client)
|
||||||
redirect(url, resp_headers, env, @redirect_limit)
|
redirect(url, resp_headers, env, @redirect_limit)
|
||||||
|
|
||||||
{:ok, code, resp_headers} when code in @redirect_statuses ->
|
{:ok, code, resp_headers} when code in @redirect_statuses ->
|
||||||
|
|
@ -48,11 +49,34 @@ defmodule Pleroma.ReverseProxy.Client.Hackney do
|
||||||
res
|
res
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
:hackney.request(method, url, headers, body, opts)
|
do_request(method, url, headers, body, opts)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
|
def stream_body(%{released: true}), do: :done
|
||||||
|
|
||||||
|
def stream_body(%{ref: ref, recv_timeout: recv_timeout} = client) do
|
||||||
|
_ = :hackney.stream_next(ref)
|
||||||
|
|
||||||
|
case receive_response(ref, recv_timeout) do
|
||||||
|
{:ok, data} when is_binary(data) ->
|
||||||
|
{:ok, data, client}
|
||||||
|
|
||||||
|
# Reaching the end of the body does not free the connection: Hackney parks
|
||||||
|
# a finished conn instead of stopping it, and an unpooled one has neither
|
||||||
|
# an idle timeout nor a live owner monitor to reap it, so it would sit
|
||||||
|
# there forever pinning every binary it read. Release it here, mirroring
|
||||||
|
# what the gun client does, so callers only close what they abandon.
|
||||||
|
{:ok, :done} ->
|
||||||
|
close(client)
|
||||||
|
:done
|
||||||
|
|
||||||
|
{:error, error} ->
|
||||||
|
{:error, error}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def stream_body(ref) do
|
def stream_body(ref) do
|
||||||
case :hackney.stream_body(ref) do
|
case :hackney.stream_body(ref) do
|
||||||
:done -> :done
|
:done -> :done
|
||||||
|
|
@ -62,8 +86,98 @@ defmodule Pleroma.ReverseProxy.Client.Hackney do
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
|
def close(%{released: true}), do: :ok
|
||||||
|
|
||||||
|
def close(%{ref: ref}), do: :hackney.close(ref)
|
||||||
|
|
||||||
def close(ref), do: :hackney.close(ref)
|
def close(ref), do: :hackney.close(ref)
|
||||||
|
|
||||||
|
defp do_request(method, url, headers, body, opts) do
|
||||||
|
recv_timeout = Keyword.get(opts, :recv_timeout, :infinity)
|
||||||
|
|
||||||
|
req_opts =
|
||||||
|
opts
|
||||||
|
|> Keyword.put(:async, :once)
|
||||||
|
|> Keyword.put(:stream_to, self())
|
||||||
|
|
||||||
|
with {:ok, ref} <- :hackney.request(method, url, headers, body, req_opts) do
|
||||||
|
result =
|
||||||
|
with {:ok, {:status, code, _reason}} <- receive_response(ref, recv_timeout),
|
||||||
|
{:ok, {:headers, resp_headers}} <- receive_response(ref, recv_timeout) do
|
||||||
|
{:ok, code, resp_headers, build_client(ref, recv_timeout, method, code, opts)}
|
||||||
|
end
|
||||||
|
|
||||||
|
case result do
|
||||||
|
{:ok, _, _, _} = response ->
|
||||||
|
response
|
||||||
|
|
||||||
|
error ->
|
||||||
|
close(ref)
|
||||||
|
flush_responses(ref)
|
||||||
|
error
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# A response that cannot carry a body (204, 304, or a reply to HEAD) is
|
||||||
|
# complete as soon as the headers are parsed. Hackney queues `done` for us and,
|
||||||
|
# if the connection is pooled, checks it straight back in, so the ref stops
|
||||||
|
# being ours at that point: pulling the stream or stopping the connection
|
||||||
|
# afterwards would disturb whichever request checks it out next. Mark it so
|
||||||
|
# `stream_body/1` and `close/1` leave it alone.
|
||||||
|
#
|
||||||
|
# This relies on hackney never running its own redirect handling for us --
|
||||||
|
# `request/5` forces `follow_redirect: false` -- otherwise a redirect status
|
||||||
|
# would be checked back in too and would need the same treatment.
|
||||||
|
defp build_client(ref, recv_timeout, method, code, opts) do
|
||||||
|
client = %{ref: ref, recv_timeout: recv_timeout}
|
||||||
|
|
||||||
|
if released?(method, code) and pooled?(opts) do
|
||||||
|
flush_responses(ref)
|
||||||
|
Map.put(client, :released, true)
|
||||||
|
else
|
||||||
|
client
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp released?(method, _code) when method in [:head, :HEAD, "head", "HEAD"], do: true
|
||||||
|
defp released?(_method, code) when code in [204, 304], do: true
|
||||||
|
defp released?(_method, _code), do: false
|
||||||
|
|
||||||
|
# Mirrors hackney's own pooling decision: :hackney.use_pool/1, plus the fact
|
||||||
|
# that a proxy tunnel always bypasses the pool. An unpooled connection is never
|
||||||
|
# checked in, and parks in `connected` with no idle timeout, so it leaks
|
||||||
|
# forever unless we stop it ourselves.
|
||||||
|
defp pooled?(opts) do
|
||||||
|
no_proxy?(Keyword.get(opts, :proxy)) and use_pool?(Keyword.get(opts, :pool))
|
||||||
|
end
|
||||||
|
|
||||||
|
# Hackney reads `false` as "no proxy", same as an absent option.
|
||||||
|
defp no_proxy?(nil), do: true
|
||||||
|
defp no_proxy?(false), do: true
|
||||||
|
defp no_proxy?(_proxy), do: false
|
||||||
|
|
||||||
|
defp use_pool?(false), do: false
|
||||||
|
defp use_pool?(nil), do: Application.get_env(:hackney, :use_default_pool, true)
|
||||||
|
defp use_pool?(_pool_name), do: true
|
||||||
|
|
||||||
|
defp receive_response(ref, recv_timeout) do
|
||||||
|
receive do
|
||||||
|
{:hackney_response, ^ref, {:error, error}} -> {:error, error}
|
||||||
|
{:hackney_response, ^ref, response} -> {:ok, response}
|
||||||
|
after
|
||||||
|
recv_timeout -> {:error, :timeout}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp flush_responses(ref) do
|
||||||
|
receive do
|
||||||
|
{:hackney_response, ^ref, _response} -> flush_responses(ref)
|
||||||
|
after
|
||||||
|
0 -> :ok
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
defp redirect(url, resp_headers, env, limit) when limit == 0 do
|
defp redirect(url, resp_headers, env, limit) when limit == 0 do
|
||||||
new_url = absolute_redirect_url(url, resp_headers)
|
new_url = absolute_redirect_url(url, resp_headers)
|
||||||
|
|
||||||
|
|
@ -71,17 +185,18 @@ defmodule Pleroma.ReverseProxy.Client.Hackney do
|
||||||
"#{__MODULE__}: Handling redirect #{url} -> #{new_url}; redirect limit was reached - returning response after final redirect"
|
"#{__MODULE__}: Handling redirect #{url} -> #{new_url}; redirect limit was reached - returning response after final redirect"
|
||||||
)
|
)
|
||||||
|
|
||||||
:hackney.request(env.method, new_url, env.headers, env.body, env.req_opts)
|
do_request(env.method, new_url, env.headers, env.body, env.req_opts)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp redirect(url, resp_headers, env, limit) do
|
defp redirect(url, resp_headers, env, limit) do
|
||||||
new_url = absolute_redirect_url(url, resp_headers)
|
new_url = absolute_redirect_url(url, resp_headers)
|
||||||
Logger.debug("#{__MODULE__}: handling redirect #{url} -> #{new_url}; limit = #{limit}")
|
Logger.debug("#{__MODULE__}: handling redirect #{url} -> #{new_url}; limit = #{limit}")
|
||||||
|
|
||||||
res = :hackney.request(env.method, new_url, env.headers, env.body, env.req_opts)
|
res = do_request(env.method, new_url, env.headers, env.body, env.req_opts)
|
||||||
|
|
||||||
case res do
|
case res do
|
||||||
{:ok, code, new_resp_headers, _client} when code in @redirect_statuses ->
|
{:ok, code, new_resp_headers, client} when code in @redirect_statuses ->
|
||||||
|
close(client)
|
||||||
redirect(new_url, new_resp_headers, env, limit - 1)
|
redirect(new_url, new_resp_headers, env, limit - 1)
|
||||||
|
|
||||||
{:ok, code, new_resp_headers} when code in @redirect_statuses ->
|
{:ok, code, new_resp_headers} when code in @redirect_statuses ->
|
||||||
|
|
|
||||||
72
lib/pleroma/tesla/middleware/max_body.ex
Normal file
72
lib/pleroma/tesla/middleware/max_body.ex
Normal file
|
|
@ -0,0 +1,72 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Tesla.Middleware.MaxBody do
|
||||||
|
@moduledoc """
|
||||||
|
Enforces the `:max_body` adapter option for every adapter.
|
||||||
|
|
||||||
|
`Pleroma.HTTP.Adapter.Gun` applies the limit as it accumulates a `:plain`
|
||||||
|
body, so an oversized response is abandoned mid-transfer. Hackney cannot:
|
||||||
|
since 4.0 it ignores `with_body` and always hands back a fully read binary
|
||||||
|
(`hackney:request/5`), so `Tesla.Adapter.Hackney` never takes the branch that
|
||||||
|
would honour `:max_body`. Without this middleware the option is silently
|
||||||
|
unenforced there.
|
||||||
|
|
||||||
|
The check therefore bounds what callers are handed, and refuses up front when
|
||||||
|
`content-length` already announces too much. It cannot bound peak memory on
|
||||||
|
the hackney path, because the adapter has read the body before any middleware
|
||||||
|
runs.
|
||||||
|
|
||||||
|
Streaming bodies are left alone: neither adapter limits those, and the caller
|
||||||
|
that owns the stream is the one that has to drain and release it.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@behaviour Tesla.Middleware
|
||||||
|
|
||||||
|
@impl Tesla.Middleware
|
||||||
|
def call(%Tesla.Env{opts: opts} = env, next, _opts) do
|
||||||
|
case opts[:adapter][:max_body] do
|
||||||
|
limit when is_integer(limit) and limit >= 0 -> run(env, next, limit)
|
||||||
|
_ -> Tesla.run(env, next)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp run(env, next, limit) do
|
||||||
|
with {:ok, env} <- Tesla.run(env, next),
|
||||||
|
:ok <- check(env, limit) do
|
||||||
|
{:ok, env}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp check(%Tesla.Env{body: body} = env, limit) when is_binary(body) do
|
||||||
|
with :ok <- check_content_length(env, limit) do
|
||||||
|
check_body(body, limit)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# body_as: :chunks and response: :stream hand back a client the caller drains
|
||||||
|
# itself, applying its own limit (Pleroma.Web.RichMedia.Helpers). There is
|
||||||
|
# nothing to measure, and failing on the header alone would strand the pool
|
||||||
|
# lease that only the caller holds a handle to.
|
||||||
|
defp check(_env, _limit), do: :ok
|
||||||
|
|
||||||
|
# A bodyless response may still announce the entity's size.
|
||||||
|
defp check_content_length(%Tesla.Env{method: :head}, _limit), do: :ok
|
||||||
|
|
||||||
|
defp check_content_length(%Tesla.Env{status: status}, _limit) when status in [204, 304],
|
||||||
|
do: :ok
|
||||||
|
|
||||||
|
defp check_content_length(env, limit) do
|
||||||
|
with value when is_binary(value) <- Tesla.get_header(env, "content-length"),
|
||||||
|
{length, ""} <- Integer.parse(value) do
|
||||||
|
if length > limit, do: {:error, :body_too_large}, else: :ok
|
||||||
|
else
|
||||||
|
_ -> :ok
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp check_body(body, limit) do
|
||||||
|
if byte_size(body) > limit, do: {:error, :body_too_large}, else: :ok
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -324,7 +324,7 @@ defmodule Pleroma.Web.CommonAPI.Utils do
|
||||||
|
|
||||||
def format_input(text, "text/x.misskeymarkdown", options) do
|
def format_input(text, "text/x.misskeymarkdown", options) do
|
||||||
text
|
text
|
||||||
|> Formatter.markdown_to_html(%{breaks: true})
|
|> Formatter.markdown_to_html(%{breaks: true, strip_email_autolinks: true})
|
||||||
|> safe_mfm_to_html()
|
|> safe_mfm_to_html()
|
||||||
|> Formatter.linkify(options)
|
|> Formatter.linkify(options)
|
||||||
|> Formatter.html_escape("text/x.misskeymarkdown")
|
|> Formatter.html_escape("text/x.misskeymarkdown")
|
||||||
|
|
|
||||||
25
mix.exs
25
mix.exs
|
|
@ -5,7 +5,7 @@ defmodule Pleroma.Mixfile do
|
||||||
[
|
[
|
||||||
app: :pleroma,
|
app: :pleroma,
|
||||||
version: version("2.10.2"),
|
version: version("2.10.2"),
|
||||||
elixir: "~> 1.15",
|
elixir: "~> 1.17",
|
||||||
elixirc_paths: elixirc_paths(Mix.env()),
|
elixirc_paths: elixirc_paths(Mix.env()),
|
||||||
compilers: Mix.compilers(),
|
compilers: Mix.compilers(),
|
||||||
elixirc_options: [warnings_as_errors: warnings_as_errors(), prune_code_paths: false],
|
elixirc_options: [warnings_as_errors: warnings_as_errors(), prune_code_paths: false],
|
||||||
|
|
@ -130,13 +130,13 @@ defmodule Pleroma.Mixfile do
|
||||||
# Type `mix help deps` for examples and options.
|
# Type `mix help deps` for examples and options.
|
||||||
defp deps do
|
defp deps do
|
||||||
[
|
[
|
||||||
{:phoenix, "~> 1.8.0"},
|
{:phoenix, "~> 1.8"},
|
||||||
{:phoenix_ecto, "~> 4.6"},
|
{:phoenix_ecto, "~> 4.6"},
|
||||||
{:ecto_sql, "~> 3.13"},
|
{:ecto_sql, "~> 3.13"},
|
||||||
{:ecto_enum, "~> 1.4"},
|
{:ecto_enum, "~> 1.4"},
|
||||||
{:postgrex, ">= 0.21.1"},
|
{:postgrex, "~> 0.22"},
|
||||||
{:phoenix_html, "~> 3.3"},
|
{:phoenix_html, "~> 3.3"},
|
||||||
{:phoenix_live_view, "~> 1.1.19"},
|
{:phoenix_live_view, "~> 1.2"},
|
||||||
{:phoenix_live_dashboard, "~> 0.8.7"},
|
{:phoenix_live_dashboard, "~> 0.8.7"},
|
||||||
{:telemetry_metrics, "~> 0.6"},
|
{:telemetry_metrics, "~> 0.6"},
|
||||||
{:telemetry_poller, "~> 1.3"},
|
{:telemetry_poller, "~> 1.3"},
|
||||||
|
|
@ -152,22 +152,24 @@ defmodule Pleroma.Mixfile do
|
||||||
{:html_entities, "~> 0.5", override: true},
|
{:html_entities, "~> 0.5", override: true},
|
||||||
{:calendar, "~> 1.0"},
|
{:calendar, "~> 1.0"},
|
||||||
{:cachex, "~> 3.6"},
|
{:cachex, "~> 3.6"},
|
||||||
{:tesla, "~> 1.15"},
|
{:tesla, "~> 1.20.0"},
|
||||||
{:castore, "~> 1.0"},
|
{:castore, "~> 1.0"},
|
||||||
{:cowlib, "~> 2.17.1"},
|
{:cowlib, "~> 2.20"},
|
||||||
{:gun, "~> 2.4.1"},
|
{:gun, "~> 2.6"},
|
||||||
{:finch, "~> 0.20"},
|
{:finch, "~> 0.20"},
|
||||||
{:jason, "~> 1.4"},
|
{:jason, "~> 1.4"},
|
||||||
{:mogrify, "~> 0.9.3", override: true},
|
{:mogrify, "~> 0.9.3", override: true},
|
||||||
{:ex_aws, "~> 2.1.9"},
|
{:ex_aws, "~> 2.1.9"},
|
||||||
{:ex_aws_s3, "~> 2.5"},
|
{:ex_aws_s3, "~> 2.5"},
|
||||||
{:sweet_xml, "~> 0.7.5"},
|
{:sweet_xml, "~> 0.7.5"},
|
||||||
{:earmark, "1.4.46"},
|
{:earmark_parser, "~> 1.4.46"},
|
||||||
{:bbcode_pleroma, "~> 0.2.0"},
|
{:bbcode_pleroma, "~> 0.2.0"},
|
||||||
|
{:nimble_parsec, "~> 1.4", override: true},
|
||||||
{:pleroma_mfm_parser, "~> 0.2.1"},
|
{:pleroma_mfm_parser, "~> 0.2.1"},
|
||||||
{:cors_plug, "~> 2.0"},
|
{:cors_plug, "~> 2.0"},
|
||||||
{:web_push_encryption, "~> 0.3.1"},
|
{:web_push_encryption, "~> 0.3.1"},
|
||||||
{:swoosh, "~> 1.16.12"},
|
{:httpoison, "~> 3.0", override: true},
|
||||||
|
{:swoosh, "~> 1.28"},
|
||||||
{:phoenix_swoosh, "~> 1.2"},
|
{:phoenix_swoosh, "~> 1.2"},
|
||||||
{:gen_smtp, "~> 0.15"},
|
{:gen_smtp, "~> 0.15"},
|
||||||
{:mua, "~> 0.2.4"},
|
{:mua, "~> 0.2.4"},
|
||||||
|
|
@ -207,7 +209,7 @@ defmodule Pleroma.Mixfile do
|
||||||
{:elixir_make, "~> 0.7.8", override: true},
|
{:elixir_make, "~> 0.7.8", override: true},
|
||||||
{:blurhash, "~> 0.1.0", hex: :rinpatch_blurhash},
|
{:blurhash, "~> 0.1.0", hex: :rinpatch_blurhash},
|
||||||
{:exile, "~> 0.10.0"},
|
{:exile, "~> 0.10.0"},
|
||||||
{:bandit, "~> 1.10"},
|
{:bandit, "~> 1.12"},
|
||||||
{:websock_adapter, "~> 0.5.8"},
|
{:websock_adapter, "~> 0.5.8"},
|
||||||
{:oban_live_dashboard, "~> 0.1.1"},
|
{:oban_live_dashboard, "~> 0.1.1"},
|
||||||
{:multipart, "~> 0.4.0", optional: true},
|
{:multipart, "~> 0.4.0", optional: true},
|
||||||
|
|
@ -219,9 +221,10 @@ defmodule Pleroma.Mixfile do
|
||||||
{:ex_doc, "~> 0.38", only: :dev, runtime: false},
|
{:ex_doc, "~> 0.38", only: :dev, runtime: false},
|
||||||
{:ex_machina, "~> 2.8", only: :test},
|
{:ex_machina, "~> 2.8", only: :test},
|
||||||
{:credo, "~> 1.7", only: [:dev, :test], runtime: false},
|
{:credo, "~> 1.7", only: [:dev, :test], runtime: false},
|
||||||
|
{:mix_audit, "~> 2.1", only: [:dev, :test], runtime: false},
|
||||||
{:mock, "~> 0.3.9", only: :test},
|
{:mock, "~> 0.3.9", only: :test},
|
||||||
{:covertool, "~> 2.0", only: :test},
|
{:covertool, "~> 2.0", only: :test},
|
||||||
{:hackney, "~> 1.20.1", override: true},
|
{:hackney, "~> 4.7.3", override: true},
|
||||||
{:mox, "~> 1.2", only: :test},
|
{:mox, "~> 1.2", only: :test},
|
||||||
{:websockex, "~> 0.4.3", only: :test},
|
{:websockex, "~> 0.4.3", only: :test},
|
||||||
{:benchee, "~> 1.4", only: :benchmark},
|
{:benchee, "~> 1.4", only: :benchmark},
|
||||||
|
|
|
||||||
78
mix.lock
78
mix.lock
|
|
@ -1,7 +1,7 @@
|
||||||
%{
|
%{
|
||||||
"accept": {:hex, :accept, "0.3.5", "b33b127abca7cc948bbe6caa4c263369abf1347cfa9d8e699c6d214660f10cd1", [:rebar3], [], "hexpm", "11b18c220bcc2eab63b5470c038ef10eb6783bcb1fcdb11aa4137defa5ac1bb8"},
|
"accept": {:hex, :accept, "0.3.5", "b33b127abca7cc948bbe6caa4c263369abf1347cfa9d8e699c6d214660f10cd1", [:rebar3], [], "hexpm", "11b18c220bcc2eab63b5470c038ef10eb6783bcb1fcdb11aa4137defa5ac1bb8"},
|
||||||
"argon2_elixir": {:hex, :argon2_elixir, "4.1.3", "4f28318286f89453364d7fbb53e03d4563fd7ed2438a60237eba5e426e97785f", [:make, :mix], [{:comeonin, "~> 5.3", [hex: :comeonin, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "7c295b8d8e0eaf6f43641698f962526cdf87c6feb7d14bd21e599271b510608c"},
|
"argon2_elixir": {:hex, :argon2_elixir, "4.1.3", "4f28318286f89453364d7fbb53e03d4563fd7ed2438a60237eba5e426e97785f", [:make, :mix], [{:comeonin, "~> 5.3", [hex: :comeonin, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "7c295b8d8e0eaf6f43641698f962526cdf87c6feb7d14bd21e599271b510608c"},
|
||||||
"bandit": {:hex, :bandit, "1.10.4", "02b9734c67c5916a008e7eb7e2ba68aaea6f8177094a5f8d95f1fb99069aac17", [:mix], [{:hpax, "~> 1.0", [hex: :hpax, repo: "hexpm", optional: false]}, {:plug, "~> 1.18", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:thousand_island, "~> 1.0", [hex: :thousand_island, repo: "hexpm", optional: false]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "a5faf501042ac1f31d736d9d4a813b3db4ef812e634583b6a457b0928798a51d"},
|
"bandit": {:hex, :bandit, "1.13.1", "1ce25a7beb750ed3fa2e905962f1f9a679cb585f1da0ae45637d06e385ce2eae", [:mix], [{:hpax, "~> 1.1", [hex: :hpax, repo: "hexpm", optional: false]}, {:plug, "~> 1.18", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:thousand_island, "~> 1.6", [hex: :thousand_island, repo: "hexpm", optional: false]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "39028f0b5a21404661410a6c19275060a9822c9dcca70b7c3e4e4fa50b11da58"},
|
||||||
"base62": {:hex, :base62, "1.2.2", "85c6627eb609317b70f555294045895ffaaeb1758666ab9ef9ca38865b11e629", [:mix], [{:custom_base, "~> 0.2.1", [hex: :custom_base, repo: "hexpm", optional: false]}], "hexpm", "d41336bda8eaa5be197f1e4592400513ee60518e5b9f4dcf38f4b4dae6f377bb"},
|
"base62": {:hex, :base62, "1.2.2", "85c6627eb609317b70f555294045895ffaaeb1758666ab9ef9ca38865b11e629", [:mix], [{:custom_base, "~> 0.2.1", [hex: :custom_base, repo: "hexpm", optional: false]}], "hexpm", "d41336bda8eaa5be197f1e4592400513ee60518e5b9f4dcf38f4b4dae6f377bb"},
|
||||||
"bbcode_pleroma": {:hex, :bbcode_pleroma, "0.2.0", "d36f5bca6e2f62261c45be30fa9b92725c0655ad45c99025cb1c3e28e25803ef", [:mix], [{:nimble_parsec, "~> 0.5", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "19851074419a5fedb4ef49e1f01b30df504bb5dbb6d6adfc135238063bebd1c3"},
|
"bbcode_pleroma": {:hex, :bbcode_pleroma, "0.2.0", "d36f5bca6e2f62261c45be30fa9b92725c0655ad45c99025cb1c3e28e25803ef", [:mix], [{:nimble_parsec, "~> 0.5", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "19851074419a5fedb4ef49e1f01b30df504bb5dbb6d6adfc135238063bebd1c3"},
|
||||||
"bcrypt_elixir": {:hex, :bcrypt_elixir, "2.3.1", "5114d780459a04f2b4aeef52307de23de961b69e13a5cd98a911e39fda13f420", [:make, :mix], [{:comeonin, "~> 5.3", [hex: :comeonin, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "42182d5f46764def15bf9af83739e3bf4ad22661b1c34fc3e88558efced07279"},
|
"bcrypt_elixir": {:hex, :bcrypt_elixir, "2.3.1", "5114d780459a04f2b4aeef52307de23de961b69e13a5cd98a911e39fda13f420", [:make, :mix], [{:comeonin, "~> 5.3", [hex: :comeonin, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "42182d5f46764def15bf9af83739e3bf4ad22661b1c34fc3e88558efced07279"},
|
||||||
|
|
@ -11,32 +11,31 @@
|
||||||
"cachex": {:hex, :cachex, "3.6.0", "14a1bfbeee060dd9bec25a5b6f4e4691e3670ebda28c8ba2884b12fe30b36bf8", [:mix], [{:eternal, "~> 1.2", [hex: :eternal, repo: "hexpm", optional: false]}, {:jumper, "~> 1.0", [hex: :jumper, repo: "hexpm", optional: false]}, {:sleeplocks, "~> 1.1", [hex: :sleeplocks, repo: "hexpm", optional: false]}, {:unsafe, "~> 1.0", [hex: :unsafe, repo: "hexpm", optional: false]}], "hexpm", "ebf24e373883bc8e0c8d894a63bbe102ae13d918f790121f5cfe6e485cc8e2e2"},
|
"cachex": {:hex, :cachex, "3.6.0", "14a1bfbeee060dd9bec25a5b6f4e4691e3670ebda28c8ba2884b12fe30b36bf8", [:mix], [{:eternal, "~> 1.2", [hex: :eternal, repo: "hexpm", optional: false]}, {:jumper, "~> 1.0", [hex: :jumper, repo: "hexpm", optional: false]}, {:sleeplocks, "~> 1.1", [hex: :sleeplocks, repo: "hexpm", optional: false]}, {:unsafe, "~> 1.0", [hex: :unsafe, repo: "hexpm", optional: false]}], "hexpm", "ebf24e373883bc8e0c8d894a63bbe102ae13d918f790121f5cfe6e485cc8e2e2"},
|
||||||
"calendar": {:hex, :calendar, "1.0.0", "f52073a708528482ec33d0a171954ca610fe2bd28f1e871f247dc7f1565fa807", [:mix], [{:tzdata, "~> 0.1.201603 or ~> 0.5.20 or ~> 1.0", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm", "990e9581920c82912a5ee50e62ff5ef96da6b15949a2ee4734f935fdef0f0a6f"},
|
"calendar": {:hex, :calendar, "1.0.0", "f52073a708528482ec33d0a171954ca610fe2bd28f1e871f247dc7f1565fa807", [:mix], [{:tzdata, "~> 0.1.201603 or ~> 0.5.20 or ~> 1.0", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm", "990e9581920c82912a5ee50e62ff5ef96da6b15949a2ee4734f935fdef0f0a6f"},
|
||||||
"captcha": {:hex, :pleroma_captcha, "1.0.4", "6e28ef98660981daeb4aee5043551d921b0094ec5bdeb4daf2f01858e14e9d2d", [:make, :mix], [], "hexpm", "aee86d4da3522ef39ba814e6954c4ebe692e72664efcb8fa5819b1335999b577"},
|
"captcha": {:hex, :pleroma_captcha, "1.0.4", "6e28ef98660981daeb4aee5043551d921b0094ec5bdeb4daf2f01858e14e9d2d", [:make, :mix], [], "hexpm", "aee86d4da3522ef39ba814e6954c4ebe692e72664efcb8fa5819b1335999b577"},
|
||||||
"castore": {:hex, :castore, "1.0.15", "8aa930c890fe18b6fe0a0cff27b27d0d4d231867897bd23ea772dee561f032a3", [:mix], [], "hexpm", "96ce4c69d7d5d7a0761420ef743e2f4096253931a3ba69e5ff8ef1844fe446d3"},
|
"castore": {:hex, :castore, "1.0.22", "a01978e6a5d42fa3a34143630588f92036e7d90e2309ecdd466722d1279a2213", [:mix], [], "hexpm", "c2a4d42723b4affbc1c3e15d8f6df3f7d8f1f627c84de643383634cce58493d5"},
|
||||||
"cc_precompiler": {:hex, :cc_precompiler, "0.1.11", "8c844d0b9fb98a3edea067f94f616b3f6b29b959b6b3bf25fee94ffe34364768", [:mix], [{:elixir_make, "~> 0.7", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "3427232caf0835f94680e5bcf082408a70b48ad68a5f5c0b02a3bea9f3a075b9"},
|
"cc_precompiler": {:hex, :cc_precompiler, "0.1.11", "8c844d0b9fb98a3edea067f94f616b3f6b29b959b6b3bf25fee94ffe34364768", [:mix], [{:elixir_make, "~> 0.7", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "3427232caf0835f94680e5bcf082408a70b48ad68a5f5c0b02a3bea9f3a075b9"},
|
||||||
"certifi": {:hex, :certifi, "2.12.0", "2d1cca2ec95f59643862af91f001478c9863c2ac9cb6e2f89780bfd8de987329", [:rebar3], [], "hexpm", "ee68d85df22e554040cdb4be100f33873ac6051387baf6a8f6ce82272340ff1c"},
|
"certifi": {:hex, :certifi, "2.17.0", "835748414307e15e05b17d0e518190228ce648b08d569a5cc93a85a40f3e5c9b", [:rebar3], [], "hexpm", "8122798a17f0293c80daada25d0f81c7f4d708c73fef782c7c9b1950e26e4d21"},
|
||||||
"combine": {:hex, :combine, "0.10.0", "eff8224eeb56498a2af13011d142c5e7997a80c8f5b97c499f84c841032e429f", [:mix], [], "hexpm", "1b1dbc1790073076580d0d1d64e42eae2366583e7aecd455d1215b0d16f2451b"},
|
"combine": {:hex, :combine, "0.10.0", "eff8224eeb56498a2af13011d142c5e7997a80c8f5b97c499f84c841032e429f", [:mix], [], "hexpm", "1b1dbc1790073076580d0d1d64e42eae2366583e7aecd455d1215b0d16f2451b"},
|
||||||
"comeonin": {:hex, :comeonin, "5.5.1", "5113e5f3800799787de08a6e0db307133850e635d34e9fab23c70b6501669510", [:mix], [], "hexpm", "65aac8f19938145377cee73973f192c5645873dcf550a8a6b18187d17c13ccdb"},
|
"comeonin": {:hex, :comeonin, "5.5.1", "5113e5f3800799787de08a6e0db307133850e635d34e9fab23c70b6501669510", [:mix], [], "hexpm", "65aac8f19938145377cee73973f192c5645873dcf550a8a6b18187d17c13ccdb"},
|
||||||
"concurrent_limiter": {:hex, :concurrent_limiter, "0.1.1", "43ae1dc23edda1ab03dd66febc739c4ff710d047bb4d735754909f9a474ae01c", [:mix], [{:telemetry, "~> 0.3", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "53968ff238c0fbb4d7ed76ddb1af0be6f3b2f77909f6796e249e737c505a16eb"},
|
"concurrent_limiter": {:hex, :concurrent_limiter, "0.1.1", "43ae1dc23edda1ab03dd66febc739c4ff710d047bb4d735754909f9a474ae01c", [:mix], [{:telemetry, "~> 0.3", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "53968ff238c0fbb4d7ed76ddb1af0be6f3b2f77909f6796e249e737c505a16eb"},
|
||||||
"connection": {:hex, :connection, "1.1.0", "ff2a49c4b75b6fb3e674bfc5536451607270aac754ffd1bdfe175abe4a6d7a68", [:mix], [], "hexpm", "722c1eb0a418fbe91ba7bd59a47e28008a189d47e37e0e7bb85585a016b2869c"},
|
"connection": {:hex, :connection, "1.1.0", "ff2a49c4b75b6fb3e674bfc5536451607270aac754ffd1bdfe175abe4a6d7a68", [:mix], [], "hexpm", "722c1eb0a418fbe91ba7bd59a47e28008a189d47e37e0e7bb85585a016b2869c"},
|
||||||
"cors_plug": {:hex, :cors_plug, "2.0.3", "316f806d10316e6d10f09473f19052d20ba0a0ce2a1d910ddf57d663dac402ae", [:mix], [{:plug, "~> 1.8", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "ee4ae1418e6ce117fc42c2ba3e6cbdca4e95ecd2fe59a05ec6884ca16d469aea"},
|
"cors_plug": {:hex, :cors_plug, "2.0.3", "316f806d10316e6d10f09473f19052d20ba0a0ce2a1d910ddf57d663dac402ae", [:mix], [{:plug, "~> 1.8", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "ee4ae1418e6ce117fc42c2ba3e6cbdca4e95ecd2fe59a05ec6884ca16d469aea"},
|
||||||
"covertool": {:hex, :covertool, "2.0.7", "398be995c4cf1a2861174389b3577ca97beee43b60c8f1afcf510f1b07d32408", [:rebar3], [], "hexpm", "46158ed6e1a0df7c0a912e314c7b8e053bd74daa5fc6b790614922a155b5720c"},
|
"covertool": {:hex, :covertool, "2.0.7", "398be995c4cf1a2861174389b3577ca97beee43b60c8f1afcf510f1b07d32408", [:rebar3], [], "hexpm", "46158ed6e1a0df7c0a912e314c7b8e053bd74daa5fc6b790614922a155b5720c"},
|
||||||
"cowboy": {:hex, :cowboy, "2.13.0", "09d770dd5f6a22cc60c071f432cd7cb87776164527f205c5a6b0f24ff6b38990", [:make, :rebar3], [{:cowlib, ">= 2.14.0 and < 3.0.0", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, ">= 1.8.0 and < 3.0.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "e724d3a70995025d654c1992c7b11dbfea95205c047d86ff9bf1cda92ddc5614"},
|
"cowboy": {:hex, :cowboy, "2.19.0", "78b9d92d25a23e56d7341040b67946c04067cd77de966e2372865f673a8e6f59", [:make, :rebar3], [{:cowlib, ">= 2.20.0 and < 3.0.0", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, ">= 1.8.0 and < 3.0.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "986dae81f99fcb78ef2d8efc21d738cee189410840eeaf32eca84ada81dcf6d4"},
|
||||||
"cowboy_telemetry": {:hex, :cowboy_telemetry, "0.4.0", "f239f68b588efa7707abce16a84d0d2acf3a0f50571f8bb7f56a15865aae820c", [:rebar3], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7d98bac1ee4565d31b62d59f8823dfd8356a169e7fcbb83831b8a5397404c9de"},
|
"cowboy_telemetry": {:hex, :cowboy_telemetry, "0.4.0", "f239f68b588efa7707abce16a84d0d2acf3a0f50571f8bb7f56a15865aae820c", [:rebar3], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7d98bac1ee4565d31b62d59f8823dfd8356a169e7fcbb83831b8a5397404c9de"},
|
||||||
"cowlib": {:hex, :cowlib, "2.17.1", "3e6053016d1ab245730f0af688755476dcedb1c25ed8fb5751f59a2bfdc0c9af", [:make, :rebar3], [], "hexpm", "ff08bd17e6dd931445b18af77315b9b5fe052407110964ad2588c686b57b5e3f"},
|
"cowlib": {:hex, :cowlib, "2.21.0", "3ea757c79784bdbdebac7857ce3adf2290af6b8680d4248c75e40ced2e98c89e", [:make, :rebar3], [], "hexpm", "0960248773762a5319b3c17285028bc76f54f58ef0f9451367401bdf67b46274"},
|
||||||
"credo": {:hex, :credo, "1.7.12", "9e3c20463de4b5f3f23721527fcaf16722ec815e70ff6c60b86412c695d426c1", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "8493d45c656c5427d9c729235b99d498bd133421f3e0a683e5c1b561471291e5"},
|
"credo": {:hex, :credo, "1.7.12", "9e3c20463de4b5f3f23721527fcaf16722ec815e70ff6c60b86412c695d426c1", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "8493d45c656c5427d9c729235b99d498bd133421f3e0a683e5c1b561471291e5"},
|
||||||
"crontab": {:hex, :crontab, "1.1.8", "2ce0e74777dfcadb28a1debbea707e58b879e6aa0ffbf9c9bb540887bce43617", [:mix], [{:ecto, "~> 1.0 or ~> 2.0 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: true]}], "hexpm"},
|
"crontab": {:hex, :crontab, "1.1.8", "2ce0e74777dfcadb28a1debbea707e58b879e6aa0ffbf9c9bb540887bce43617", [:mix], [{:ecto, "~> 1.0 or ~> 2.0 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: true]}], "hexpm"},
|
||||||
"custom_base": {:hex, :custom_base, "0.2.1", "4a832a42ea0552299d81652aa0b1f775d462175293e99dfbe4d7dbaab785a706", [:mix], [], "hexpm", "8df019facc5ec9603e94f7270f1ac73ddf339f56ade76a721eaa57c1493ba463"},
|
"custom_base": {:hex, :custom_base, "0.2.1", "4a832a42ea0552299d81652aa0b1f775d462175293e99dfbe4d7dbaab785a706", [:mix], [], "hexpm", "8df019facc5ec9603e94f7270f1ac73ddf339f56ade76a721eaa57c1493ba463"},
|
||||||
"db_connection": {:hex, :db_connection, "2.8.0", "64fd82cfa6d8e25ec6660cea73e92a4cbc6a18b31343910427b702838c4b33b2", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "008399dae5eee1bf5caa6e86d204dcb44242c82b1ed5e22c881f2c34da201b15"},
|
"db_connection": {:hex, :db_connection, "2.10.1", "d5465f6bcc125c1b8981c1dbf23c193ca16f446ec0b25832dc174f74f18be510", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "18ed94c6e627b4bf452dbd4df61b69a35a1e768525140bc1917b7a685026a6a3"},
|
||||||
"decimal": {:hex, :decimal, "2.3.0", "3ad6255aa77b4a3c4f818171b12d237500e63525c2fd056699967a3e7ea20f62", [:mix], [], "hexpm", "a4d66355cb29cb47c3cf30e71329e58361cfcb37c34235ef3bf1d7bf3773aeac"},
|
"decimal": {:hex, :decimal, "3.2.0", "f1235f05d841a211fda78a08fdb9777f2ff51a71f18455233fae186ff76187c6", [:mix], [], "hexpm", "190bca80580ce61aa69bb0eacadd7677710205c770225c2c2c07e32a5c31b234"},
|
||||||
"deep_merge": {:hex, :deep_merge, "1.0.0", "b4aa1a0d1acac393bdf38b2291af38cb1d4a52806cf7a4906f718e1feb5ee961", [:mix], [], "hexpm", "ce708e5f094b9cd4e8f2be4f00d2f4250c4095be93f8cd6d018c753894885430"},
|
"deep_merge": {:hex, :deep_merge, "1.0.0", "b4aa1a0d1acac393bdf38b2291af38cb1d4a52806cf7a4906f718e1feb5ee961", [:mix], [], "hexpm", "ce708e5f094b9cd4e8f2be4f00d2f4250c4095be93f8cd6d018c753894885430"},
|
||||||
"dialyxir": {:hex, :dialyxir, "1.4.6", "7cca478334bf8307e968664343cbdb432ee95b4b68a9cba95bdabb0ad5bdfd9a", [:mix], [{:erlex, ">= 0.2.7", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "8cf5615c5cd4c2da6c501faae642839c8405b49f8aa057ad4ae401cb808ef64d"},
|
"dialyxir": {:hex, :dialyxir, "1.4.6", "7cca478334bf8307e968664343cbdb432ee95b4b68a9cba95bdabb0ad5bdfd9a", [:mix], [{:erlex, ">= 0.2.7", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "8cf5615c5cd4c2da6c501faae642839c8405b49f8aa057ad4ae401cb808ef64d"},
|
||||||
"earmark": {:hex, :earmark, "1.4.46", "8c7287bd3137e99d26ae4643e5b7ef2129a260e3dcf41f251750cb4563c8fb81", [:mix], [], "hexpm", "798d86db3d79964e759ddc0c077d5eb254968ed426399fbf5a62de2b5ff8910a"},
|
"earmark_parser": {:hex, :earmark_parser, "1.4.46", "67607a0532e810c6f630a515c548d0b24949643f168cc556303bee4cf96105c7", [:mix], [], "hexpm", "9c44636e8a1c68c62f526b2dcd85d941dbbcee7ab82cf64ba06ce28bef8e89f5"},
|
||||||
"earmark_parser": {:hex, :earmark_parser, "1.4.44", "f20830dd6b5c77afe2b063777ddbbff09f9759396500cdbe7523efd58d7a339c", [:mix], [], "hexpm", "4778ac752b4701a5599215f7030989c989ffdc4f6df457c5f36938cc2d2a2750"},
|
|
||||||
"eblurhash": {:git, "https://github.com/zotonic/eblurhash.git", "bc37ceb426ef021ee9927fb249bb93f7059194ab", [ref: "bc37ceb426ef021ee9927fb249bb93f7059194ab"]},
|
"eblurhash": {:git, "https://github.com/zotonic/eblurhash.git", "bc37ceb426ef021ee9927fb249bb93f7059194ab", [ref: "bc37ceb426ef021ee9927fb249bb93f7059194ab"]},
|
||||||
"ecto": {:hex, :ecto, "3.13.2", "7d0c0863f3fc8d71d17fc3ad3b9424beae13f02712ad84191a826c7169484f01", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "669d9291370513ff56e7b7e7081b7af3283d02e046cf3d403053c557894a0b3e"},
|
"ecto": {:hex, :ecto, "3.14.0", "2fa64521eebfcb2670d907a86e4ad947290e9933706bb315e6fb5c21b172cb26", [:mix], [{:decimal, "~> 3.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "130d69ffb4285f9ce4792b65dfbb994fd13ea4cbc3cbea2524b199aa3de84af3"},
|
||||||
"ecto_enum": {:hex, :ecto_enum, "1.4.0", "d14b00e04b974afc69c251632d1e49594d899067ee2b376277efd8233027aec8", [:mix], [{:ecto, ">= 3.0.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:ecto_sql, "> 3.0.0", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:mariaex, ">= 0.0.0", [hex: :mariaex, repo: "hexpm", optional: true]}, {:postgrex, ">= 0.0.0", [hex: :postgrex, repo: "hexpm", optional: true]}], "hexpm", "8fb55c087181c2b15eee406519dc22578fa60dd82c088be376d0010172764ee4"},
|
"ecto_enum": {:hex, :ecto_enum, "1.4.0", "d14b00e04b974afc69c251632d1e49594d899067ee2b376277efd8233027aec8", [:mix], [{:ecto, ">= 3.0.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:ecto_sql, "> 3.0.0", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:mariaex, ">= 0.0.0", [hex: :mariaex, repo: "hexpm", optional: true]}, {:postgrex, ">= 0.0.0", [hex: :postgrex, repo: "hexpm", optional: true]}], "hexpm", "8fb55c087181c2b15eee406519dc22578fa60dd82c088be376d0010172764ee4"},
|
||||||
"ecto_psql_extras": {:hex, :ecto_psql_extras, "0.8.8", "aa02529c97f69aed5722899f5dc6360128735a92dd169f23c5d50b1f7fdede08", [:mix], [{:ecto_sql, "~> 3.7", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:postgrex, "> 0.16.0", [hex: :postgrex, repo: "hexpm", optional: false]}, {:table_rex, "~> 3.1.1 or ~> 4.0", [hex: :table_rex, repo: "hexpm", optional: false]}], "hexpm", "04c63d92b141723ad6fed2e60a4b461ca00b3594d16df47bbc48f1f4534f2c49"},
|
"ecto_psql_extras": {:hex, :ecto_psql_extras, "0.8.8", "aa02529c97f69aed5722899f5dc6360128735a92dd169f23c5d50b1f7fdede08", [:mix], [{:ecto_sql, "~> 3.7", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:postgrex, "> 0.16.0", [hex: :postgrex, repo: "hexpm", optional: false]}, {:table_rex, "~> 3.1.1 or ~> 4.0", [hex: :table_rex, repo: "hexpm", optional: false]}], "hexpm", "04c63d92b141723ad6fed2e60a4b461ca00b3594d16df47bbc48f1f4534f2c49"},
|
||||||
"ecto_sql": {:hex, :ecto_sql, "3.13.2", "a07d2461d84107b3d037097c822ffdd36ed69d1cf7c0f70e12a3d1decf04e2e1", [:mix], [{:db_connection, "~> 2.4.1 or ~> 2.5", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.13.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.7", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.19 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1 or ~> 2.2", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "539274ab0ecf1a0078a6a72ef3465629e4d6018a3028095dc90f60a19c371717"},
|
"ecto_sql": {:hex, :ecto_sql, "3.14.0", "06446ab8410d2f85bfbb80857ee224ab3b693700cbb38f6535d507449a627b2e", [:mix], [{:db_connection, "~> 2.9", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 3.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:ecto, "~> 3.14.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.8", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.19 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1 or ~> 2.2", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "f4d8d36faf294c9417b5a37ec7ac8217ee2abdef5fcf197ba690f361548d3949"},
|
||||||
"eimp": {:hex, :eimp, "1.0.14", "fc297f0c7e2700457a95a60c7010a5f1dcb768a083b6d53f49cd94ab95a28f22", [:rebar3], [{:p1_utils, "1.0.18", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "501133f3112079b92d9e22da8b88bf4f0e13d4d67ae9c15c42c30bd25ceb83b6"},
|
"eimp": {:hex, :eimp, "1.0.14", "fc297f0c7e2700457a95a60c7010a5f1dcb768a083b6d53f49cd94ab95a28f22", [:rebar3], [{:p1_utils, "1.0.18", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "501133f3112079b92d9e22da8b88bf4f0e13d4d67ae9c15c42c30bd25ceb83b6"},
|
||||||
"elixir_make": {:hex, :elixir_make, "0.7.8", "505026f266552ee5aabca0b9f9c229cbb496c689537c9f922f3eb5431157efc7", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:certifi, "~> 2.0", [hex: :certifi, repo: "hexpm", optional: true]}], "hexpm", "7a71945b913d37ea89b06966e1342c85cfe549b15e6d6d081e8081c493062c07"},
|
"elixir_make": {:hex, :elixir_make, "0.7.8", "505026f266552ee5aabca0b9f9c229cbb496c689537c9f922f3eb5431157efc7", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:certifi, "~> 2.0", [hex: :certifi, repo: "hexpm", optional: true]}], "hexpm", "7a71945b913d37ea89b06966e1342c85cfe549b15e6d6d081e8081c493062c07"},
|
||||||
"erlex": {:hex, :erlex, "0.2.7", "810e8725f96ab74d17aac676e748627a07bc87eb950d2b83acd29dc047a30595", [:mix], [], "hexpm", "3ed95f79d1a844c3f6bf0cea61e0d5612a42ce56da9c03f01df538685365efb0"},
|
"erlex": {:hex, :erlex, "0.2.7", "810e8725f96ab74d17aac676e748627a07bc87eb950d2b83acd29dc047a30595", [:mix], [], "hexpm", "3ed95f79d1a844c3f6bf0cea61e0d5612a42ce56da9c03f01df538685365efb0"},
|
||||||
|
|
@ -53,20 +52,21 @@
|
||||||
"fast_html": {:hex, :fast_html, "2.5.0", "23578c1c1fa03db6a3786d78218b2d944df34b0fc3729e72de912f390913c80f", [:make, :mix], [{:elixir_make, "~> 0.4", [hex: :elixir_make, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 1.1", [hex: :nimble_pool, repo: "hexpm", optional: false]}], "hexpm", "69eb46ed98a5d9cca1ccd4a5ac94ce5dd626fc29513fbaa0a16cd8b2da67ae3e"},
|
"fast_html": {:hex, :fast_html, "2.5.0", "23578c1c1fa03db6a3786d78218b2d944df34b0fc3729e72de912f390913c80f", [:make, :mix], [{:elixir_make, "~> 0.4", [hex: :elixir_make, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 1.1", [hex: :nimble_pool, repo: "hexpm", optional: false]}], "hexpm", "69eb46ed98a5d9cca1ccd4a5ac94ce5dd626fc29513fbaa0a16cd8b2da67ae3e"},
|
||||||
"fast_sanitize": {:hex, :fast_sanitize, "0.2.3", "67b93dfb34e302bef49fec3aaab74951e0f0602fd9fa99085987af05bd91c7a5", [:mix], [{:fast_html, "~> 2.0", [hex: :fast_html, repo: "hexpm", optional: false]}, {:plug, "~> 1.8", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "e8ad286d10d0386e15d67d0ee125245ebcfbc7d7290b08712ba9013c8c5e56e2"},
|
"fast_sanitize": {:hex, :fast_sanitize, "0.2.3", "67b93dfb34e302bef49fec3aaab74951e0f0602fd9fa99085987af05bd91c7a5", [:mix], [{:fast_html, "~> 2.0", [hex: :fast_html, repo: "hexpm", optional: false]}, {:plug, "~> 1.8", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "e8ad286d10d0386e15d67d0ee125245ebcfbc7d7290b08712ba9013c8c5e56e2"},
|
||||||
"file_system": {:hex, :file_system, "0.2.10", "fb082005a9cd1711c05b5248710f8826b02d7d1784e7c3451f9c1231d4fc162d", [:mix], [], "hexpm", "41195edbfb562a593726eda3b3e8b103a309b733ad25f3d642ba49696bf715dc"},
|
"file_system": {:hex, :file_system, "0.2.10", "fb082005a9cd1711c05b5248710f8826b02d7d1784e7c3451f9c1231d4fc162d", [:mix], [], "hexpm", "41195edbfb562a593726eda3b3e8b103a309b733ad25f3d642ba49696bf715dc"},
|
||||||
"finch": {:hex, :finch, "0.20.0", "5330aefb6b010f424dcbbc4615d914e9e3deae40095e73ab0c1bb0968933cadf", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.6.2 or ~> 1.7", [hex: :mint, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.4 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 1.1", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "2658131a74d051aabfcba936093c903b8e89da9a1b63e430bee62045fa9b2ee2"},
|
"finch": {:hex, :finch, "0.23.0", "e3f9287ac25a8832f848b144c2b57346aac65b205e2e0629a52adfe6507fd837", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.8", [hex: :mint, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.4 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 1.1", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "80e58d3f936f57e3fdf404f83a3642897ae6d9fb642934e46da4d8fe761b99d5"},
|
||||||
"flake_id": {:hex, :flake_id, "0.1.0", "7716b086d2e405d09b647121a166498a0d93d1a623bead243e1f74216079ccb3", [:mix], [{:base62, "~> 1.2", [hex: :base62, repo: "hexpm", optional: false]}, {:ecto, ">= 2.0.0", [hex: :ecto, repo: "hexpm", optional: true]}], "hexpm", "31fc8090fde1acd267c07c36ea7365b8604055f897d3a53dd967658c691bd827"},
|
"flake_id": {:hex, :flake_id, "0.1.0", "7716b086d2e405d09b647121a166498a0d93d1a623bead243e1f74216079ccb3", [:mix], [{:base62, "~> 1.2", [hex: :base62, repo: "hexpm", optional: false]}, {:ecto, ">= 2.0.0", [hex: :ecto, repo: "hexpm", optional: true]}], "hexpm", "31fc8090fde1acd267c07c36ea7365b8604055f897d3a53dd967658c691bd827"},
|
||||||
"floki": {:hex, :floki, "0.38.0", "62b642386fa3f2f90713f6e231da0fa3256e41ef1089f83b6ceac7a3fd3abf33", [:mix], [], "hexpm", "a5943ee91e93fb2d635b612caf5508e36d37548e84928463ef9dd986f0d1abd9"},
|
"floki": {:hex, :floki, "0.38.0", "62b642386fa3f2f90713f6e231da0fa3256e41ef1089f83b6ceac7a3fd3abf33", [:mix], [], "hexpm", "a5943ee91e93fb2d635b612caf5508e36d37548e84928463ef9dd986f0d1abd9"},
|
||||||
"gen_smtp": {:hex, :gen_smtp, "0.15.0", "9f51960c17769b26833b50df0b96123605a8024738b62db747fece14eb2fbfcc", [:rebar3], [], "hexpm", "29bd14a88030980849c7ed2447b8db6d6c9278a28b11a44cafe41b791205440f"},
|
"gen_smtp": {:hex, :gen_smtp, "0.15.0", "9f51960c17769b26833b50df0b96123605a8024738b62db747fece14eb2fbfcc", [:rebar3], [], "hexpm", "29bd14a88030980849c7ed2447b8db6d6c9278a28b11a44cafe41b791205440f"},
|
||||||
"gettext": {:hex, :gettext, "0.24.0", "6f4d90ac5f3111673cbefc4ebee96fe5f37a114861ab8c7b7d5b30a1108ce6d8", [:mix], [{:expo, "~> 0.5.1", [hex: :expo, repo: "hexpm", optional: false]}], "hexpm", "bdf75cdfcbe9e4622dd18e034b227d77dd17f0f133853a1c73b97b3d6c770e8b"},
|
"gettext": {:hex, :gettext, "0.24.0", "6f4d90ac5f3111673cbefc4ebee96fe5f37a114861ab8c7b7d5b30a1108ce6d8", [:mix], [{:expo, "~> 0.5.1", [hex: :expo, repo: "hexpm", optional: false]}], "hexpm", "bdf75cdfcbe9e4622dd18e034b227d77dd17f0f133853a1c73b97b3d6c770e8b"},
|
||||||
"gun": {:hex, :gun, "2.4.1", "c3a8bff8e155e1fc8e499216599bb7effbd27bc1985662a8b25016090dc18428", [:make, :rebar3], [{:cowlib, ">= 2.15.0 and < 3.0.0", [hex: :cowlib, repo: "hexpm", optional: false]}], "hexpm", "1450575b4d393aa0811322727b90ad1dd94b5c10026f54a1641785bcbd250384"},
|
"gun": {:hex, :gun, "2.7.0", "8dfc20ff1b70f3c78f07122f7d82ac6950c40d33a8a9b35da0134f49fb35b334", [:make, :rebar3], [{:cowlib, ">= 2.21.0 and < 3.0.0", [hex: :cowlib, repo: "hexpm", optional: false]}], "hexpm", "2a90d073069e72654b6c9a91fae365b746ff5ce385a4a5bbf71ef53a8bf95d82"},
|
||||||
"hackney": {:hex, :hackney, "1.20.1", "8d97aec62ddddd757d128bfd1df6c5861093419f8f7a4223823537bad5d064e2", [:rebar3], [{:certifi, "~> 2.12.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~> 6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~> 1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~> 1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.4.1", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~> 1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~> 0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "fe9094e5f1a2a2c0a7d10918fee36bfec0ec2a979994cff8cfe8058cd9af38e3"},
|
"h2": {:hex, :h2, "0.11.0", "b8c9f7c86f400582c5cbed59f79c0efd6cf49ad6478c210bede91d2d15019b14", [:rebar3], [], "hexpm", "c61f24361d0dc76375eaf35c2bf0e01235d81b7685538df571db036467fac358"},
|
||||||
"hpax": {:hex, :hpax, "1.0.3", "ed67ef51ad4df91e75cc6a1494f851850c0bd98ebc0be6e81b026e765ee535aa", [:mix], [], "hexpm", "8eab6e1cfa8d5918c2ce4ba43588e894af35dbd8e91e6e55c817bca5847df34a"},
|
"hackney": {:hex, :hackney, "4.7.3", "42feec1a4986000411cb5d389bbc9fb61d9dff84c5eef42b11fc40ea327e6f9a", [:rebar3], [{:certifi, "~> 2.17.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:h2, "~> 0.11.0", [hex: :h2, repo: "hexpm", optional: false]}, {:idna, "~> 7.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:mimerl, "~> 1.5", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.4.2", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~> 1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:webtransport, "~> 0.4.4", [hex: :webtransport, repo: "hexpm", optional: false]}], "hexpm", "3f460e01d5a1abba39c32d06e9a733bb76e35d1ab87a83868b6bb29d646fe25f"},
|
||||||
|
"hpax": {:hex, :hpax, "1.1.0", "782931867cc23217c68fb5f68fe1a11f5e7544c7fda82c8a7019a5df5a4a1cdf", [:mix], [], "hexpm", "0b8d0f05832f55571d65ac720f79bf8994138ffbb133209dc4685eae0ad456a8"},
|
||||||
"html_entities": {:hex, :html_entities, "0.5.2", "9e47e70598da7de2a9ff6af8758399251db6dbb7eebe2b013f2bbd2515895c3c", [:mix], [], "hexpm", "c53ba390403485615623b9531e97696f076ed415e8d8058b1dbaa28181f4fdcc"},
|
"html_entities": {:hex, :html_entities, "0.5.2", "9e47e70598da7de2a9ff6af8758399251db6dbb7eebe2b013f2bbd2515895c3c", [:mix], [], "hexpm", "c53ba390403485615623b9531e97696f076ed415e8d8058b1dbaa28181f4fdcc"},
|
||||||
"http_signatures": {:hex, :http_signatures, "0.1.3", "19f26aee35b4684e37efdce3ac4638605e6e41a04368186bd39d2b6138a60ea9", [:mix], [{:plug, "~> 1.18", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "20313a65516db88006f85b090f6f76cc5b04e9609b45943657e6781eb91174f4"},
|
"http_signatures": {:hex, :http_signatures, "0.1.3", "19f26aee35b4684e37efdce3ac4638605e6e41a04368186bd39d2b6138a60ea9", [:mix], [{:plug, "~> 1.18", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "20313a65516db88006f85b090f6f76cc5b04e9609b45943657e6781eb91174f4"},
|
||||||
"httpoison": {:hex, :httpoison, "1.8.2", "9eb9c63ae289296a544842ef816a85d881d4a31f518a0fec089aaa744beae290", [:mix], [{:hackney, "~> 1.17", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "2bb350d26972e30c96e2ca74a1aaf8293d61d0742ff17f01e0279fef11599921"},
|
"httpoison": {:hex, :httpoison, "3.0.0", "8566a933bb9175236d1ec335978445b67cd1f5b5d3ead6ca4b80be469d41f5d9", [:mix], [{:hackney, "~> 4.0", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "9130197b7658901c493d6fcfb842fb9676300fa8a6c8ed058c8889cf1a77f3c2"},
|
||||||
"idna": {:hex, :idna, "6.1.1", "8a63070e9f7d0c62eb9d9fcb360a7de382448200fbbd1b106cc96d3d8099df8d", [:rebar3], [{:unicode_util_compat, "~> 0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "92376eb7894412ed19ac475e4a86f7b413c1b9fbb5bd16dccd57934157944cea"},
|
"idna": {:hex, :idna, "7.1.0", "1067a13043538129602d2f2ce6899d8713125c7d19734aa557ce2e3ea55bd4f1", [:rebar3], [], "hexpm", "6ae959a025bf36df61a8cab8508d9654891b5426a84c44d82deaffd6ddf8c71f"},
|
||||||
"inet_cidr": {:hex, :inet_cidr, "1.0.9", "e0ef72a2942529da78c8e4147d53f2ef5f6f5293335c3637b0fdf83c012cc816", [:mix], [], "hexpm", "172da15ff7cf635b1feaf14f5818be28c811b37cc5fb7c5f7c01058c1c1066cc"},
|
"inet_cidr": {:hex, :inet_cidr, "1.0.9", "e0ef72a2942529da78c8e4147d53f2ef5f6f5293335c3637b0fdf83c012cc816", [:mix], [], "hexpm", "172da15ff7cf635b1feaf14f5818be28c811b37cc5fb7c5f7c01058c1c1066cc"},
|
||||||
"jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"},
|
"jason": {:hex, :jason, "1.4.5", "2e3a008590b0b8d7388c20293e9dcc9cf3e5d642fd2a114e4cbbb52e595d940a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0 or ~> 3.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "b0c823996102bcd0239b3c2444eb00409b72f6a140c1950bc8b457d836b30684"},
|
||||||
"joken": {:hex, :joken, "2.6.2", "5daaf82259ca603af4f0b065475099ada1b2b849ff140ccd37f4b6828ca6892a", [:mix], [{:jose, "~> 1.11.10", [hex: :jose, repo: "hexpm", optional: false]}], "hexpm", "5134b5b0a6e37494e46dbf9e4dad53808e5e787904b7c73972651b51cce3d72b"},
|
"joken": {:hex, :joken, "2.6.2", "5daaf82259ca603af4f0b065475099ada1b2b849ff140ccd37f4b6828ca6892a", [:mix], [{:jose, "~> 1.11.10", [hex: :jose, repo: "hexpm", optional: false]}], "hexpm", "5134b5b0a6e37494e46dbf9e4dad53808e5e787904b7c73972651b51cce3d72b"},
|
||||||
"jose": {:hex, :jose, "1.11.10", "a903f5227417bd2a08c8a00a0cbcc458118be84480955e8d251297a425723f83", [:mix, :rebar3], [], "hexpm", "0d6cd36ff8ba174db29148fc112b5842186b68a90ce9fc2b3ec3afe76593e614"},
|
"jose": {:hex, :jose, "1.11.10", "a903f5227417bd2a08c8a00a0cbcc458118be84480955e8d251297a425723f83", [:mix, :rebar3], [], "hexpm", "0d6cd36ff8ba174db29148fc112b5842186b68a90ce9fc2b3ec3afe76593e614"},
|
||||||
"jumper": {:hex, :jumper, "1.0.2", "68cdcd84472a00ac596b4e6459a41b3062d4427cbd4f1e8c8793c5b54f1406a7", [:mix], [], "hexpm", "9b7782409021e01ab3c08270e26f36eb62976a38c1aa64b2eaf6348422f165e1"},
|
"jumper": {:hex, :jumper, "1.0.2", "68cdcd84472a00ac596b4e6459a41b3062d4427cbd4f1e8c8793c5b54f1406a7", [:mix], [], "hexpm", "9b7782409021e01ab3c08270e26f36eb62976a38c1aa64b2eaf6348422f165e1"},
|
||||||
|
|
@ -81,7 +81,8 @@
|
||||||
"metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"},
|
"metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"},
|
||||||
"mime": {:hex, :mime, "1.6.0", "dabde576a497cef4bbdd60aceee8160e02a6c89250d6c0b29e56c0dfb00db3d2", [:mix], [], "hexpm", "31a1a8613f8321143dde1dafc36006a17d28d02bdfecb9e95a880fa7aabd19a7"},
|
"mime": {:hex, :mime, "1.6.0", "dabde576a497cef4bbdd60aceee8160e02a6c89250d6c0b29e56c0dfb00db3d2", [:mix], [], "hexpm", "31a1a8613f8321143dde1dafc36006a17d28d02bdfecb9e95a880fa7aabd19a7"},
|
||||||
"mimerl": {:hex, :mimerl, "1.5.0", "f35aca6f23242339b3666e0ac0702379e362b469d0aea167f6cc713547e777ed", [:rebar3], [], "hexpm", "db648ce065bae14ea84ca8b5dd123f42f49417cef693541110bf6f9e9be9ecc4"},
|
"mimerl": {:hex, :mimerl, "1.5.0", "f35aca6f23242339b3666e0ac0702379e362b469d0aea167f6cc713547e777ed", [:rebar3], [], "hexpm", "db648ce065bae14ea84ca8b5dd123f42f49417cef693541110bf6f9e9be9ecc4"},
|
||||||
"mint": {:hex, :mint, "1.7.1", "113fdb2b2f3b59e47c7955971854641c61f378549d73e829e1768de90fc1abf1", [:mix], [{:castore, "~> 0.1.0 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:hpax, "~> 0.1.1 or ~> 0.2.0 or ~> 1.0", [hex: :hpax, repo: "hexpm", optional: false]}], "hexpm", "fceba0a4d0f24301ddee3024ae116df1c3f4bb7a563a731f45fdfeb9d39a231b"},
|
"mint": {:hex, :mint, "1.11.0", "a713551624815c0435237b93d90ea8b9b14254690c66f732d0ef8930f76ff1d9", [:mix], [{:castore, "~> 0.1.0 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:hpax, "~> 1.1", [hex: :hpax, repo: "hexpm", optional: false]}], "hexpm", "c6279ba2d6aa3a383a1d4cfbe7b59f42e6efd400f58d8e2acfeac48a438693ab"},
|
||||||
|
"mix_audit": {:hex, :mix_audit, "2.1.5", "c0f77cee6b4ef9d97e37772359a187a166c7a1e0e08b50edf5bf6959dfe5a016", [:make, :mix], [{:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}, {:yaml_elixir, "~> 2.11", [hex: :yaml_elixir, repo: "hexpm", optional: false]}], "hexpm", "87f9298e21da32f697af535475860dc1d3617a010e0b418d2ec6142bc8b42d69"},
|
||||||
"mochiweb": {:hex, :mochiweb, "2.18.0", "eb55f1db3e6e960fac4e6db4e2db9ec3602cc9f30b86cd1481d56545c3145d2e", [:rebar3], [], "hexpm"},
|
"mochiweb": {:hex, :mochiweb, "2.18.0", "eb55f1db3e6e960fac4e6db4e2db9ec3602cc9f30b86cd1481d56545c3145d2e", [:rebar3], [], "hexpm"},
|
||||||
"mock": {:hex, :mock, "0.3.9", "10e44ad1f5962480c5c9b9fa779c6c63de9bd31997c8e04a853ec990a9d841af", [:mix], [{:meck, "~> 0.9.2", [hex: :meck, repo: "hexpm", optional: false]}], "hexpm", "9e1b244c4ca2551bb17bb8415eed89e40ee1308e0fbaed0a4fdfe3ec8a4adbd3"},
|
"mock": {:hex, :mock, "0.3.9", "10e44ad1f5962480c5c9b9fa779c6c63de9bd31997c8e04a853ec990a9d841af", [:mix], [{:meck, "~> 0.9.2", [hex: :meck, repo: "hexpm", optional: false]}], "hexpm", "9e1b244c4ca2551bb17bb8415eed89e40ee1308e0fbaed0a4fdfe3ec8a4adbd3"},
|
||||||
"mogrify": {:hex, :mogrify, "0.9.3", "238c782f00271dace01369ad35ae2e9dd020feee3443b9299ea5ea6bed559841", [:mix], [], "hexpm", "0189b1e1de27455f2b9ae8cf88239cefd23d38de9276eb5add7159aea51731e6"},
|
"mogrify": {:hex, :mogrify, "0.9.3", "238c782f00271dace01369ad35ae2e9dd020feee3443b9299ea5ea6bed559841", [:mix], [], "hexpm", "0189b1e1de27455f2b9ae8cf88239cefd23d38de9276eb5add7159aea51731e6"},
|
||||||
|
|
@ -89,8 +90,8 @@
|
||||||
"mua": {:hex, :mua, "0.2.4", "a9172ab0a1ac8732cf2699d739ceac3febcb9b4ffc540260ad2e32c0b6632af9", [:mix], [{:castore, "~> 0.1.0 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}], "hexpm", "e7e4dacd5ad65f13e3542772e74a159c00bd2d5579e729e9bb72d2c73a266fb7"},
|
"mua": {:hex, :mua, "0.2.4", "a9172ab0a1ac8732cf2699d739ceac3febcb9b4ffc540260ad2e32c0b6632af9", [:mix], [{:castore, "~> 0.1.0 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}], "hexpm", "e7e4dacd5ad65f13e3542772e74a159c00bd2d5579e729e9bb72d2c73a266fb7"},
|
||||||
"multipart": {:hex, :multipart, "0.4.0", "634880a2148d4555d050963373d0e3bbb44a55b2badd87fa8623166172e9cda0", [:mix], [{:mime, "~> 1.2 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}], "hexpm", "3c5604bc2fb17b3137e5d2abdf5dacc2647e60c5cc6634b102cf1aef75a06f0a"},
|
"multipart": {:hex, :multipart, "0.4.0", "634880a2148d4555d050963373d0e3bbb44a55b2badd87fa8623166172e9cda0", [:mix], [{:mime, "~> 1.2 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}], "hexpm", "3c5604bc2fb17b3137e5d2abdf5dacc2647e60c5cc6634b102cf1aef75a06f0a"},
|
||||||
"nimble_options": {:hex, :nimble_options, "1.1.1", "e3a492d54d85fc3fd7c5baf411d9d2852922f66e69476317787a7b2bb000a61b", [:mix], [], "hexpm", "821b2470ca9442c4b6984882fe9bb0389371b8ddec4d45a9504f00a66f650b44"},
|
"nimble_options": {:hex, :nimble_options, "1.1.1", "e3a492d54d85fc3fd7c5baf411d9d2852922f66e69476317787a7b2bb000a61b", [:mix], [], "hexpm", "821b2470ca9442c4b6984882fe9bb0389371b8ddec4d45a9504f00a66f650b44"},
|
||||||
"nimble_ownership": {:hex, :nimble_ownership, "1.0.1", "f69fae0cdd451b1614364013544e66e4f5d25f36a2056a9698b793305c5aa3a6", [:mix], [], "hexpm", "3825e461025464f519f3f3e4a1f9b68c47dc151369611629ad08b636b73bb22d"},
|
"nimble_ownership": {:hex, :nimble_ownership, "1.0.2", "fa8a6f2d8c592ad4d79b2ca617473c6aefd5869abfa02563a77682038bf916cf", [:mix], [], "hexpm", "098af64e1f6f8609c6672127cfe9e9590a5d3fcdd82bc17a377b8692fd81a879"},
|
||||||
"nimble_parsec": {:hex, :nimble_parsec, "0.6.0", "32111b3bf39137144abd7ba1cce0914533b2d16ef35e8abc5ec8be6122944263", [:mix], [], "hexpm", "27eac315a94909d4dc68bc07a4a83e06c8379237c5ea528a9acff4ca1c873c52"},
|
"nimble_parsec": {:hex, :nimble_parsec, "1.4.2", "8efba0122db06df95bfaa78f791344a89352ba04baedd3849593bfce4d0dc1c6", [:mix], [], "hexpm", "4b21398942dda052b403bbe1da991ccd03a053668d147d53fb8c4e0efe09c973"},
|
||||||
"nimble_pool": {:hex, :nimble_pool, "1.1.0", "bf9c29fbdcba3564a8b800d1eeb5a3c58f36e1e11d7b7fb2e084a643f645f06b", [:mix], [], "hexpm", "af2e4e6b34197db81f7aad230c1118eac993acc0dae6bc83bac0126d4ae0813a"},
|
"nimble_pool": {:hex, :nimble_pool, "1.1.0", "bf9c29fbdcba3564a8b800d1eeb5a3c58f36e1e11d7b7fb2e084a643f645f06b", [:mix], [], "hexpm", "af2e4e6b34197db81f7aad230c1118eac993acc0dae6bc83bac0126d4ae0813a"},
|
||||||
"nodex": {:git, "https://git.pleroma.social/pleroma/nodex", "cb6730f943cfc6aad674c92161be23a8411f15d1", [ref: "cb6730f943cfc6aad674c92161be23a8411f15d1"]},
|
"nodex": {:git, "https://git.pleroma.social/pleroma/nodex", "cb6730f943cfc6aad674c92161be23a8411f15d1", [ref: "cb6730f943cfc6aad674c92161be23a8411f15d1"]},
|
||||||
"oban": {:hex, :oban, "2.19.4", "045adb10db1161dceb75c254782f97cdc6596e7044af456a59decb6d06da73c1", [:mix], [{:ecto_sql, "~> 3.10", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:ecto_sqlite3, "~> 0.9", [hex: :ecto_sqlite3, repo: "hexpm", optional: true]}, {:igniter, "~> 0.5", [hex: :igniter, repo: "hexpm", optional: true]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: true]}, {:myxql, "~> 0.7", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.16", [hex: :postgrex, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "5fcc6219e6464525b808d97add17896e724131f498444a292071bf8991c99f97"},
|
"oban": {:hex, :oban, "2.19.4", "045adb10db1161dceb75c254782f97cdc6596e7044af456a59decb6d06da73c1", [:mix], [{:ecto_sql, "~> 3.10", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:ecto_sqlite3, "~> 0.9", [hex: :ecto_sqlite3, repo: "hexpm", optional: true]}, {:igniter, "~> 0.5", [hex: :igniter, repo: "hexpm", optional: true]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: true]}, {:myxql, "~> 0.7", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.16", [hex: :postgrex, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "5fcc6219e6464525b808d97add17896e724131f498444a292071bf8991c99f97"},
|
||||||
|
|
@ -99,27 +100,27 @@
|
||||||
"oban_plugins_lazarus": {:hex, :oban_plugins_lazarus, "0.1.1", "a5141d569e5b9f3bec8f4a958231d2c538af097d3c1ad06274f096fc06956821", [:mix], [{:oban, "< 3.0.0", [hex: :oban, repo: "hexpm", optional: false]}], "hexpm", "7e9c51bc44d33f71c0a52cf0cd5c8d4c70380ede065c1042013f5123f2fc9729"},
|
"oban_plugins_lazarus": {:hex, :oban_plugins_lazarus, "0.1.1", "a5141d569e5b9f3bec8f4a958231d2c538af097d3c1ad06274f096fc06956821", [:mix], [{:oban, "< 3.0.0", [hex: :oban, repo: "hexpm", optional: false]}], "hexpm", "7e9c51bc44d33f71c0a52cf0cd5c8d4c70380ede065c1042013f5123f2fc9729"},
|
||||||
"oban_web": {:hex, :oban_web, "2.11.6", "53933cb4253c4d9f1098ee311c06f07935259f0e564dcf2d66bae4cc98e317fe", [:mix], [{:jason, "~> 1.2", [hex: :jason, repo: "hexpm", optional: false]}, {:oban, "~> 2.19", [hex: :oban, repo: "hexpm", optional: false]}, {:oban_met, "~> 1.0", [hex: :oban_met, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.7", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 3.3 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 1.0", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}, {:phoenix_pubsub, "~> 2.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}], "hexpm", "576d94b705688c313694c2c114ca21aa0f8f2ad1b9ca45c052c5ba316d3e8d10"},
|
"oban_web": {:hex, :oban_web, "2.11.6", "53933cb4253c4d9f1098ee311c06f07935259f0e564dcf2d66bae4cc98e317fe", [:mix], [{:jason, "~> 1.2", [hex: :jason, repo: "hexpm", optional: false]}, {:oban, "~> 2.19", [hex: :oban, repo: "hexpm", optional: false]}, {:oban_met, "~> 1.0", [hex: :oban_met, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.7", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 3.3 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 1.0", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}, {:phoenix_pubsub, "~> 2.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}], "hexpm", "576d94b705688c313694c2c114ca21aa0f8f2ad1b9ca45c052c5ba316d3e8d10"},
|
||||||
"octo_fetch": {:hex, :octo_fetch, "0.4.0", "074b5ecbc08be10b05b27e9db08bc20a3060142769436242702931c418695b19", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~> 1.1", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm", "cf8be6f40cd519d7000bb4e84adcf661c32e59369ca2827c4e20042eda7a7fc6"},
|
"octo_fetch": {:hex, :octo_fetch, "0.4.0", "074b5ecbc08be10b05b27e9db08bc20a3060142769436242702931c418695b19", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~> 1.1", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm", "cf8be6f40cd519d7000bb4e84adcf661c32e59369ca2827c4e20042eda7a7fc6"},
|
||||||
"open_api_spex": {:hex, :open_api_spex, "3.22.0", "fbf90dc82681dc042a4ee79853c8e989efbba73d9e87439085daf849bbf8bc20", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}, {:poison, "~> 3.0 or ~> 4.0 or ~> 5.0 or ~> 6.0", [hex: :poison, repo: "hexpm", optional: true]}, {:ymlr, "~> 2.0 or ~> 3.0 or ~> 4.0 or ~> 5.0", [hex: :ymlr, repo: "hexpm", optional: true]}], "hexpm", "dd751ddbdd709bb4a5313e9a24530da6e66594773c7242a0c2592cbd9f589063"},
|
"open_api_spex": {:hex, :open_api_spex, "3.22.3", "0e383bf23cc3a060bffaebbcd09fc06bfc908d948c00e518aed36bbf8a2fe473", [:mix], [{:decimal, "~> 1.0 or ~> 2.0 or ~> 3.0", [hex: :decimal, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}, {:poison, "~> 3.0 or ~> 4.0 or ~> 5.0 or ~> 6.0", [hex: :poison, repo: "hexpm", optional: true]}, {:ymlr, "~> 2.0 or ~> 3.0 or ~> 4.0 or ~> 5.0", [hex: :ymlr, repo: "hexpm", optional: true]}], "hexpm", "5f74f1878fdc38f8e961b0b943ac7af88dcf3a82a0c0ef6680ddfd3d161aecbd"},
|
||||||
"parse_trans": {:hex, :parse_trans, "3.4.1", "6e6aa8167cb44cc8f39441d05193be6e6f4e7c2946cb2759f015f8c56b76e5ff", [:rebar3], [], "hexpm", "620a406ce75dada827b82e453c19cf06776be266f5a67cff34e1ef2cbb60e49a"},
|
"parse_trans": {:hex, :parse_trans, "3.4.2", "c352ddc1a0d5e54f9b1654d45f9c432eef76f9cea371c55ddff769ef688fdb74", [:rebar3], [], "hexpm", "4c25347de3b7c35732d32e69ab43d1ceee0beae3f3b3ade1b59cbd3dd224d9ca"},
|
||||||
"pbkdf2_elixir": {:hex, :pbkdf2_elixir, "1.2.1", "9cbe354b58121075bd20eb83076900a3832324b7dd171a6895fab57b6bb2752c", [:mix], [{:comeonin, "~> 5.3", [hex: :comeonin, repo: "hexpm", optional: false]}], "hexpm", "d3b40a4a4630f0b442f19eca891fcfeeee4c40871936fed2f68e1c4faa30481f"},
|
"pbkdf2_elixir": {:hex, :pbkdf2_elixir, "1.2.1", "9cbe354b58121075bd20eb83076900a3832324b7dd171a6895fab57b6bb2752c", [:mix], [{:comeonin, "~> 5.3", [hex: :comeonin, repo: "hexpm", optional: false]}], "hexpm", "d3b40a4a4630f0b442f19eca891fcfeeee4c40871936fed2f68e1c4faa30481f"},
|
||||||
"phoenix": {:hex, :phoenix, "1.8.3", "49ac5e485083cb1495a905e47eb554277bdd9c65ccb4fc5100306b350151aa95", [:mix], [{:bandit, "~> 1.0", [hex: :bandit, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.7", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:websock_adapter, "~> 0.5.3", [hex: :websock_adapter, repo: "hexpm", optional: false]}], "hexpm", "36169f95cc2e155b78be93d9590acc3f462f1e5438db06e6248613f27c80caec"},
|
"phoenix": {:hex, :phoenix, "1.8.11", "9bb8f0c4e9f0b9eaf7f34cf8cb0bd25bd8599763a5587d64918a9ebb04027823", [:mix], [{:bandit, "~> 1.0", [hex: :bandit, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.7", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 2.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:websock_adapter, "~> 0.5", [hex: :websock_adapter, repo: "hexpm", optional: false]}], "hexpm", "44f028f4129e5a29487e868f84903373e3d032da151ad0c789c3849f464e7351"},
|
||||||
"phoenix_ecto": {:hex, :phoenix_ecto, "4.6.5", "c4ef322acd15a574a8b1a08eff0ee0a85e73096b53ce1403b6563709f15e1cea", [:mix], [{:ecto, "~> 3.5", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.1", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.9", [hex: :plug, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.16 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}], "hexpm", "26ec3208eef407f31b748cadd044045c6fd485fbff168e35963d2f9dfff28d4b"},
|
"phoenix_ecto": {:hex, :phoenix_ecto, "4.6.5", "c4ef322acd15a574a8b1a08eff0ee0a85e73096b53ce1403b6563709f15e1cea", [:mix], [{:ecto, "~> 3.5", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.1", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.9", [hex: :plug, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.16 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}], "hexpm", "26ec3208eef407f31b748cadd044045c6fd485fbff168e35963d2f9dfff28d4b"},
|
||||||
"phoenix_html": {:hex, :phoenix_html, "3.3.4", "42a09fc443bbc1da37e372a5c8e6755d046f22b9b11343bf885067357da21cb3", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "0249d3abec3714aff3415e7ee3d9786cb325be3151e6c4b3021502c585bf53fb"},
|
"phoenix_html": {:hex, :phoenix_html, "3.3.4", "42a09fc443bbc1da37e372a5c8e6755d046f22b9b11343bf885067357da21cb3", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "0249d3abec3714aff3415e7ee3d9786cb325be3151e6c4b3021502c585bf53fb"},
|
||||||
"phoenix_live_dashboard": {:hex, :phoenix_live_dashboard, "0.8.7", "405880012cb4b706f26dd1c6349125bfc903fb9e44d1ea668adaf4e04d4884b7", [:mix], [{:ecto, "~> 3.6.2 or ~> 3.7", [hex: :ecto, repo: "hexpm", optional: true]}, {:ecto_mysql_extras, "~> 0.5", [hex: :ecto_mysql_extras, repo: "hexpm", optional: true]}, {:ecto_psql_extras, "~> 0.7", [hex: :ecto_psql_extras, repo: "hexpm", optional: true]}, {:ecto_sqlite3_extras, "~> 1.1.7 or ~> 1.2.0", [hex: :ecto_sqlite3_extras, repo: "hexpm", optional: true]}, {:mime, "~> 1.6 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 0.19 or ~> 1.0", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}, {:telemetry_metrics, "~> 0.6 or ~> 1.0", [hex: :telemetry_metrics, repo: "hexpm", optional: false]}], "hexpm", "3a8625cab39ec261d48a13b7468dc619c0ede099601b084e343968309bd4d7d7"},
|
"phoenix_live_dashboard": {:hex, :phoenix_live_dashboard, "0.8.7", "405880012cb4b706f26dd1c6349125bfc903fb9e44d1ea668adaf4e04d4884b7", [:mix], [{:ecto, "~> 3.6.2 or ~> 3.7", [hex: :ecto, repo: "hexpm", optional: true]}, {:ecto_mysql_extras, "~> 0.5", [hex: :ecto_mysql_extras, repo: "hexpm", optional: true]}, {:ecto_psql_extras, "~> 0.7", [hex: :ecto_psql_extras, repo: "hexpm", optional: true]}, {:ecto_sqlite3_extras, "~> 1.1.7 or ~> 1.2.0", [hex: :ecto_sqlite3_extras, repo: "hexpm", optional: true]}, {:mime, "~> 1.6 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 0.19 or ~> 1.0", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}, {:telemetry_metrics, "~> 0.6 or ~> 1.0", [hex: :telemetry_metrics, repo: "hexpm", optional: false]}], "hexpm", "3a8625cab39ec261d48a13b7468dc619c0ede099601b084e343968309bd4d7d7"},
|
||||||
"phoenix_live_reload": {:hex, :phoenix_live_reload, "1.3.3", "3a53772a6118d5679bf50fc1670505a290e32a1d195df9e069d8c53ab040c054", [:mix], [{:file_system, "~> 0.2.1 or ~> 0.3", [hex: :file_system, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.4", [hex: :phoenix, repo: "hexpm", optional: false]}], "hexpm", "766796676e5f558dbae5d1bdb066849673e956005e3730dfd5affd7a6da4abac"},
|
"phoenix_live_reload": {:hex, :phoenix_live_reload, "1.3.3", "3a53772a6118d5679bf50fc1670505a290e32a1d195df9e069d8c53ab040c054", [:mix], [{:file_system, "~> 0.2.1 or ~> 0.3", [hex: :file_system, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.4", [hex: :phoenix, repo: "hexpm", optional: false]}], "hexpm", "766796676e5f558dbae5d1bdb066849673e956005e3730dfd5affd7a6da4abac"},
|
||||||
"phoenix_live_view": {:hex, :phoenix_live_view, "1.1.19", "c95e9acbc374fb796ee3e24bfecc8213123c74d9f9e45667ca40bb0a4d242953", [:mix], [{:igniter, ">= 0.6.16 and < 1.0.0-0", [hex: :igniter, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:lazy_html, "~> 0.1.0", [hex: :lazy_html, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6.15 or ~> 1.7.0 or ~> 1.8.0-rc", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 3.3 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.15", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "d5ad357d6b21562a5b431f0ad09dfe76db9ce5648c6949f1aac334c8c4455d32"},
|
"phoenix_live_view": {:hex, :phoenix_live_view, "1.2.9", "d35ddac2fab4480e6bdbf712ff104fd9e969a2bbe81b578ee80f066b371f56db", [:mix], [{:igniter, ">= 0.6.16 and < 1.0.0-0", [hex: :igniter, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:lazy_html, "~> 0.1.0", [hex: :lazy_html, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6.15 or ~> 1.7.0 or ~> 1.8.0", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 3.3 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.15", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "2f9528c3d7046edabbb30a91710ca33988f8d8bc20a964a1fc48b32134572afa"},
|
||||||
"phoenix_pubsub": {:hex, :phoenix_pubsub, "2.1.3", "3168d78ba41835aecad272d5e8cd51aa87a7ac9eb836eabc42f6e57538e3731d", [:mix], [], "hexpm", "bba06bc1dcfd8cb086759f0edc94a8ba2bc8896d5331a1e2c2902bf8e36ee502"},
|
"phoenix_pubsub": {:hex, :phoenix_pubsub, "2.2.0", "ff3a5616e1bed6804de7773b92cbccfc0b0f473faf1f63d7daf1206c7aeaaa6f", [:mix], [], "hexpm", "adc313a5bf7136039f63cfd9668fde73bba0765e0614cba80c06ac9460ff3e96"},
|
||||||
"phoenix_swoosh": {:hex, :phoenix_swoosh, "1.2.1", "b74ccaa8046fbc388a62134360ee7d9742d5a8ae74063f34eb050279de7a99e1", [:mix], [{:finch, "~> 0.8", [hex: :finch, repo: "hexpm", optional: true]}, {:hackney, "~> 1.10", [hex: :hackney, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6", [hex: :phoenix, repo: "hexpm", optional: true]}, {:phoenix_html, "~> 3.0 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:phoenix_view, "~> 1.0 or ~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: false]}, {:swoosh, "~> 1.5", [hex: :swoosh, repo: "hexpm", optional: false]}], "hexpm", "4000eeba3f9d7d1a6bf56d2bd56733d5cadf41a7f0d8ffe5bb67e7d667e204a2"},
|
"phoenix_swoosh": {:hex, :phoenix_swoosh, "1.2.1", "b74ccaa8046fbc388a62134360ee7d9742d5a8ae74063f34eb050279de7a99e1", [:mix], [{:finch, "~> 0.8", [hex: :finch, repo: "hexpm", optional: true]}, {:hackney, "~> 1.10", [hex: :hackney, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6", [hex: :phoenix, repo: "hexpm", optional: true]}, {:phoenix_html, "~> 3.0 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:phoenix_view, "~> 1.0 or ~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: false]}, {:swoosh, "~> 1.5", [hex: :swoosh, repo: "hexpm", optional: false]}], "hexpm", "4000eeba3f9d7d1a6bf56d2bd56733d5cadf41a7f0d8ffe5bb67e7d667e204a2"},
|
||||||
"phoenix_template": {:hex, :phoenix_template, "1.0.4", "e2092c132f3b5e5b2d49c96695342eb36d0ed514c5b252a77048d5969330d639", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "2c0c81f0e5c6753faf5cca2f229c9709919aba34fab866d3bc05060c9c444206"},
|
"phoenix_template": {:hex, :phoenix_template, "1.0.4", "e2092c132f3b5e5b2d49c96695342eb36d0ed514c5b252a77048d5969330d639", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "2c0c81f0e5c6753faf5cca2f229c9709919aba34fab866d3bc05060c9c444206"},
|
||||||
"phoenix_view": {:hex, :phoenix_view, "2.0.4", "b45c9d9cf15b3a1af5fb555c674b525391b6a1fe975f040fb4d913397b31abf4", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}], "hexpm", "4e992022ce14f31fe57335db27a28154afcc94e9983266835bb3040243eb620b"},
|
"phoenix_view": {:hex, :phoenix_view, "2.0.4", "b45c9d9cf15b3a1af5fb555c674b525391b6a1fe975f040fb4d913397b31abf4", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}], "hexpm", "4e992022ce14f31fe57335db27a28154afcc94e9983266835bb3040243eb620b"},
|
||||||
"pleroma_mfm_parser": {:hex, :pleroma_mfm_parser, "0.2.1", "87a40fec4e2e035447df34e1c98e1b816b6a448d49baea73eb2273531ac5ce66", [:mix], [], "hexpm", "ec4845461da95d63ab3d592086d65114c45057a1f2d48d768f3def45b0d7696f"},
|
"pleroma_mfm_parser": {:hex, :pleroma_mfm_parser, "0.2.1", "87a40fec4e2e035447df34e1c98e1b816b6a448d49baea73eb2273531ac5ce66", [:mix], [], "hexpm", "ec4845461da95d63ab3d592086d65114c45057a1f2d48d768f3def45b0d7696f"},
|
||||||
"plug": {:hex, :plug, "1.19.1", "09bac17ae7a001a68ae393658aa23c7e38782be5c5c00c80be82901262c394c0", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "560a0017a8f6d5d30146916862aaf9300b7280063651dd7e532b8be168511e62"},
|
"plug": {:hex, :plug, "1.20.3", "56c480c633ec2ce10140e236e15233bf576e1d323887d7c96711bd02ab5160db", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "be266aee1b8536ef6409d58cf39a3121319f0ec47cfa1b24024485aa0e76ad76"},
|
||||||
"plug_cowboy": {:hex, :plug_cowboy, "2.7.4", "729c752d17cf364e2b8da5bdb34fb5804f56251e88bb602aff48ae0bd8673d11", [:mix], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:cowboy_telemetry, "~> 0.3", [hex: :cowboy_telemetry, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "9b85632bd7012615bae0a5d70084deb1b25d2bcbb32cab82d1e9a1e023168aa3"},
|
"plug_cowboy": {:hex, :plug_cowboy, "2.9.0", "87e21e0d9054ced99c36d128f49e3ea2cd8b745fffb97de50bff99706087af4f", [:mix], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:cowboy_telemetry, "~> 0.3", [hex: :cowboy_telemetry, repo: "hexpm", optional: false]}, {:plug, "~> 1.18", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "2002bafba4f3a45b55a58e68d70211b153a7ed18d37edb1ceb6e96e7a92c422e"},
|
||||||
"plug_crypto": {:hex, :plug_crypto, "2.1.1", "19bda8184399cb24afa10be734f84a16ea0a2bc65054e23a62bb10f06bc89491", [:mix], [], "hexpm", "6470bce6ffe41c8bd497612ffde1a7e4af67f36a15eea5f921af71cf3e11247c"},
|
"plug_crypto": {:hex, :plug_crypto, "2.2.0", "144014737daaf485407f5ed77daeaad74d651b216a28c87543f8cc7043f8efc8", [:mix], [], "hexpm", "83a95744ab1c75876542b6fab135fcc176280e0f301a111c1f757fddcec95d2c"},
|
||||||
"plug_static_index_html": {:hex, :plug_static_index_html, "1.0.0", "840123d4d3975585133485ea86af73cb2600afd7f2a976f9f5fd8b3808e636a0", [:mix], [{:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "79fd4fcf34d110605c26560cbae8f23c603ec4158c08298bd4360fdea90bb5cf"},
|
"plug_static_index_html": {:hex, :plug_static_index_html, "1.0.0", "840123d4d3975585133485ea86af73cb2600afd7f2a976f9f5fd8b3808e636a0", [:mix], [{:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "79fd4fcf34d110605c26560cbae8f23c603ec4158c08298bd4360fdea90bb5cf"},
|
||||||
"poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [:mix], [], "hexpm", "fec8660eb7733ee4117b85f55799fd3833eb769a6df71ccf8903e8dc5447cfce"},
|
"poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [:mix], [], "hexpm", "fec8660eb7733ee4117b85f55799fd3833eb769a6df71ccf8903e8dc5447cfce"},
|
||||||
"poolboy": {:hex, :poolboy, "1.5.2", "392b007a1693a64540cead79830443abf5762f5d30cf50bc95cb2c1aaafa006b", [:rebar3], [], "hexpm", "dad79704ce5440f3d5a3681c8590b9dc25d1a561e8f5a9c995281012860901e3"},
|
"poolboy": {:hex, :poolboy, "1.5.2", "392b007a1693a64540cead79830443abf5762f5d30cf50bc95cb2c1aaafa006b", [:rebar3], [], "hexpm", "dad79704ce5440f3d5a3681c8590b9dc25d1a561e8f5a9c995281012860901e3"},
|
||||||
"postgrex": {:hex, :postgrex, "0.21.1", "2c5cc830ec11e7a0067dd4d623c049b3ef807e9507a424985b8dcf921224cd88", [:mix], [{:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "27d8d21c103c3cc68851b533ff99eef353e6a0ff98dc444ea751de43eb48bdac"},
|
"postgrex": {:hex, :postgrex, "0.22.4", "d271f595dfd25230b6398354e19d17bb5e2d20130fd2d9bdca7e15f125d43552", [:mix], [{:db_connection, "~> 2.9", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0 or ~> 3.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "4aae45a2d60e35b04eea2602440be152fae332901f1fc7a60fc7cb7f0f9a9c5a"},
|
||||||
"pot": {:hex, :pot, "1.0.2", "13abb849139fdc04ab8154986abbcb63bdee5de6ed2ba7e1713527e33df923dd", [:rebar3], [], "hexpm", "78fe127f5a4f5f919d6ea5a2a671827bd53eb9d37e5b4128c0ad3df99856c2e0"},
|
"pot": {:hex, :pot, "1.0.2", "13abb849139fdc04ab8154986abbcb63bdee5de6ed2ba7e1713527e33df923dd", [:rebar3], [], "hexpm", "78fe127f5a4f5f919d6ea5a2a671827bd53eb9d37e5b4128c0ad3df99856c2e0"},
|
||||||
"prom_ex": {:hex, :prom_ex, "1.9.0", "63e6dda6c05cdeec1f26c48443dcc38ffd2118b3665ae8d2bd0e5b79f2aea03e", [:mix], [{:absinthe, ">= 1.6.0", [hex: :absinthe, repo: "hexpm", optional: true]}, {:broadway, ">= 1.0.2", [hex: :broadway, repo: "hexpm", optional: true]}, {:ecto, ">= 3.5.0", [hex: :ecto, repo: "hexpm", optional: true]}, {:finch, "~> 0.15", [hex: :finch, repo: "hexpm", optional: false]}, {:jason, "~> 1.2", [hex: :jason, repo: "hexpm", optional: false]}, {:oban, ">= 2.4.0", [hex: :oban, repo: "hexpm", optional: true]}, {:octo_fetch, "~> 0.3", [hex: :octo_fetch, repo: "hexpm", optional: false]}, {:phoenix, ">= 1.5.0", [hex: :phoenix, repo: "hexpm", optional: true]}, {:phoenix_live_view, ">= 0.14.0", [hex: :phoenix_live_view, repo: "hexpm", optional: true]}, {:plug, ">= 1.12.1", [hex: :plug, repo: "hexpm", optional: true]}, {:plug_cowboy, "~> 2.5", [hex: :plug_cowboy, repo: "hexpm", optional: false]}, {:telemetry, ">= 1.0.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:telemetry_metrics, "~> 0.6", [hex: :telemetry_metrics, repo: "hexpm", optional: false]}, {:telemetry_metrics_prometheus_core, "~> 1.0", [hex: :telemetry_metrics_prometheus_core, repo: "hexpm", optional: false]}, {:telemetry_poller, "~> 1.0", [hex: :telemetry_poller, repo: "hexpm", optional: false]}], "hexpm", "01f3d4f69ec93068219e686cc65e58a29c42bea5429a8ff4e2121f19db178ee6"},
|
"prom_ex": {:hex, :prom_ex, "1.9.0", "63e6dda6c05cdeec1f26c48443dcc38ffd2118b3665ae8d2bd0e5b79f2aea03e", [:mix], [{:absinthe, ">= 1.6.0", [hex: :absinthe, repo: "hexpm", optional: true]}, {:broadway, ">= 1.0.2", [hex: :broadway, repo: "hexpm", optional: true]}, {:ecto, ">= 3.5.0", [hex: :ecto, repo: "hexpm", optional: true]}, {:finch, "~> 0.15", [hex: :finch, repo: "hexpm", optional: false]}, {:jason, "~> 1.2", [hex: :jason, repo: "hexpm", optional: false]}, {:oban, ">= 2.4.0", [hex: :oban, repo: "hexpm", optional: true]}, {:octo_fetch, "~> 0.3", [hex: :octo_fetch, repo: "hexpm", optional: false]}, {:phoenix, ">= 1.5.0", [hex: :phoenix, repo: "hexpm", optional: true]}, {:phoenix_live_view, ">= 0.14.0", [hex: :phoenix_live_view, repo: "hexpm", optional: true]}, {:plug, ">= 1.12.1", [hex: :plug, repo: "hexpm", optional: true]}, {:plug_cowboy, "~> 2.5", [hex: :plug_cowboy, repo: "hexpm", optional: false]}, {:telemetry, ">= 1.0.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:telemetry_metrics, "~> 0.6", [hex: :telemetry_metrics, repo: "hexpm", optional: false]}, {:telemetry_metrics_prometheus_core, "~> 1.0", [hex: :telemetry_metrics_prometheus_core, repo: "hexpm", optional: false]}, {:telemetry_poller, "~> 1.0", [hex: :telemetry_poller, repo: "hexpm", optional: false]}], "hexpm", "01f3d4f69ec93068219e686cc65e58a29c42bea5429a8ff4e2121f19db178ee6"},
|
||||||
"prometheus": {:hex, :prometheus, "4.10.0", "792adbf0130ff61b5fa8826f013772af24b6e57b984445c8d602c8a0355704a1", [:mix, :rebar3], [{:quantile_estimator, "~> 0.2.1", [hex: :quantile_estimator, repo: "hexpm", optional: false]}], "hexpm", "2a99bb6dce85e238c7236fde6b0064f9834dc420ddbd962aac4ea2a3c3d59384"},
|
"prometheus": {:hex, :prometheus, "4.10.0", "792adbf0130ff61b5fa8826f013772af24b6e57b984445c8d602c8a0355704a1", [:mix, :rebar3], [{:quantile_estimator, "~> 0.2.1", [hex: :quantile_estimator, repo: "hexpm", optional: false]}], "hexpm", "2a99bb6dce85e238c7236fde6b0064f9834dc420ddbd962aac4ea2a3c3d59384"},
|
||||||
|
|
@ -129,26 +130,24 @@
|
||||||
"prometheus_phx": {:git, "https://git.pleroma.social/pleroma/elixir-libraries/prometheus-phx.git", "9cd8f248c9381ffedc799905050abce194a97514", [branch: "no-logging"]},
|
"prometheus_phx": {:git, "https://git.pleroma.social/pleroma/elixir-libraries/prometheus-phx.git", "9cd8f248c9381ffedc799905050abce194a97514", [branch: "no-logging"]},
|
||||||
"prometheus_plugs": {:hex, :prometheus_plugs, "1.1.5", "25933d48f8af3a5941dd7b621c889749894d8a1082a6ff7c67cc99dec26377c5", [:mix], [{:accept, "~> 0.1", [hex: :accept, repo: "hexpm", optional: false]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}, {:prometheus_ex, "~> 1.1 or ~> 2.0 or ~> 3.0", [hex: :prometheus_ex, repo: "hexpm", optional: false]}, {:prometheus_process_collector, "~> 1.1", [hex: :prometheus_process_collector, repo: "hexpm", optional: true]}], "hexpm", "0273a6483ccb936d79ca19b0ab629aef0dba958697c94782bb728b920dfc6a79"},
|
"prometheus_plugs": {:hex, :prometheus_plugs, "1.1.5", "25933d48f8af3a5941dd7b621c889749894d8a1082a6ff7c67cc99dec26377c5", [:mix], [{:accept, "~> 0.1", [hex: :accept, repo: "hexpm", optional: false]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}, {:prometheus_ex, "~> 1.1 or ~> 2.0 or ~> 3.0", [hex: :prometheus_ex, repo: "hexpm", optional: false]}, {:prometheus_process_collector, "~> 1.1", [hex: :prometheus_process_collector, repo: "hexpm", optional: true]}], "hexpm", "0273a6483ccb936d79ca19b0ab629aef0dba958697c94782bb728b920dfc6a79"},
|
||||||
"quantile_estimator": {:hex, :quantile_estimator, "0.2.1", "ef50a361f11b5f26b5f16d0696e46a9e4661756492c981f7b2229ef42ff1cd15", [:rebar3], [], "hexpm", "282a8a323ca2a845c9e6f787d166348f776c1d4a41ede63046d72d422e3da946"},
|
"quantile_estimator": {:hex, :quantile_estimator, "0.2.1", "ef50a361f11b5f26b5f16d0696e46a9e4661756492c981f7b2229ef42ff1cd15", [:rebar3], [], "hexpm", "282a8a323ca2a845c9e6f787d166348f776c1d4a41ede63046d72d422e3da946"},
|
||||||
"quic": {:hex, :quic, "0.10.2", "4b390507a85f65ce47808f3df1a864e0baf9adb7a1b4ea9f4dcd66fe9d0cb166", [:rebar3], [], "hexpm", "7c196a66973c877a59768a5687f0a0610ff11817254d0a4e45cc4e3a16b1d00b"},
|
"quic": {:hex, :quic, "1.8.0", "7b074fc6f8a13b4a81e4e11254bf061db61c5c07b4366d568a79a38c5e8bb5f3", [:rebar3], [], "hexpm", "6b1f8f08a45412ac503bc6771efb8edc696bb14079bc48d2ca960b92933da8a4"},
|
||||||
"ranch": {:hex, :ranch, "2.2.0", "25528f82bc8d7c6152c57666ca99ec716510fe0925cb188172f41ce93117b1b0", [:make, :rebar3], [], "hexpm", "fa0b99a1780c80218a4197a59ea8d3bdae32fbff7e88527d7d8a4787eff4f8e7"},
|
"ranch": {:hex, :ranch, "2.2.1", "fc2bb0e800efbeab8ff82eb325450b9c08653b14c3f69e8b09bbea304973105d", [:make, :rebar3], [], "hexpm", "55f05cce20ec2da1d90de5d5981afb93dbfc01325fc7e933189aa5c62f037c24"},
|
||||||
"recon": {:hex, :recon, "2.5.6", "9052588e83bfedfd9b72e1034532aee2a5369d9d9343b61aeb7fbce761010741", [:mix, :rebar3], [], "hexpm", "96c6799792d735cc0f0fd0f86267e9d351e63339cbe03df9d162010cefc26bb0"},
|
"recon": {:hex, :recon, "2.5.6", "9052588e83bfedfd9b72e1034532aee2a5369d9d9343b61aeb7fbce761010741", [:mix, :rebar3], [], "hexpm", "96c6799792d735cc0f0fd0f86267e9d351e63339cbe03df9d162010cefc26bb0"},
|
||||||
"remote_ip": {:hex, :remote_ip, "1.2.0", "fb078e12a44414f4cef5a75963c33008fe169b806572ccd17257c208a7bc760f", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "2ff91de19c48149ce19ed230a81d377186e4412552a597d6a5137373e5877cb7"},
|
"remote_ip": {:hex, :remote_ip, "1.2.0", "fb078e12a44414f4cef5a75963c33008fe169b806572ccd17257c208a7bc760f", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "2ff91de19c48149ce19ed230a81d377186e4412552a597d6a5137373e5877cb7"},
|
||||||
"rustler": {:hex, :rustler, "0.30.0", "cefc49922132b072853fa9b0ca4dc2ffcb452f68fb73b779042b02d545e097fb", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:toml, "~> 0.6", [hex: :toml, repo: "hexpm", optional: false]}], "hexpm", "9ef1abb6a7dda35c47cfc649e6a5a61663af6cf842a55814a554a84607dee389"},
|
|
||||||
"sleeplocks": {:hex, :sleeplocks, "1.1.3", "96a86460cc33b435c7310dbd27ec82ca2c1f24ae38e34f8edde97f756503441a", [:rebar3], [], "hexpm", "d3b3958552e6eb16f463921e70ae7c767519ef8f5be46d7696cc1ed649421321"},
|
"sleeplocks": {:hex, :sleeplocks, "1.1.3", "96a86460cc33b435c7310dbd27ec82ca2c1f24ae38e34f8edde97f756503441a", [:rebar3], [], "hexpm", "d3b3958552e6eb16f463921e70ae7c767519ef8f5be46d7696cc1ed649421321"},
|
||||||
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.7", "354c321cf377240c7b8716899e182ce4890c5938111a1296add3ec74cf1715df", [:make, :mix, :rebar3], [], "hexpm", "fe4c190e8f37401d30167c8c405eda19469f34577987c76dde613e838bbc67f8"},
|
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.7", "354c321cf377240c7b8716899e182ce4890c5938111a1296add3ec74cf1715df", [:make, :mix, :rebar3], [], "hexpm", "fe4c190e8f37401d30167c8c405eda19469f34577987c76dde613e838bbc67f8"},
|
||||||
"statistex": {:hex, :statistex, "1.1.0", "7fec1eb2f580a0d2c1a05ed27396a084ab064a40cfc84246dbfb0c72a5c761e5", [:mix], [], "hexpm", "f5950ea26ad43246ba2cce54324ac394a4e7408fdcf98b8e230f503a0cba9cf5"},
|
"statistex": {:hex, :statistex, "1.1.0", "7fec1eb2f580a0d2c1a05ed27396a084ab064a40cfc84246dbfb0c72a5c761e5", [:mix], [], "hexpm", "f5950ea26ad43246ba2cce54324ac394a4e7408fdcf98b8e230f503a0cba9cf5"},
|
||||||
"sweet_xml": {:hex, :sweet_xml, "0.7.5", "803a563113981aaac202a1dbd39771562d0ad31004ddbfc9b5090bdcd5605277", [:mix], [], "hexpm", "193b28a9b12891cae351d81a0cead165ffe67df1b73fe5866d10629f4faefb12"},
|
"sweet_xml": {:hex, :sweet_xml, "0.7.5", "803a563113981aaac202a1dbd39771562d0ad31004ddbfc9b5090bdcd5605277", [:mix], [], "hexpm", "193b28a9b12891cae351d81a0cead165ffe67df1b73fe5866d10629f4faefb12"},
|
||||||
"swoosh": {:hex, :swoosh, "1.16.12", "cbb24ad512f2f7f24c7a469661c188a00a8c2cd64e0ab54acd1520f132092dfd", [:mix], [{:bandit, ">= 1.0.0", [hex: :bandit, repo: "hexpm", optional: true]}, {:cowboy, "~> 1.1 or ~> 2.4", [hex: :cowboy, repo: "hexpm", optional: true]}, {:ex_aws, "~> 2.1", [hex: :ex_aws, repo: "hexpm", optional: true]}, {:finch, "~> 0.6", [hex: :finch, repo: "hexpm", optional: true]}, {:gen_smtp, "~> 0.13 or ~> 1.0", [hex: :gen_smtp, repo: "hexpm", optional: true]}, {:hackney, "~> 1.9", [hex: :hackney, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mail, "~> 0.2", [hex: :mail, repo: "hexpm", optional: true]}, {:mime, "~> 1.1 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mua, "~> 0.2.3", [hex: :mua, repo: "hexpm", optional: true]}, {:multipart, "~> 0.4", [hex: :multipart, repo: "hexpm", optional: true]}, {:plug, "~> 1.9", [hex: :plug, repo: "hexpm", optional: true]}, {:plug_cowboy, ">= 1.0.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:req, "~> 0.5 or ~> 1.0", [hex: :req, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "0e262df1ae510d59eeaaa3db42189a2aa1b3746f73771eb2616fc3f7ee63cc20"},
|
"swoosh": {:hex, :swoosh, "1.28.0", "30d9f3519a150128e90d97f9878a8a5cd8325ed03d618d1c626201496fc20baf", [:mix], [{:bandit, ">= 1.0.0", [hex: :bandit, repo: "hexpm", optional: true]}, {:cowboy, "~> 1.1 or ~> 2.4", [hex: :cowboy, repo: "hexpm", optional: true]}, {:ex_aws, "~> 2.1", [hex: :ex_aws, repo: "hexpm", optional: true]}, {:finch, "~> 0.6", [hex: :finch, repo: "hexpm", optional: true]}, {:gen_smtp, "~> 0.13 or ~> 1.0", [hex: :gen_smtp, repo: "hexpm", optional: true]}, {:hackney, ">= 1.9.0 and < 5.0.0", [hex: :hackney, repo: "hexpm", optional: true]}, {:idna, ">= 6.0.0 and < 8.0.0", [hex: :idna, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mail, "~> 0.2", [hex: :mail, repo: "hexpm", optional: true]}, {:mime, "~> 1.1 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mua, "~> 0.2.3", [hex: :mua, repo: "hexpm", optional: true]}, {:multipart, "~> 0.4", [hex: :multipart, repo: "hexpm", optional: true]}, {:plug, "~> 1.9", [hex: :plug, repo: "hexpm", optional: true]}, {:plug_cowboy, ">= 1.0.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:req, "~> 0.5.10 or ~> 0.6 or ~> 1.0", [hex: :req, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "bb5c0b7c988beb53786254a61580597fe1017a652039061ee2b4c28b5097b10e"},
|
||||||
"syslog": {:git, "https://github.com/Vagabond/erlang-syslog", "614a1fde7a8654bee2815b599bbdaae1bb0323b5", [ref: "614a1fde7a8654bee2815b599bbdaae1bb0323b5"]},
|
"syslog": {:git, "https://github.com/Vagabond/erlang-syslog", "614a1fde7a8654bee2815b599bbdaae1bb0323b5", [ref: "614a1fde7a8654bee2815b599bbdaae1bb0323b5"]},
|
||||||
"table_rex": {:hex, :table_rex, "4.1.0", "fbaa8b1ce154c9772012bf445bfb86b587430fb96f3b12022d3f35ee4a68c918", [:mix], [], "hexpm", "95932701df195d43bc2d1c6531178fc8338aa8f38c80f098504d529c43bc2601"},
|
"table_rex": {:hex, :table_rex, "4.1.0", "fbaa8b1ce154c9772012bf445bfb86b587430fb96f3b12022d3f35ee4a68c918", [:mix], [], "hexpm", "95932701df195d43bc2d1c6531178fc8338aa8f38c80f098504d529c43bc2601"},
|
||||||
"telemetry": {:hex, :telemetry, "1.0.0", "0f453a102cdf13d506b7c0ab158324c337c41f1cc7548f0bc0e130bbf0ae9452", [:rebar3], [], "hexpm", "73bc09fa59b4a0284efb4624335583c528e07ec9ae76aca96ea0673850aec57a"},
|
"telemetry": {:hex, :telemetry, "1.0.0", "0f453a102cdf13d506b7c0ab158324c337c41f1cc7548f0bc0e130bbf0ae9452", [:rebar3], [], "hexpm", "73bc09fa59b4a0284efb4624335583c528e07ec9ae76aca96ea0673850aec57a"},
|
||||||
"telemetry_metrics": {:hex, :telemetry_metrics, "0.6.2", "2caabe9344ec17eafe5403304771c3539f3b6e2f7fb6a6f602558c825d0d0bfb", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "9b43db0dc33863930b9ef9d27137e78974756f5f198cae18409970ed6fa5b561"},
|
"telemetry_metrics": {:hex, :telemetry_metrics, "0.6.2", "2caabe9344ec17eafe5403304771c3539f3b6e2f7fb6a6f602558c825d0d0bfb", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "9b43db0dc33863930b9ef9d27137e78974756f5f198cae18409970ed6fa5b561"},
|
||||||
"telemetry_metrics_prometheus_core": {:hex, :telemetry_metrics_prometheus_core, "1.2.1", "c9755987d7b959b557084e6990990cb96a50d6482c683fb9622a63837f3cd3d8", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:telemetry_metrics, "~> 0.6 or ~> 1.0", [hex: :telemetry_metrics, repo: "hexpm", optional: false]}], "hexpm", "5e2c599da4983c4f88a33e9571f1458bf98b0cf6ba930f1dc3a6e8cf45d5afb6"},
|
"telemetry_metrics_prometheus_core": {:hex, :telemetry_metrics_prometheus_core, "1.2.1", "c9755987d7b959b557084e6990990cb96a50d6482c683fb9622a63837f3cd3d8", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:telemetry_metrics, "~> 0.6 or ~> 1.0", [hex: :telemetry_metrics, repo: "hexpm", optional: false]}], "hexpm", "5e2c599da4983c4f88a33e9571f1458bf98b0cf6ba930f1dc3a6e8cf45d5afb6"},
|
||||||
"telemetry_poller": {:hex, :telemetry_poller, "1.3.0", "d5c46420126b5ac2d72bc6580fb4f537d35e851cc0f8dbd571acf6d6e10f5ec7", [:rebar3], [{:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "51f18bed7128544a50f75897db9974436ea9bfba560420b646af27a9a9b35211"},
|
"telemetry_poller": {:hex, :telemetry_poller, "1.3.0", "d5c46420126b5ac2d72bc6580fb4f537d35e851cc0f8dbd571acf6d6e10f5ec7", [:rebar3], [{:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "51f18bed7128544a50f75897db9974436ea9bfba560420b646af27a9a9b35211"},
|
||||||
"tesla": {:hex, :tesla, "1.15.3", "3a2b5c37f09629b8dcf5d028fbafc9143c0099753559d7fe567eaabfbd9b8663", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:exjsx, ">= 3.0.0", [hex: :exjsx, repo: "hexpm", optional: true]}, {:finch, "~> 0.13", [hex: :finch, repo: "hexpm", optional: true]}, {:fuse, "~> 2.4", [hex: :fuse, repo: "hexpm", optional: true]}, {:gun, ">= 1.0.0", [hex: :gun, repo: "hexpm", optional: true]}, {:hackney, "~> 1.21", [hex: :hackney, repo: "hexpm", optional: true]}, {:ibrowse, "4.4.2", [hex: :ibrowse, repo: "hexpm", optional: true]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: true]}, {:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.0", [hex: :mint, repo: "hexpm", optional: true]}, {:mox, "~> 1.0", [hex: :mox, repo: "hexpm", optional: true]}, {:msgpax, "~> 2.3", [hex: :msgpax, repo: "hexpm", optional: true]}, {:poison, ">= 1.0.0", [hex: :poison, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm", "98bb3d4558abc67b92fb7be4cd31bb57ca8d80792de26870d362974b58caeda7"},
|
"tesla": {:hex, :tesla, "1.20.0", "922b504e186da2ee82990d44e7422435f41b67403908a00340c3a0b23a17aa90", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:exjsx, ">= 3.0.0", [hex: :exjsx, repo: "hexpm", optional: true]}, {:finch, "~> 0.13", [hex: :finch, repo: "hexpm", optional: true]}, {:fuse, "~> 2.4", [hex: :fuse, repo: "hexpm", optional: true]}, {:gun, ">= 1.0.0", [hex: :gun, repo: "hexpm", optional: true]}, {:hackney, "~> 1.21 or >= 4.0.2 and < 5.0.0-0", [hex: :hackney, repo: "hexpm", optional: true]}, {:ibrowse, "4.4.2", [hex: :ibrowse, repo: "hexpm", optional: true]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: true]}, {:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.0", [hex: :mint, repo: "hexpm", optional: true]}, {:mox, "~> 1.0", [hex: :mox, repo: "hexpm", optional: true]}, {:msgpax, "~> 2.3", [hex: :msgpax, repo: "hexpm", optional: true]}, {:opentelemetry_semantic_conventions, "~> 1.27", [hex: :opentelemetry_semantic_conventions, repo: "hexpm", optional: true]}, {:poison, ">= 1.0.0", [hex: :poison, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm", "3ecb41cb458772332752c3acdfe983e23abb991f5a43cfd69a64e9ea3f4b0061"},
|
||||||
"thousand_island": {:hex, :thousand_island, "1.4.3", "2158209580f633be38d43ec4e3ce0a01079592b9657afff9080d5d8ca149a3af", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "6e4ce09b0fd761a58594d02814d40f77daff460c48a7354a15ab353bb998ea0b"},
|
"thousand_island": {:hex, :thousand_island, "1.6.0", "d6ad7c2220c96a270cc8ca1586ec4d8bde648a4a3a53f003ce093f41beec0288", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "4d4720470f091fc21a1311777c2ac66916c43621bffc068590c9f9bb3cd057a3"},
|
||||||
"timex": {:hex, :timex, "3.7.7", "3ed093cae596a410759104d878ad7b38e78b7c2151c6190340835515d4a46b8a", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:gettext, "~> 0.10", [hex: :gettext, repo: "hexpm", optional: false]}, {:tzdata, "~> 1.0", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm", "0ec4b09f25fe311321f9fc04144a7e3affe48eb29481d7a5583849b6c4dfa0a7"},
|
"timex": {:hex, :timex, "3.7.7", "3ed093cae596a410759104d878ad7b38e78b7c2151c6190340835515d4a46b8a", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:gettext, "~> 0.10", [hex: :gettext, repo: "hexpm", optional: false]}, {:tzdata, "~> 1.0", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm", "0ec4b09f25fe311321f9fc04144a7e3affe48eb29481d7a5583849b6c4dfa0a7"},
|
||||||
"toml": {:hex, :toml, "0.7.0", "fbcd773caa937d0c7a02c301a1feea25612720ac3fa1ccb8bfd9d30d822911de", [:mix], [], "hexpm", "0690246a2478c1defd100b0c9b89b4ea280a22be9a7b313a8a058a2408a2fa70"},
|
|
||||||
"trailing_format_plug": {:hex, :trailing_format_plug, "0.0.7", "64b877f912cf7273bed03379936df39894149e35137ac9509117e59866e10e45", [:mix], [{:plug, "> 0.12.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "bd4fde4c15f3e993a999e019d64347489b91b7a9096af68b2bdadd192afa693f"},
|
"trailing_format_plug": {:hex, :trailing_format_plug, "0.0.7", "64b877f912cf7273bed03379936df39894149e35137ac9509117e59866e10e45", [:mix], [{:plug, "> 0.12.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "bd4fde4c15f3e993a999e019d64347489b91b7a9096af68b2bdadd192afa693f"},
|
||||||
"tzdata": {:hex, :tzdata, "1.0.5", "69f1ee029a49afa04ad77801febaf69385f3d3e3d1e4b56b9469025677b89a28", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "55519aa2a99e5d2095c1e61cc74c9be69688f8ab75c27da724eb8279ff402a5a"},
|
"tzdata": {:hex, :tzdata, "1.0.5", "69f1ee029a49afa04ad77801febaf69385f3d3e3d1e4b56b9469025677b89a28", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "55519aa2a99e5d2095c1e61cc74c9be69688f8ab75c27da724eb8279ff402a5a"},
|
||||||
"ueberauth": {:hex, :ueberauth, "0.10.8", "ba78fbcbb27d811a6cd06ad851793aaf7d27c3b30c9e95349c2c362b344cd8f0", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "f2d3172e52821375bccb8460e5fa5cb91cfd60b19b636b6e57e9759b6f8c10c1"},
|
"ueberauth": {:hex, :ueberauth, "0.10.8", "ba78fbcbb27d811a6cd06ad851793aaf7d27c3b30c9e95349c2c362b344cd8f0", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "f2d3172e52821375bccb8460e5fa5cb91cfd60b19b636b6e57e9759b6f8c10c1"},
|
||||||
|
|
@ -157,6 +156,9 @@
|
||||||
"vix": {:hex, :vix, "0.36.0", "3132dc065beda06dab1895a53d8c852d8e6a5bbca375c609435e968b1290e113", [:make, :mix], [{:cc_precompiler, "~> 0.1.4 or ~> 0.2", [hex: :cc_precompiler, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.7.3 or ~> 0.8", [hex: :elixir_make, repo: "hexpm", optional: false]}, {:kino, "~> 0.7", [hex: :kino, repo: "hexpm", optional: true]}], "hexpm", "92f912b4e90c453f92942742105bcdb367ad53406759da251bd2e587e33f4134"},
|
"vix": {:hex, :vix, "0.36.0", "3132dc065beda06dab1895a53d8c852d8e6a5bbca375c609435e968b1290e113", [:make, :mix], [{:cc_precompiler, "~> 0.1.4 or ~> 0.2", [hex: :cc_precompiler, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.7.3 or ~> 0.8", [hex: :elixir_make, repo: "hexpm", optional: false]}, {:kino, "~> 0.7", [hex: :kino, repo: "hexpm", optional: true]}], "hexpm", "92f912b4e90c453f92942742105bcdb367ad53406759da251bd2e587e33f4134"},
|
||||||
"web_push_encryption": {:hex, :web_push_encryption, "0.3.1", "76d0e7375142dfee67391e7690e89f92578889cbcf2879377900b5620ee4708d", [:mix], [{:httpoison, "~> 1.0", [hex: :httpoison, repo: "hexpm", optional: false]}, {:jose, "~> 1.11.1", [hex: :jose, repo: "hexpm", optional: false]}], "hexpm", "4f82b2e57622fb9337559058e8797cb0df7e7c9790793bdc4e40bc895f70e2a2"},
|
"web_push_encryption": {:hex, :web_push_encryption, "0.3.1", "76d0e7375142dfee67391e7690e89f92578889cbcf2879377900b5620ee4708d", [:mix], [{:httpoison, "~> 1.0", [hex: :httpoison, repo: "hexpm", optional: false]}, {:jose, "~> 1.11.1", [hex: :jose, repo: "hexpm", optional: false]}], "hexpm", "4f82b2e57622fb9337559058e8797cb0df7e7c9790793bdc4e40bc895f70e2a2"},
|
||||||
"websock": {:hex, :websock, "0.5.3", "2f69a6ebe810328555b6fe5c831a851f485e303a7c8ce6c5f675abeb20ebdadc", [:mix], [], "hexpm", "6105453d7fac22c712ad66fab1d45abdf049868f253cf719b625151460b8b453"},
|
"websock": {:hex, :websock, "0.5.3", "2f69a6ebe810328555b6fe5c831a851f485e303a7c8ce6c5f675abeb20ebdadc", [:mix], [], "hexpm", "6105453d7fac22c712ad66fab1d45abdf049868f253cf719b625151460b8b453"},
|
||||||
"websock_adapter": {:hex, :websock_adapter, "0.5.8", "3b97dc94e407e2d1fc666b2fb9acf6be81a1798a2602294aac000260a7c4a47d", [:mix], [{:bandit, ">= 0.6.0", [hex: :bandit, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.6", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "315b9a1865552212b5f35140ad194e67ce31af45bcee443d4ecb96b5fd3f3782"},
|
"websock_adapter": {:hex, :websock_adapter, "0.5.9", "43dc3ba6d89ef5dec5b1d0a39698436a1e856d000d84bf31a3149862b01a287f", [:mix], [{:bandit, ">= 0.6.0", [hex: :bandit, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.6", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "5534d5c9adad3c18a0f58a9371220d75a803bf0b9a3d87e6fe072faaeed76a08"},
|
||||||
"websockex": {:hex, :websockex, "0.4.3", "92b7905769c79c6480c02daacaca2ddd49de936d912976a4d3c923723b647bf0", [:mix], [], "hexpm", "95f2e7072b85a3a4cc385602d42115b73ce0b74a9121d0d6dbbf557645ac53e4"},
|
"websockex": {:hex, :websockex, "0.4.3", "92b7905769c79c6480c02daacaca2ddd49de936d912976a4d3c923723b647bf0", [:mix], [], "hexpm", "95f2e7072b85a3a4cc385602d42115b73ce0b74a9121d0d6dbbf557645ac53e4"},
|
||||||
|
"webtransport": {:hex, :webtransport, "0.4.4", "714180b340fbd30a7d666f3f593a612186ff1ba444566b3e8c9ce37fb2950638", [:rebar3], [{:h2, "~> 0.11.0", [hex: :h2, repo: "hexpm", optional: false]}, {:quic, "~> 1.8.0", [hex: :quic, repo: "hexpm", optional: false]}], "hexpm", "80a6641e0f6628938ceee4c9d7a4f6d48a6089890da1ae3288ca8dab13a6005e"},
|
||||||
|
"yamerl": {:hex, :yamerl, "0.10.0", "4ff81fee2f1f6a46f1700c0d880b24d193ddb74bd14ef42cb0bcf46e81ef2f8e", [:rebar3], [], "hexpm", "346adb2963f1051dc837a2364e4acf6eb7d80097c0f53cbdc3046ec8ec4b4e6e"},
|
||||||
|
"yaml_elixir": {:hex, :yaml_elixir, "2.12.2", "9dd1330fb4cd9a36a7b0f502e5b12486eff632792ee4a5f0eba52a4d4ec32c9c", [:mix], [{:yamerl, "~> 0.10", [hex: :yamerl, repo: "hexpm", optional: false]}], "hexpm", "e7c1b10122f973e6558462d51c39026ba0e14afbc6745318e990ea82cfe9e159"},
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ defmodule Pleroma.FormatterTest do
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
use Pleroma.DataCase
|
use Pleroma.DataCase
|
||||||
|
|
||||||
|
import Mock
|
||||||
import Pleroma.Factory
|
import Pleroma.Factory
|
||||||
|
|
||||||
setup_all do
|
setup_all do
|
||||||
|
|
@ -372,4 +373,85 @@ defmodule Pleroma.FormatterTest do
|
||||||
|
|
||||||
assert Formatter.html_escape(text, "text/plain") == expected
|
assert Formatter.html_escape(text, "text/plain") == expected
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "markdown_to_html/2" do
|
||||||
|
test "escapes link attribute values" do
|
||||||
|
markdown = ~s|[click](http://example.com/?a=x" onerror="alert(1))|
|
||||||
|
|
||||||
|
html = Formatter.markdown_to_html(markdown)
|
||||||
|
|
||||||
|
assert html =~ ~s|href="http://example.com/?a=x"|
|
||||||
|
refute html =~ "onerror"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "preserves entities without rendering dangerous raw HTML" do
|
||||||
|
markdown = "safe & sound<script>alert(1)</script>"
|
||||||
|
|
||||||
|
assert Formatter.markdown_to_html(markdown) == "<p>safe & sound</p>"
|
||||||
|
|
||||||
|
assert Formatter.markdown_to_html("<div><script>alert(1)</script>safe</div>") ==
|
||||||
|
"<div>safe</div>"
|
||||||
|
|
||||||
|
assert Formatter.markdown_to_html("<ScRiPt>alert(1)</ScRiPt>") == ""
|
||||||
|
assert Formatter.markdown_to_html("<BR>") == "<BR>"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "escapes dangerous HTML inside code" do
|
||||||
|
markdown = "`<script>&</script>`"
|
||||||
|
|
||||||
|
assert Formatter.markdown_to_html(markdown) ==
|
||||||
|
~s(<p><code class="inline"><script>&amp;</script></code></p>)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "rejects entity-encoded URL delimiters" do
|
||||||
|
markdown = "[click](http://example.com/?a=x"onerror=alert(1))"
|
||||||
|
|
||||||
|
html = Formatter.markdown_to_html(markdown)
|
||||||
|
|
||||||
|
assert html =~ ~s|href="http://example.com/?a=x"|
|
||||||
|
refute html =~ "onerror"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "strips email autolinks without changing explicit mailto links" do
|
||||||
|
opts = %{strip_email_autolinks: true}
|
||||||
|
|
||||||
|
assert Formatter.markdown_to_html("<foo@example.com>", opts) ==
|
||||||
|
"<p>foo@example.com</p>"
|
||||||
|
|
||||||
|
assert Formatter.markdown_to_html("[email](mailto:foo@example.com)", opts) ==
|
||||||
|
~s(<p><a href="mailto:foo@example.com">email</a></p>)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "handles malformed Markdown that crashed older EarmarkParser releases" do
|
||||||
|
assert Formatter.markdown_to_html("\\[{:}") == "<p>[{:}</p>"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "handles a malformed Markdown regression corpus" do
|
||||||
|
markdown_samples = [
|
||||||
|
"- hello\n{:.claass .klass}",
|
||||||
|
"[link](\"",
|
||||||
|
"<script",
|
||||||
|
"</style>",
|
||||||
|
"~~~",
|
||||||
|
"<![CDATA[",
|
||||||
|
<<0>>
|
||||||
|
]
|
||||||
|
|
||||||
|
for markdown <- markdown_samples do
|
||||||
|
html = Formatter.markdown_to_html(markdown)
|
||||||
|
|
||||||
|
assert is_binary(html)
|
||||||
|
refute html =~ <<0>>
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
test "falls back to escaped text when parsing fails" do
|
||||||
|
markdown = "<script>alert(1)</script>\nnext"
|
||||||
|
|
||||||
|
with_mock EarmarkParser, as_ast: fn _text, _opts -> raise "parser failed" end do
|
||||||
|
assert Formatter.markdown_to_html(markdown, %{breaks: true}) ==
|
||||||
|
"<p><script>alert(1)</script><br>next</p>"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,13 @@ defmodule Pleroma.HTTP.AdapterHelper.HackneyTest do
|
||||||
assert opts[:with_body] == true
|
assert opts[:with_body] == true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "does not allow connection options to enable Hackney redirects", %{uri: uri} do
|
||||||
|
opts = Hackney.options([follow_redirect: true, force_redirect: true], uri)
|
||||||
|
|
||||||
|
assert opts[:follow_redirect] == false
|
||||||
|
assert opts[:force_redirect] == false
|
||||||
|
end
|
||||||
|
|
||||||
test "add proxy and opts from config", %{uri: uri} do
|
test "add proxy and opts from config", %{uri: uri} do
|
||||||
opts = Hackney.options([proxy: "localhost:8123"], uri)
|
opts = Hackney.options([proxy: "localhost:8123"], uri)
|
||||||
|
|
||||||
|
|
|
||||||
201
test/pleroma/http/hackney_conn_leak_test.exs
Normal file
201
test/pleroma/http/hackney_conn_leak_test.exs
Normal file
|
|
@ -0,0 +1,201 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
# Regression guard for the Hackney dependency, not for Pleroma code.
|
||||||
|
#
|
||||||
|
# Hackney 4 leaks a `hackney_conn` gen_statem for an unpooled request that ends
|
||||||
|
# without a reusable socket: a body cut short by the peer, a bodyless 204/304,
|
||||||
|
# or a failed body read. `read_full_body/2` hands back `socket = undefined`, so
|
||||||
|
# the connection goes straight to its `closed` state, bypassing the reuse check;
|
||||||
|
# `closed(enter)` arms a stop timer only for pooled connections, and an unpooled
|
||||||
|
# one is owned by `hackney_conn_sup`, so the owner-DOWN cleanup never fires
|
||||||
|
# either. The process parks forever holding every refc binary it read.
|
||||||
|
#
|
||||||
|
# Pleroma cannot work around this: since 4.0 Hackney ignores `with_body` and
|
||||||
|
# always returns a fully read binary, so a synchronous request never yields a
|
||||||
|
# handle to close, and a truncated read still reports `{:ok, body}`.
|
||||||
|
#
|
||||||
|
# Media proxy warming hits this whenever an attachment fetch is cut short and
|
||||||
|
# the outbound proxy is used as a CONNECT tunnel, since tunnels are never
|
||||||
|
# pooled. That is how it was found.
|
||||||
|
#
|
||||||
|
# Fixed upstream by https://github.com/benoitc/hackney/pull/919; mix.exs pins
|
||||||
|
# that commit until it lands in a release. Should this start failing, the pin
|
||||||
|
# has been moved to a version without the fix.
|
||||||
|
#
|
||||||
|
# https://github.com/benoitc/hackney/issues/918
|
||||||
|
defmodule Pleroma.HTTP.HackneyConnLeakTest do
|
||||||
|
use ExUnit.Case, async: false
|
||||||
|
|
||||||
|
alias Pleroma.HTTP.AdapterHelper.Hackney, as: HackneyAdapterHelper
|
||||||
|
|
||||||
|
@sent_bytes 64 * 1024
|
||||||
|
@announced_bytes 100_000_000
|
||||||
|
|
||||||
|
setup do
|
||||||
|
{:ok, _} = Application.ensure_all_started(:hackney)
|
||||||
|
|
||||||
|
{:ok, server} = start_server()
|
||||||
|
on_exit(fn -> stop_server(server) end)
|
||||||
|
|
||||||
|
{:ok, server: server}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "an unpooled request with a truncated body does not leak a connection process", %{
|
||||||
|
server: server
|
||||||
|
} do
|
||||||
|
before = conn_pids()
|
||||||
|
|
||||||
|
# pool: false takes hackney's connect_direct/4 path, the same unpooled shape
|
||||||
|
# a CONNECT tunnel gets. A pooled connection is reaped by the pool's own
|
||||||
|
# grace timer and would not show the leak.
|
||||||
|
assert {:ok, %Tesla.Env{status: 200, body: body}} =
|
||||||
|
request(server, pool: false)
|
||||||
|
|
||||||
|
# The short read still reports success, which is why nothing downstream
|
||||||
|
# knows to clean up.
|
||||||
|
assert byte_size(body) == @sent_bytes
|
||||||
|
|
||||||
|
leaked = wait_for_cleanup(before)
|
||||||
|
|
||||||
|
assert leaked == [],
|
||||||
|
"leaked #{length(leaked)} hackney_conn process(es): " <>
|
||||||
|
inspect(Enum.map(leaked, &describe/1))
|
||||||
|
end
|
||||||
|
|
||||||
|
# A control rather than a guard: a pooled connection is stopped by the pool's
|
||||||
|
# own grace timer both before and after the fix. It is here to show the
|
||||||
|
# unpooled assertion above is measuring the right thing.
|
||||||
|
test "a pooled request with a truncated body does not leak either", %{server: server} do
|
||||||
|
before = conn_pids()
|
||||||
|
|
||||||
|
assert {:ok, %Tesla.Env{status: 200}} = request(server, pool: :media)
|
||||||
|
|
||||||
|
assert wait_for_cleanup(before) == []
|
||||||
|
end
|
||||||
|
|
||||||
|
defp request(server, extra_opts) do
|
||||||
|
url = "#{server.base_url}/truncated"
|
||||||
|
|
||||||
|
adapter_opts =
|
||||||
|
HackneyAdapterHelper.options([proxy: false] ++ extra_opts, URI.parse(url))
|
||||||
|
|
||||||
|
client = Tesla.client([], Tesla.Adapter.Hackney)
|
||||||
|
|
||||||
|
Tesla.request(client, method: :get, url: url, opts: [adapter: adapter_opts])
|
||||||
|
end
|
||||||
|
|
||||||
|
defp conn_pids do
|
||||||
|
:hackney_conn_sup
|
||||||
|
|> Process.whereis()
|
||||||
|
|> case do
|
||||||
|
nil ->
|
||||||
|
[]
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
for {_, pid, _, _} <- :supervisor.which_children(:hackney_conn_sup), is_pid(pid), do: pid
|
||||||
|
end
|
||||||
|
|> MapSet.new()
|
||||||
|
end
|
||||||
|
|
||||||
|
# Only connections this test created matter; the pool may add unrelated ones.
|
||||||
|
# A pooled connection gets a brief grace window before it stops, so poll
|
||||||
|
# rather than sampling once.
|
||||||
|
defp wait_for_cleanup(before, attempts \\ 40) do
|
||||||
|
leaked =
|
||||||
|
conn_pids()
|
||||||
|
|> Enum.reject(&MapSet.member?(before, &1))
|
||||||
|
|> Enum.filter(&Process.alive?/1)
|
||||||
|
|
||||||
|
cond do
|
||||||
|
leaked == [] -> []
|
||||||
|
attempts == 0 -> leaked
|
||||||
|
true -> Process.sleep(50) && wait_for_cleanup(before, attempts - 1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp describe(pid) do
|
||||||
|
state =
|
||||||
|
try do
|
||||||
|
pid |> :sys.get_state(200) |> elem(0)
|
||||||
|
rescue
|
||||||
|
_ -> :unknown
|
||||||
|
catch
|
||||||
|
:exit, _ -> :dead
|
||||||
|
end
|
||||||
|
|
||||||
|
binary =
|
||||||
|
case Process.info(pid, :binary) do
|
||||||
|
{:binary, refs} -> Enum.reduce(refs, 0, fn ref, acc -> acc + elem(ref, 1) end)
|
||||||
|
_ -> 0
|
||||||
|
end
|
||||||
|
|
||||||
|
%{pid: pid, state: state, binary_bytes: binary}
|
||||||
|
end
|
||||||
|
|
||||||
|
# Announces a body far larger than it sends, then closes: what an upstream
|
||||||
|
# aborting mid-stream looks like to the client.
|
||||||
|
defp start_server do
|
||||||
|
{:ok, listener} =
|
||||||
|
:gen_tcp.listen(0, [
|
||||||
|
:binary,
|
||||||
|
active: false,
|
||||||
|
packet: :raw,
|
||||||
|
reuseaddr: true,
|
||||||
|
ip: {127, 0, 0, 1}
|
||||||
|
])
|
||||||
|
|
||||||
|
{:ok, {{127, 0, 0, 1}, port}} = :inet.sockname(listener)
|
||||||
|
{:ok, acceptor} = Task.start(fn -> accept_loop(listener) end)
|
||||||
|
|
||||||
|
{:ok, %{listener: listener, acceptor: acceptor, base_url: "http://127.0.0.1:#{port}"}}
|
||||||
|
end
|
||||||
|
|
||||||
|
# Closing the listener makes the blocked accept/1 return {:error, :closed},
|
||||||
|
# which ends the loop.
|
||||||
|
defp stop_server(%{listener: listener, acceptor: acceptor}) do
|
||||||
|
:ok = :gen_tcp.close(listener)
|
||||||
|
if Process.alive?(acceptor), do: Process.exit(acceptor, :kill)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp accept_loop(listener) do
|
||||||
|
case :gen_tcp.accept(listener) do
|
||||||
|
{:ok, socket} ->
|
||||||
|
serve(socket)
|
||||||
|
accept_loop(listener)
|
||||||
|
|
||||||
|
{:error, _} ->
|
||||||
|
:ok
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp serve(socket) do
|
||||||
|
with {:ok, _data} <- recv_headers(socket) do
|
||||||
|
:gen_tcp.send(socket, [
|
||||||
|
"HTTP/1.1 200 OK\r\n",
|
||||||
|
"Content-Type: application/octet-stream\r\n",
|
||||||
|
"Content-Length: #{@announced_bytes}\r\n\r\n",
|
||||||
|
:binary.copy("x", @sent_bytes)
|
||||||
|
])
|
||||||
|
end
|
||||||
|
|
||||||
|
:gen_tcp.close(socket)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp recv_headers(socket, acc \\ <<>>) do
|
||||||
|
case :gen_tcp.recv(socket, 0, 1_000) do
|
||||||
|
{:ok, data} ->
|
||||||
|
acc = acc <> data
|
||||||
|
|
||||||
|
cond do
|
||||||
|
:binary.match(acc, "\r\n\r\n") != :nomatch -> {:ok, acc}
|
||||||
|
byte_size(acc) > 8_192 -> {:error, :too_large}
|
||||||
|
true -> recv_headers(socket, acc)
|
||||||
|
end
|
||||||
|
|
||||||
|
{:error, _} = error ->
|
||||||
|
error
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -19,30 +19,6 @@ defmodule Pleroma.HTTP.HackneyFollowRedirectRegressionTest do
|
||||||
{:ok, tls_server: tls_server, proxy: proxy}
|
{:ok, tls_server: tls_server, proxy: proxy}
|
||||||
end
|
end
|
||||||
|
|
||||||
test "hackney follow_redirect crashes behind CONNECT proxy on relative redirects", %{
|
|
||||||
tls_server: tls_server,
|
|
||||||
proxy: proxy
|
|
||||||
} do
|
|
||||||
url = "#{tls_server.base_url}/redirect"
|
|
||||||
|
|
||||||
opts = [
|
|
||||||
pool: :media,
|
|
||||||
proxy: proxy.proxy_url,
|
|
||||||
insecure: true,
|
|
||||||
connect_timeout: 1_000,
|
|
||||||
recv_timeout: 1_000,
|
|
||||||
follow_redirect: true,
|
|
||||||
force_redirect: true
|
|
||||||
]
|
|
||||||
|
|
||||||
{pid, ref} = spawn_monitor(fn -> :hackney.request(:get, url, [], <<>>, opts) end)
|
|
||||||
|
|
||||||
assert_receive {:DOWN, ^ref, :process, ^pid, reason}, 5_000
|
|
||||||
|
|
||||||
assert match?({%FunctionClauseError{}, _}, reason) or match?(%FunctionClauseError{}, reason) or
|
|
||||||
match?({:function_clause, _}, reason)
|
|
||||||
end
|
|
||||||
|
|
||||||
test "redirects work via proxy when hackney follow_redirect is disabled", %{
|
test "redirects work via proxy when hackney follow_redirect is disabled", %{
|
||||||
tls_server: tls_server,
|
tls_server: tls_server,
|
||||||
proxy: proxy
|
proxy: proxy
|
||||||
|
|
@ -52,7 +28,7 @@ defmodule Pleroma.HTTP.HackneyFollowRedirectRegressionTest do
|
||||||
adapter_opts = [
|
adapter_opts = [
|
||||||
pool: :media,
|
pool: :media,
|
||||||
proxy: proxy.proxy_url,
|
proxy: proxy.proxy_url,
|
||||||
insecure: true,
|
ssl_options: tls_client_options(tls_server),
|
||||||
connect_timeout: 1_000,
|
connect_timeout: 1_000,
|
||||||
recv_timeout: 1_000,
|
recv_timeout: 1_000,
|
||||||
follow_redirect: false,
|
follow_redirect: false,
|
||||||
|
|
@ -75,7 +51,7 @@ defmodule Pleroma.HTTP.HackneyFollowRedirectRegressionTest do
|
||||||
opts = [
|
opts = [
|
||||||
pool: :media,
|
pool: :media,
|
||||||
proxy: proxy.proxy_url,
|
proxy: proxy.proxy_url,
|
||||||
insecure: true,
|
ssl_options: tls_client_options(tls_server),
|
||||||
connect_timeout: 1_000,
|
connect_timeout: 1_000,
|
||||||
recv_timeout: 1_000,
|
recv_timeout: 1_000,
|
||||||
follow_redirect: true
|
follow_redirect: true
|
||||||
|
|
@ -97,7 +73,7 @@ defmodule Pleroma.HTTP.HackneyFollowRedirectRegressionTest do
|
||||||
opts = [
|
opts = [
|
||||||
pool: :media,
|
pool: :media,
|
||||||
proxy: proxy.proxy_url,
|
proxy: proxy.proxy_url,
|
||||||
insecure: true,
|
ssl_options: tls_client_options(tls_server),
|
||||||
connect_timeout: 1_000,
|
connect_timeout: 1_000,
|
||||||
recv_timeout: 1_000,
|
recv_timeout: 1_000,
|
||||||
follow_redirect: true
|
follow_redirect: true
|
||||||
|
|
@ -119,7 +95,7 @@ defmodule Pleroma.HTTP.HackneyFollowRedirectRegressionTest do
|
||||||
opts = [
|
opts = [
|
||||||
pool: :media,
|
pool: :media,
|
||||||
proxy: proxy.proxy_url,
|
proxy: proxy.proxy_url,
|
||||||
insecure: true,
|
ssl_options: tls_client_options(tls_server),
|
||||||
connect_timeout: 1_000,
|
connect_timeout: 1_000,
|
||||||
recv_timeout: 1_000,
|
recv_timeout: 1_000,
|
||||||
follow_redirect: true
|
follow_redirect: true
|
||||||
|
|
@ -131,6 +107,133 @@ defmodule Pleroma.HTTP.HackneyFollowRedirectRegressionTest do
|
||||||
Pleroma.ReverseProxy.Client.Hackney.close(ref)
|
Pleroma.ReverseProxy.Client.Hackney.close(ref)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "hackney reverse proxy cancels timed out asynchronous requests", %{
|
||||||
|
tls_server: tls_server,
|
||||||
|
proxy: proxy
|
||||||
|
} do
|
||||||
|
url = "#{tls_server.base_url}/delayed"
|
||||||
|
|
||||||
|
opts = [
|
||||||
|
pool: :media,
|
||||||
|
proxy: proxy.proxy_url,
|
||||||
|
ssl_options: tls_client_options(tls_server),
|
||||||
|
connect_timeout: 1_000,
|
||||||
|
recv_timeout: 10,
|
||||||
|
follow_redirect: false
|
||||||
|
]
|
||||||
|
|
||||||
|
assert {:error, :timeout} =
|
||||||
|
Pleroma.ReverseProxy.Client.Hackney.request(:get, url, [], "", opts)
|
||||||
|
|
||||||
|
refute_receive {:hackney_response, _, _}, 150
|
||||||
|
end
|
||||||
|
|
||||||
|
test "hackney reverse proxy normalizes asynchronous connection errors", %{
|
||||||
|
tls_server: tls_server,
|
||||||
|
proxy: proxy
|
||||||
|
} do
|
||||||
|
url = "#{tls_server.base_url}/closed"
|
||||||
|
|
||||||
|
opts = [
|
||||||
|
pool: :media,
|
||||||
|
proxy: proxy.proxy_url,
|
||||||
|
ssl_options: tls_client_options(tls_server),
|
||||||
|
connect_timeout: 1_000,
|
||||||
|
recv_timeout: 1_000,
|
||||||
|
follow_redirect: false
|
||||||
|
]
|
||||||
|
|
||||||
|
assert {:error, :closed} =
|
||||||
|
Pleroma.ReverseProxy.Client.Hackney.request(:get, url, [], "", opts)
|
||||||
|
end
|
||||||
|
|
||||||
|
# A pooled connection serving a bodyless response is checked back in by hackney
|
||||||
|
# at header time, so stopping that ref afterwards would evict a connection
|
||||||
|
# another request may already hold. These go direct rather than through the
|
||||||
|
# CONNECT proxy, because a proxy tunnel is never pooled.
|
||||||
|
test "hackney reverse proxy marks a pooled bodyless response as already released",
|
||||||
|
%{tls_server: tls_server} do
|
||||||
|
url = "#{tls_server.base_url}/no_content"
|
||||||
|
|
||||||
|
assert {:ok, 204, _headers, client} =
|
||||||
|
Pleroma.ReverseProxy.Client.Hackney.request(
|
||||||
|
:get,
|
||||||
|
url,
|
||||||
|
[],
|
||||||
|
"",
|
||||||
|
direct_opts(tls_server)
|
||||||
|
)
|
||||||
|
|
||||||
|
assert %{released: true} = client
|
||||||
|
assert :done = Pleroma.ReverseProxy.Client.Hackney.stream_body(client)
|
||||||
|
assert :ok = Pleroma.ReverseProxy.Client.Hackney.close(client)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "hackney reverse proxy still owns a response that carries a body",
|
||||||
|
%{tls_server: tls_server} do
|
||||||
|
url = "#{tls_server.base_url}/final"
|
||||||
|
|
||||||
|
assert {:ok, 200, _headers, client} =
|
||||||
|
Pleroma.ReverseProxy.Client.Hackney.request(
|
||||||
|
:get,
|
||||||
|
url,
|
||||||
|
[],
|
||||||
|
"",
|
||||||
|
direct_opts(tls_server)
|
||||||
|
)
|
||||||
|
|
||||||
|
refute Map.has_key?(client, :released)
|
||||||
|
assert "ok" == collect_body(client)
|
||||||
|
|
||||||
|
# Streaming to the end must stop the conn: hackney only parks it, and this
|
||||||
|
# one has no idle timeout and no live owner monitor to reap it.
|
||||||
|
refute Process.alive?(client.ref)
|
||||||
|
end
|
||||||
|
|
||||||
|
# An unpooled connection is never checked in and parks forever with no idle
|
||||||
|
# timeout, so it stays ours to stop even when the response has no body.
|
||||||
|
test "hackney reverse proxy still owns a bodyless response over a proxy tunnel", %{
|
||||||
|
tls_server: tls_server,
|
||||||
|
proxy: proxy
|
||||||
|
} do
|
||||||
|
url = "#{tls_server.base_url}/no_content"
|
||||||
|
|
||||||
|
opts = Keyword.put(direct_opts(tls_server), :proxy, proxy.proxy_url)
|
||||||
|
|
||||||
|
assert {:ok, 204, _headers, client} =
|
||||||
|
Pleroma.ReverseProxy.Client.Hackney.request(:get, url, [], "", opts)
|
||||||
|
|
||||||
|
refute Map.has_key?(client, :released)
|
||||||
|
Pleroma.ReverseProxy.Client.Hackney.close(client)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "hackney reverse proxy still owns a bodyless response when pooling is off", %{
|
||||||
|
tls_server: tls_server
|
||||||
|
} do
|
||||||
|
url = "#{tls_server.base_url}/no_content"
|
||||||
|
|
||||||
|
opts = Keyword.put(direct_opts(tls_server), :pool, false)
|
||||||
|
|
||||||
|
assert {:ok, 204, _headers, client} =
|
||||||
|
Pleroma.ReverseProxy.Client.Hackney.request(:get, url, [], "", opts)
|
||||||
|
|
||||||
|
refute Map.has_key?(client, :released)
|
||||||
|
Pleroma.ReverseProxy.Client.Hackney.close(client)
|
||||||
|
end
|
||||||
|
|
||||||
|
# proxy: false so a proxy in the build environment cannot turn these into
|
||||||
|
# tunnelled requests, which are never pooled and would defeat the point.
|
||||||
|
defp direct_opts(tls_server) do
|
||||||
|
[
|
||||||
|
pool: :media,
|
||||||
|
proxy: false,
|
||||||
|
ssl_options: tls_client_options(tls_server),
|
||||||
|
connect_timeout: 1_000,
|
||||||
|
recv_timeout: 1_000,
|
||||||
|
follow_redirect: false
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
defp collect_body(ref, acc \\ "") do
|
defp collect_body(ref, acc \\ "") do
|
||||||
case Pleroma.ReverseProxy.Client.Hackney.stream_body(ref) do
|
case Pleroma.ReverseProxy.Client.Hackney.stream_body(ref) do
|
||||||
:done -> acc
|
:done -> acc
|
||||||
|
|
@ -161,7 +264,13 @@ defmodule Pleroma.HTTP.HackneyFollowRedirectRegressionTest do
|
||||||
accept_tls_loop(listener)
|
accept_tls_loop(listener)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
{:ok, %{listener: listener, acceptor: acceptor, base_url: "https://127.0.0.1:#{port}"}}
|
{:ok,
|
||||||
|
%{
|
||||||
|
listener: listener,
|
||||||
|
acceptor: acceptor,
|
||||||
|
certfile: certfile,
|
||||||
|
base_url: "https://127.0.0.1:#{port}"
|
||||||
|
}}
|
||||||
end
|
end
|
||||||
|
|
||||||
defp stop_tls_redirect_server(%{listener: listener, acceptor: acceptor}) do
|
defp stop_tls_redirect_server(%{listener: listener, acceptor: acceptor}) do
|
||||||
|
|
@ -191,6 +300,16 @@ defmodule Pleroma.HTTP.HackneyFollowRedirectRegressionTest do
|
||||||
{:ok, data} <- recv_ssl_headers(ssl_socket),
|
{:ok, data} <- recv_ssl_headers(ssl_socket),
|
||||||
{:ok, path} <- parse_path(data) do
|
{:ok, path} <- parse_path(data) do
|
||||||
case path do
|
case path do
|
||||||
|
"/closed" ->
|
||||||
|
:ok
|
||||||
|
|
||||||
|
"/no_content" ->
|
||||||
|
send_ssl_response(ssl_socket, 204, "No Content", [], "")
|
||||||
|
|
||||||
|
"/delayed" ->
|
||||||
|
Process.sleep(100)
|
||||||
|
send_ssl_response(ssl_socket, 200, "OK", [], "ok")
|
||||||
|
|
||||||
"/infinite_redirect" ->
|
"/infinite_redirect" ->
|
||||||
send_ssl_response(ssl_socket, 302, "Found", [{"Location", "/infinite_redirect"}], "")
|
send_ssl_response(ssl_socket, 302, "Found", [{"Location", "/infinite_redirect"}], "")
|
||||||
|
|
||||||
|
|
@ -210,11 +329,17 @@ defmodule Pleroma.HTTP.HackneyFollowRedirectRegressionTest do
|
||||||
:ssl.close(ssl_socket)
|
:ssl.close(ssl_socket)
|
||||||
else
|
else
|
||||||
_ ->
|
_ ->
|
||||||
_ = :gen_tcp.close(tcp_socket)
|
_ = :ssl.close(tcp_socket)
|
||||||
:ok
|
:ok
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp tls_client_options(_tls_server) do
|
||||||
|
[server_name_indication: ~c"localhost", verify_fun: {&accept_test_certificate/3, []}]
|
||||||
|
end
|
||||||
|
|
||||||
|
defp accept_test_certificate(_cert, _event, state), do: {:valid, state}
|
||||||
|
|
||||||
defp recv_ssl_headers(socket, acc \\ <<>>) do
|
defp recv_ssl_headers(socket, acc \\ <<>>) do
|
||||||
case :ssl.recv(socket, 0, 1_000) do
|
case :ssl.recv(socket, 0, 1_000) do
|
||||||
{:ok, data} ->
|
{:ok, data} ->
|
||||||
|
|
|
||||||
165
test/pleroma/http/hackney_max_body_regression_test.exs
Normal file
165
test/pleroma/http/hackney_max_body_regression_test.exs
Normal file
|
|
@ -0,0 +1,165 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
# Hackney 4 ignores `with_body` and always returns a fully read binary, so
|
||||||
|
# `Tesla.Adapter.Hackney` never reaches the clause that honours `:max_body`.
|
||||||
|
# The limit is enforced by Pleroma.Tesla.Middleware.MaxBody instead; these
|
||||||
|
# exercise it against the real adapter.
|
||||||
|
defmodule Pleroma.HTTP.HackneyMaxBodyRegressionTest do
|
||||||
|
use ExUnit.Case, async: false
|
||||||
|
|
||||||
|
alias Pleroma.HTTP.AdapterHelper.Hackney, as: HackneyAdapterHelper
|
||||||
|
|
||||||
|
@body_bytes 64 * 1024
|
||||||
|
|
||||||
|
setup do
|
||||||
|
{:ok, _} = Application.ensure_all_started(:hackney)
|
||||||
|
|
||||||
|
{:ok, server} = start_server()
|
||||||
|
on_exit(fn -> stop_server(server) end)
|
||||||
|
|
||||||
|
{:ok, server: server}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp request(url, extra_opts) do
|
||||||
|
adapter_opts =
|
||||||
|
HackneyAdapterHelper.options(
|
||||||
|
[pool: :media, proxy: false] ++ extra_opts,
|
||||||
|
URI.parse(url)
|
||||||
|
)
|
||||||
|
|
||||||
|
client =
|
||||||
|
Tesla.client(
|
||||||
|
[Tesla.Middleware.FollowRedirects, Pleroma.Tesla.Middleware.MaxBody],
|
||||||
|
Tesla.Adapter.Hackney
|
||||||
|
)
|
||||||
|
|
||||||
|
Tesla.request(client, method: :get, url: url, opts: [adapter: adapter_opts])
|
||||||
|
end
|
||||||
|
|
||||||
|
test "a body over max_body is refused", %{server: server} do
|
||||||
|
assert {:error, :body_too_large} =
|
||||||
|
request("#{server.base_url}/big", max_body: 1024)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Without a Content-Length the header pre-check cannot fire, so this is the
|
||||||
|
# case that proves hackney's fully-read binary is measured.
|
||||||
|
test "a chunked body over max_body is refused on size alone", %{server: server} do
|
||||||
|
assert {:error, :body_too_large} =
|
||||||
|
request("#{server.base_url}/chunked", max_body: 1024)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "a body under max_body is returned", %{server: server} do
|
||||||
|
assert {:ok, %Tesla.Env{status: 200, body: body}} =
|
||||||
|
request("#{server.base_url}/big", max_body: @body_bytes * 2)
|
||||||
|
|
||||||
|
assert byte_size(body) == @body_bytes
|
||||||
|
end
|
||||||
|
|
||||||
|
test "without max_body the body is returned whatever its size", %{server: server} do
|
||||||
|
assert {:ok, %Tesla.Env{status: 200, body: body}} = request("#{server.base_url}/big", [])
|
||||||
|
assert byte_size(body) == @body_bytes
|
||||||
|
end
|
||||||
|
|
||||||
|
# Pins why the middleware is needed. Tesla.Adapter.Hackney only honours
|
||||||
|
# :max_body on the branch where hackney hands back a ref, which 4.x never
|
||||||
|
# does for a synchronous request. Should this ever start failing, hackney or
|
||||||
|
# Tesla have regained streaming and the middleware can go.
|
||||||
|
test "the bare adapter does not enforce max_body", %{server: server} do
|
||||||
|
url = "#{server.base_url}/big"
|
||||||
|
|
||||||
|
adapter_opts =
|
||||||
|
HackneyAdapterHelper.options(
|
||||||
|
[pool: :media, proxy: false, max_body: 1024],
|
||||||
|
URI.parse(url)
|
||||||
|
)
|
||||||
|
|
||||||
|
client = Tesla.client([], Tesla.Adapter.Hackney)
|
||||||
|
|
||||||
|
assert {:ok, %Tesla.Env{status: 200, body: body}} =
|
||||||
|
Tesla.request(client, method: :get, url: url, opts: [adapter: adapter_opts])
|
||||||
|
|
||||||
|
assert byte_size(body) == @body_bytes
|
||||||
|
end
|
||||||
|
|
||||||
|
defp start_server do
|
||||||
|
{:ok, listener} =
|
||||||
|
:gen_tcp.listen(0, [
|
||||||
|
:binary,
|
||||||
|
active: false,
|
||||||
|
packet: :raw,
|
||||||
|
reuseaddr: true,
|
||||||
|
ip: {127, 0, 0, 1}
|
||||||
|
])
|
||||||
|
|
||||||
|
{:ok, {{127, 0, 0, 1}, port}} = :inet.sockname(listener)
|
||||||
|
{:ok, acceptor} = Task.start(fn -> accept_loop(listener) end)
|
||||||
|
|
||||||
|
{:ok, %{listener: listener, acceptor: acceptor, base_url: "http://127.0.0.1:#{port}"}}
|
||||||
|
end
|
||||||
|
|
||||||
|
# Closing the listener makes the blocked accept/1 return {:error, :closed},
|
||||||
|
# which ends the loop.
|
||||||
|
defp stop_server(%{listener: listener, acceptor: acceptor}) do
|
||||||
|
:ok = :gen_tcp.close(listener)
|
||||||
|
if Process.alive?(acceptor), do: Process.exit(acceptor, :kill)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp accept_loop(listener) do
|
||||||
|
case :gen_tcp.accept(listener) do
|
||||||
|
{:ok, socket} ->
|
||||||
|
serve(socket)
|
||||||
|
accept_loop(listener)
|
||||||
|
|
||||||
|
{:error, _} ->
|
||||||
|
:ok
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp serve(socket) do
|
||||||
|
with {:ok, data} <- recv_headers(socket) do
|
||||||
|
body = :binary.copy("x", @body_bytes)
|
||||||
|
|
||||||
|
if String.contains?(data, "/chunked") do
|
||||||
|
# No Content-Length, so only the body-size check can catch this.
|
||||||
|
:gen_tcp.send(socket, [
|
||||||
|
"HTTP/1.1 200 OK\r\n",
|
||||||
|
"Content-Type: text/plain\r\n",
|
||||||
|
"Transfer-Encoding: chunked\r\n",
|
||||||
|
"Connection: close\r\n\r\n",
|
||||||
|
:erlang.integer_to_list(byte_size(body), 16),
|
||||||
|
"\r\n",
|
||||||
|
body,
|
||||||
|
"\r\n0\r\n\r\n"
|
||||||
|
])
|
||||||
|
else
|
||||||
|
:gen_tcp.send(socket, [
|
||||||
|
"HTTP/1.1 200 OK\r\n",
|
||||||
|
"Content-Type: text/plain\r\n",
|
||||||
|
"Content-Length: #{byte_size(body)}\r\n",
|
||||||
|
"Connection: close\r\n\r\n",
|
||||||
|
body
|
||||||
|
])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
:gen_tcp.close(socket)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp recv_headers(socket, acc \\ <<>>) do
|
||||||
|
case :gen_tcp.recv(socket, 0, 1_000) do
|
||||||
|
{:ok, data} ->
|
||||||
|
acc = acc <> data
|
||||||
|
|
||||||
|
cond do
|
||||||
|
:binary.match(acc, "\r\n\r\n") != :nomatch -> {:ok, acc}
|
||||||
|
byte_size(acc) > 8_192 -> {:error, :too_large}
|
||||||
|
true -> recv_headers(socket, acc)
|
||||||
|
end
|
||||||
|
|
||||||
|
{:error, _} = error ->
|
||||||
|
error
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -22,7 +22,7 @@ defmodule Pleroma.HTTP.HackneyRedirectRegressionTest do
|
||||||
|
|
||||||
adapter_opts =
|
adapter_opts =
|
||||||
HackneyAdapterHelper.options(
|
HackneyAdapterHelper.options(
|
||||||
[pool: :media, follow_redirect: false, no_proxy_env: true],
|
[pool: :media, follow_redirect: false, proxy: false],
|
||||||
uri
|
uri
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,21 @@ defmodule Pleroma.ReverseProxyTest do
|
||||||
|
|
||||||
setup :verify_on_exit!
|
setup :verify_on_exit!
|
||||||
|
|
||||||
|
# Every path that obtains a client ref releases it, so tests that do not assert
|
||||||
|
# on the release still need close/1 to be callable. Recording each call lets
|
||||||
|
# the tests below pin *exactly* once: a Mox `expect` would silently fall
|
||||||
|
# through to this stub on a second call.
|
||||||
|
setup do
|
||||||
|
test_pid = self()
|
||||||
|
|
||||||
|
stub(ClientMock, :close, fn client ->
|
||||||
|
send(test_pid, {:closed, client})
|
||||||
|
:ok
|
||||||
|
end)
|
||||||
|
|
||||||
|
:ok
|
||||||
|
end
|
||||||
|
|
||||||
defp start_bandit(url, opts \\ []) do
|
defp start_bandit(url, opts \\ []) do
|
||||||
start_supervised!(
|
start_supervised!(
|
||||||
{Bandit,
|
{Bandit,
|
||||||
|
|
@ -183,6 +198,100 @@ defmodule Pleroma.ReverseProxyTest do
|
||||||
assert conn.status == 200
|
assert conn.status == 200
|
||||||
assert Cachex.get(:failed_proxy_url_cache, url) == {:ok, nil}
|
assert Cachex.get(:failed_proxy_url_cache, url) == {:ok, nil}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "uses max_read_duration as the default HTTP receive timeout", %{conn: conn} do
|
||||||
|
ClientMock
|
||||||
|
|> expect(:request, fn :get, "/timeout", _, _, opts ->
|
||||||
|
assert opts[:recv_timeout] == 123
|
||||||
|
{:error, :timeout}
|
||||||
|
end)
|
||||||
|
|
||||||
|
capture_log(fn -> ReverseProxy.call(conn, "/timeout", max_read_duration: 123) end)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "preserves an explicit HTTP receive timeout", %{conn: conn} do
|
||||||
|
ClientMock
|
||||||
|
|> expect(:request, fn :get, "/timeout", _, _, opts ->
|
||||||
|
assert opts[:recv_timeout] == 456
|
||||||
|
{:error, :timeout}
|
||||||
|
end)
|
||||||
|
|
||||||
|
capture_log(fn ->
|
||||||
|
ReverseProxy.call(conn, "/timeout", max_read_duration: 123, http: [recv_timeout: 456])
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "keeps unlimited read durations unlimited", %{conn: conn} do
|
||||||
|
for {max_read_duration, url} <- [{0, "/zero-timeout"}, {:infinity, "/infinite-timeout"}] do
|
||||||
|
ClientMock
|
||||||
|
|> expect(:request, fn :get, ^url, _, _, opts ->
|
||||||
|
assert opts[:recv_timeout] == :infinity
|
||||||
|
{:error, :timeout}
|
||||||
|
end)
|
||||||
|
|
||||||
|
capture_log(fn ->
|
||||||
|
ReverseProxy.call(conn, url, max_read_duration: max_read_duration)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# ReverseProxy releases exactly what it abandons. A body it streamed to the end
|
||||||
|
# is the client's to release, mirroring how the gun client frees its stream on
|
||||||
|
# :done -- see Pleroma.ReverseProxy.Client.Hackney.stream_body/1 for why an
|
||||||
|
# abandoned Hackney ref never gets reaped on its own.
|
||||||
|
describe "client ref release" do
|
||||||
|
test "leaves a fully streamed body for the client to release", %{conn: conn} do
|
||||||
|
ClientMock
|
||||||
|
|> expect(:request, fn :get, "/release", _, _, _ ->
|
||||||
|
{:ok, 200, [{"content-type", "image/png"}], %{url: "/release"}}
|
||||||
|
end)
|
||||||
|
|> expect(:stream_body, fn _ -> :done end)
|
||||||
|
|
||||||
|
conn = ReverseProxy.call(conn, "/release")
|
||||||
|
|
||||||
|
assert conn.halted
|
||||||
|
refute_received {:closed, _}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "releases the client when content-length exceeds max_body_length", %{conn: conn} do
|
||||||
|
ClientMock
|
||||||
|
|> expect(:request, fn :get, "/release-huge", _, _, _ ->
|
||||||
|
{:ok, 200, [{"content-length", "102227307"}], %{url: "/release-huge"}}
|
||||||
|
end)
|
||||||
|
|
||||||
|
assert capture_log(fn ->
|
||||||
|
ReverseProxy.call(conn, "/release-huge", max_body_length: 25 * 1_048_576)
|
||||||
|
end) =~ ":body_too_large"
|
||||||
|
|
||||||
|
assert_received {:closed, %{url: "/release-huge"}}
|
||||||
|
refute_received {:closed, _}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "releases the client when the upstream status is not proxiable", %{conn: conn} do
|
||||||
|
ClientMock
|
||||||
|
|> expect(:request, fn :get, "/release-418", _, _, _ ->
|
||||||
|
{:ok, 418, [], %{url: "/release-418"}}
|
||||||
|
end)
|
||||||
|
|
||||||
|
assert capture_log(fn -> ReverseProxy.call(conn, "/release-418") end) =~
|
||||||
|
"failed with HTTP status 418"
|
||||||
|
|
||||||
|
assert_received {:closed, %{url: "/release-418"}}
|
||||||
|
refute_received {:closed, _}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "has nothing to release when the reply carries no ref", %{conn: conn} do
|
||||||
|
ClientMock
|
||||||
|
|> expect(:request, fn :head, "/release-headless", _, _, _ ->
|
||||||
|
{:ok, 200, [{"content-type", "image/png"}]}
|
||||||
|
end)
|
||||||
|
|
||||||
|
conn = ReverseProxy.call(Map.put(conn, :method, "HEAD"), "/release-headless")
|
||||||
|
|
||||||
|
assert conn.status == 200
|
||||||
|
refute_received {:closed, _}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
test "use Pleroma's user agent in the request; don't pass the client's", %{conn: conn} do
|
test "use Pleroma's user agent in the request; don't pass the client's", %{conn: conn} do
|
||||||
|
|
@ -398,7 +507,7 @@ defmodule Pleroma.ReverseProxyTest do
|
||||||
refute :timeout in responses
|
refute :timeout in responses
|
||||||
end
|
end
|
||||||
|
|
||||||
defp stream_mock(invokes, with_close? \\ false) do
|
defp stream_mock(invokes) do
|
||||||
ClientMock
|
ClientMock
|
||||||
|> expect(:request, fn :get, "/stream-bytes/" <> length, _, _, _ ->
|
|> expect(:request, fn :get, "/stream-bytes/" <> length, _, _, _ ->
|
||||||
Registry.register(ClientMock, "/stream-bytes/" <> length, 0)
|
Registry.register(ClientMock, "/stream-bytes/" <> length, 0)
|
||||||
|
|
@ -424,10 +533,6 @@ defmodule Pleroma.ReverseProxyTest do
|
||||||
:done
|
:done
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
if with_close? do
|
|
||||||
expect(ClientMock, :close, fn _ -> :ok end)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "max_body" do
|
describe "max_body" do
|
||||||
|
|
@ -458,7 +563,7 @@ defmodule Pleroma.ReverseProxyTest do
|
||||||
end
|
end
|
||||||
|
|
||||||
test "max_body_length returns error if streaming body more than that option", %{conn: conn} do
|
test "max_body_length returns error if streaming body more than that option", %{conn: conn} do
|
||||||
stream_mock(3, true)
|
stream_mock(3)
|
||||||
|
|
||||||
log =
|
log =
|
||||||
capture_log(fn ->
|
capture_log(fn ->
|
||||||
|
|
@ -469,6 +574,9 @@ defmodule Pleroma.ReverseProxyTest do
|
||||||
|
|
||||||
assert log =~
|
assert log =~
|
||||||
"Elixir.Pleroma.ReverseProxy request to /stream-bytes/50 failed while reading/chunking: :body_too_large"
|
"Elixir.Pleroma.ReverseProxy request to /stream-bytes/50 failed while reading/chunking: :body_too_large"
|
||||||
|
|
||||||
|
assert_received {:closed, %{url: "/stream-bytes/50"}}
|
||||||
|
refute_received {:closed, _}
|
||||||
end
|
end
|
||||||
|
|
||||||
test "max_body_length accepts a body exactly at the limit", %{conn: conn} do
|
test "max_body_length accepts a body exactly at the limit", %{conn: conn} do
|
||||||
|
|
|
||||||
87
test/pleroma/tesla/middleware/max_body_test.exs
Normal file
87
test/pleroma/tesla/middleware/max_body_test.exs
Normal file
|
|
@ -0,0 +1,87 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Tesla.Middleware.MaxBodyTest do
|
||||||
|
use ExUnit.Case, async: true
|
||||||
|
|
||||||
|
alias Pleroma.Tesla.Middleware.MaxBody
|
||||||
|
|
||||||
|
defp client(adapter_opts, response) do
|
||||||
|
Tesla.client([{MaxBody, nil}], fn env ->
|
||||||
|
{:ok, %{env | status: 200, headers: response.headers, body: response.body}}
|
||||||
|
end)
|
||||||
|
|> then(fn c -> {c, [opts: [adapter: adapter_opts]]} end)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp get(adapter_opts, response) do
|
||||||
|
{c, opts} = client(adapter_opts, response)
|
||||||
|
Tesla.get(c, "http://example.com/", opts)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "passes a body within the limit through untouched" do
|
||||||
|
assert {:ok, %Tesla.Env{status: 200, body: "hello"}} =
|
||||||
|
get([max_body: 100], %{headers: [], body: "hello"})
|
||||||
|
end
|
||||||
|
|
||||||
|
test "rejects a body larger than the limit" do
|
||||||
|
assert {:error, :body_too_large} =
|
||||||
|
get([max_body: 4], %{headers: [], body: "hello"})
|
||||||
|
end
|
||||||
|
|
||||||
|
test "rejects when content-length exceeds the limit" do
|
||||||
|
assert {:error, :body_too_large} =
|
||||||
|
get([max_body: 4], %{headers: [{"content-length", "1000"}], body: ""})
|
||||||
|
end
|
||||||
|
|
||||||
|
test "allows a content-length equal to the limit" do
|
||||||
|
assert {:ok, %Tesla.Env{status: 200}} =
|
||||||
|
get([max_body: 5], %{headers: [{"content-length", "5"}], body: "hello"})
|
||||||
|
end
|
||||||
|
|
||||||
|
test "ignores an unparsable content-length" do
|
||||||
|
assert {:ok, %Tesla.Env{status: 200}} =
|
||||||
|
get([max_body: 100], %{headers: [{"content-length", "banana"}], body: "hi"})
|
||||||
|
end
|
||||||
|
|
||||||
|
test "does nothing without a max_body option" do
|
||||||
|
big = String.duplicate("x", 10_000)
|
||||||
|
|
||||||
|
assert {:ok, %Tesla.Env{body: ^big}} =
|
||||||
|
get([], %{headers: [{"content-length", "10000"}], body: big})
|
||||||
|
end
|
||||||
|
|
||||||
|
# body_as: :chunks hands back a client map, not a binary; the caller streams
|
||||||
|
# it and enforces its own limit (see Pleroma.Web.RichMedia.Helpers).
|
||||||
|
test "leaves a non-binary streaming body alone" do
|
||||||
|
body = %{pid: self(), stream: make_ref()}
|
||||||
|
|
||||||
|
assert {:ok, %Tesla.Env{body: ^body}} =
|
||||||
|
get([max_body: 1], %{headers: [], body: body})
|
||||||
|
end
|
||||||
|
|
||||||
|
# Rejecting a stream on the header alone would strand the connection lease:
|
||||||
|
# the env carrying the pid/stream is the caller's only handle to release it.
|
||||||
|
test "does not reject a streaming body on content-length alone" do
|
||||||
|
body = %{pid: self(), stream: make_ref()}
|
||||||
|
|
||||||
|
assert {:ok, %Tesla.Env{body: ^body}} =
|
||||||
|
get([max_body: 1], %{headers: [{"content-length", "20000000"}], body: body})
|
||||||
|
end
|
||||||
|
|
||||||
|
test "ignores content-length on a HEAD response" do
|
||||||
|
{c, opts} = client([max_body: 4], %{headers: [{"content-length", "9000000"}], body: ""})
|
||||||
|
|
||||||
|
assert {:ok, %Tesla.Env{status: 200}} = Tesla.head(c, "http://example.com/", opts)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "ignores content-length on a bodyless status" do
|
||||||
|
client =
|
||||||
|
Tesla.client([{MaxBody, nil}], fn env ->
|
||||||
|
{:ok, %{env | status: 304, headers: [{"content-length", "9000000"}], body: ""}}
|
||||||
|
end)
|
||||||
|
|
||||||
|
assert {:ok, %Tesla.Env{status: 304}} =
|
||||||
|
Tesla.get(client, "http://example.com/", opts: [adapter: [max_body: 4]])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -216,6 +216,39 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.ArticleNotePageValidatorTest
|
||||||
assert content =~ "@uncached"
|
assert content =~ "@uncached"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "a Misskey MFM remote mention is not rendered as an email link" do
|
||||||
|
remote_user = insert(:user, ap_id: "https://mk.absturztau.be/users/alice")
|
||||||
|
mentioned_user = insert(:user, ap_id: "https://movsw.0x0.st/users/bob")
|
||||||
|
|
||||||
|
note = %{
|
||||||
|
"id" => "https://mk.absturztau.be/notes/1",
|
||||||
|
"type" => "Note",
|
||||||
|
"actor" => remote_user.ap_id,
|
||||||
|
"attributedTo" => remote_user.ap_id,
|
||||||
|
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||||
|
"cc" => [mentioned_user.ap_id],
|
||||||
|
"content" => ~s(<p>@<a href="mailto:bob@movsw.0x0.st">bob@movsw.0x0.st</a> hello</p>),
|
||||||
|
"context" => Utils.generate_context_id(),
|
||||||
|
"tag" => [
|
||||||
|
%{
|
||||||
|
"type" => "Mention",
|
||||||
|
"name" => "@bob@movsw.0x0.st",
|
||||||
|
"href" => mentioned_user.ap_id
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"source" => %{
|
||||||
|
"content" => "@bob@movsw.0x0.st hello",
|
||||||
|
"mediaType" => "text/x.misskeymarkdown"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
%{valid?: true, changes: %{content: content}} =
|
||||||
|
ArticleNotePageValidator.cast_and_validate(note)
|
||||||
|
|
||||||
|
assert content =~ ~s(href="#{mentioned_user.ap_id}")
|
||||||
|
refute content =~ "mailto:"
|
||||||
|
end
|
||||||
|
|
||||||
test "a Misskey MFM note drops oversized source content instead of parsing it" do
|
test "a Misskey MFM note drops oversized source content instead of parsing it" do
|
||||||
user = insert(:user, ap_id: "https://misskey.example/users/oversized")
|
user = insert(:user, ap_id: "https://misskey.example/users/oversized")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -125,6 +125,14 @@ defmodule Pleroma.Web.CommonAPI.UtilsTest do
|
||||||
assert output == expected
|
assert output == expected
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "MFM keeps bare URLs containing Markdown delimiters intact" do
|
||||||
|
url = "https://example.org/a*b*c"
|
||||||
|
|
||||||
|
{output, [], []} = Utils.format_input(url, "text/x.misskeymarkdown")
|
||||||
|
|
||||||
|
assert output == ~s(<p><a href="#{url}">#{url}</a></p>)
|
||||||
|
end
|
||||||
|
|
||||||
test "works for bare text/bbcode" do
|
test "works for bare text/bbcode" do
|
||||||
text = "[b]hello world[/b]"
|
text = "[b]hello world[/b]"
|
||||||
expected = "<strong>hello world</strong>"
|
expected = "<strong>hello world</strong>"
|
||||||
|
|
@ -220,6 +228,15 @@ defmodule Pleroma.Web.CommonAPI.UtilsTest do
|
||||||
assert result == ~s[<a href="http://example.org/">OwO</a>]
|
assert result == ~s[<a href="http://example.org/">OwO</a>]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "markdown links do not allow injected attributes" do
|
||||||
|
code = ~s|[click](http://example.com/?a=x" onerror="alert(1))|
|
||||||
|
{result, [], []} = Utils.format_input(code, "text/markdown")
|
||||||
|
|
||||||
|
assert result =~ ~s(<a href="http://example.com/?a=x">)
|
||||||
|
refute result =~ ~s|onerror="alert(1)|
|
||||||
|
refute result =~ ~s(<a href="http://example.com/?a=x" onerror=)
|
||||||
|
end
|
||||||
|
|
||||||
test "rulers" do
|
test "rulers" do
|
||||||
code = ~s[before\n\n-----\n\nafter]
|
code = ~s[before\n\n-----\n\nafter]
|
||||||
{result, [], []} = Utils.format_input(code, "text/markdown")
|
{result, [], []} = Utils.format_input(code, "text/markdown")
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue