fix(mrf): match domain rules literally
Escape the domain so `.` only matches a dot, and use an optional group for the `*.` prefix, which matches the same hosts without exponential backtracking. Bad entries now match nothing instead of raising on every check.
This commit is contained in:
parent
3e9dc477c9
commit
7e157d8cc9
3 changed files with 27 additions and 20 deletions
1
changelog.d/mrf-domain-regex-escape.security
Normal file
1
changelog.d/mrf-domain-regex-escape.security
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
MRF domain rules now match dots literally, so a rule for `good.example` no longer also matches `goodxexample`, and wildcard rules no longer backtrack on hostile host names
|
||||||
|
|
@ -148,14 +148,13 @@ defmodule Pleroma.Web.ActivityPub.MRF do
|
||||||
@spec subdomains_regex([String.t()]) :: [Regex.t()]
|
@spec subdomains_regex([String.t()]) :: [Regex.t()]
|
||||||
def subdomains_regex(domains) when is_list(domains) do
|
def subdomains_regex(domains) when is_list(domains) do
|
||||||
for domain <- domains do
|
for domain <- domains do
|
||||||
try do
|
{prefix, rest} =
|
||||||
target = String.replace(domain, "*.", "(.*\\.)*")
|
case domain do
|
||||||
~r<^#{target}$>i
|
"*." <> rest -> {"(.*\\.)?", rest}
|
||||||
rescue
|
_ -> {"", domain}
|
||||||
e ->
|
end
|
||||||
Logger.error("MRF: Invalid subdomain Regex: #{domain}")
|
|
||||||
reraise e, __STACKTRACE__
|
~r<^#{prefix}#{Regex.escape(rest)}$>i
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,6 @@ defmodule Pleroma.Web.ActivityPub.MRFTest do
|
||||||
use ExUnit.Case
|
use ExUnit.Case
|
||||||
use Pleroma.Tests.Helpers
|
use Pleroma.Tests.Helpers
|
||||||
|
|
||||||
import ExUnit.CaptureLog
|
|
||||||
|
|
||||||
alias Pleroma.Web.ActivityPub.MRF
|
alias Pleroma.Web.ActivityPub.MRF
|
||||||
|
|
||||||
defp regexes_match!([], []), do: true
|
defp regexes_match!([], []), do: true
|
||||||
|
|
@ -21,14 +19,14 @@ defmodule Pleroma.Web.ActivityPub.MRFTest do
|
||||||
test "subdomains_regex/1" do
|
test "subdomains_regex/1" do
|
||||||
regexes = MRF.subdomains_regex(["unsafe.tld", "*.unsafe.tld"])
|
regexes = MRF.subdomains_regex(["unsafe.tld", "*.unsafe.tld"])
|
||||||
|
|
||||||
assert regexes_match!(regexes, [~r/^unsafe.tld$/i, ~r/^(.*\.)*unsafe.tld$/i])
|
assert regexes_match!(regexes, [~r/^unsafe\.tld$/i, ~r/^(.*\.)?unsafe\.tld$/i])
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "subdomain_match/2" do
|
describe "subdomain_match/2" do
|
||||||
test "common domains" do
|
test "common domains" do
|
||||||
regexes = MRF.subdomains_regex(["unsafe.tld", "unsafe2.tld"])
|
regexes = MRF.subdomains_regex(["unsafe.tld", "unsafe2.tld"])
|
||||||
|
|
||||||
assert regexes_match!(regexes, [~r/^unsafe.tld$/i, ~r/^unsafe2.tld$/i])
|
assert regexes_match!(regexes, [~r/^unsafe\.tld$/i, ~r/^unsafe2\.tld$/i])
|
||||||
|
|
||||||
assert MRF.subdomain_match?(regexes, "unsafe.tld")
|
assert MRF.subdomain_match?(regexes, "unsafe.tld")
|
||||||
assert MRF.subdomain_match?(regexes, "unsafe2.tld")
|
assert MRF.subdomain_match?(regexes, "unsafe2.tld")
|
||||||
|
|
@ -39,7 +37,7 @@ defmodule Pleroma.Web.ActivityPub.MRFTest do
|
||||||
test "wildcard domains with one subdomain" do
|
test "wildcard domains with one subdomain" do
|
||||||
regexes = MRF.subdomains_regex(["*.unsafe.tld"])
|
regexes = MRF.subdomains_regex(["*.unsafe.tld"])
|
||||||
|
|
||||||
assert regexes_match!(regexes, [~r/^(.*\.)*unsafe.tld$/i])
|
assert regexes_match!(regexes, [~r/^(.*\.)?unsafe\.tld$/i])
|
||||||
|
|
||||||
assert MRF.subdomain_match?(regexes, "unsafe.tld")
|
assert MRF.subdomain_match?(regexes, "unsafe.tld")
|
||||||
assert MRF.subdomain_match?(regexes, "sub.unsafe.tld")
|
assert MRF.subdomain_match?(regexes, "sub.unsafe.tld")
|
||||||
|
|
@ -50,7 +48,7 @@ defmodule Pleroma.Web.ActivityPub.MRFTest do
|
||||||
test "wildcard domains with two subdomains" do
|
test "wildcard domains with two subdomains" do
|
||||||
regexes = MRF.subdomains_regex(["*.unsafe.tld"])
|
regexes = MRF.subdomains_regex(["*.unsafe.tld"])
|
||||||
|
|
||||||
assert regexes_match!(regexes, [~r/^(.*\.)*unsafe.tld$/i])
|
assert regexes_match!(regexes, [~r/^(.*\.)?unsafe\.tld$/i])
|
||||||
|
|
||||||
assert MRF.subdomain_match?(regexes, "unsafe.tld")
|
assert MRF.subdomain_match?(regexes, "unsafe.tld")
|
||||||
assert MRF.subdomain_match?(regexes, "sub.sub.unsafe.tld")
|
assert MRF.subdomain_match?(regexes, "sub.sub.unsafe.tld")
|
||||||
|
|
@ -61,7 +59,7 @@ defmodule Pleroma.Web.ActivityPub.MRFTest do
|
||||||
test "matches are case-insensitive" do
|
test "matches are case-insensitive" do
|
||||||
regexes = MRF.subdomains_regex(["UnSafe.TLD", "UnSAFE2.Tld"])
|
regexes = MRF.subdomains_regex(["UnSafe.TLD", "UnSAFE2.Tld"])
|
||||||
|
|
||||||
assert regexes_match!(regexes, [~r/^UnSafe.TLD$/i, ~r/^UnSAFE2.Tld$/i])
|
assert regexes_match!(regexes, [~r/^UnSafe\.TLD$/i, ~r/^UnSAFE2\.Tld$/i])
|
||||||
|
|
||||||
assert MRF.subdomain_match?(regexes, "UNSAFE.TLD")
|
assert MRF.subdomain_match?(regexes, "UNSAFE.TLD")
|
||||||
assert MRF.subdomain_match?(regexes, "UNSAFE2.TLD")
|
assert MRF.subdomain_match?(regexes, "UNSAFE2.TLD")
|
||||||
|
|
@ -72,11 +70,20 @@ defmodule Pleroma.Web.ActivityPub.MRFTest do
|
||||||
refute MRF.subdomain_match?(regexes, "example.com")
|
refute MRF.subdomain_match?(regexes, "example.com")
|
||||||
end
|
end
|
||||||
|
|
||||||
test "logs sensible error on accidental wildcard" do
|
test "dots only match dots" do
|
||||||
assert_raise Regex.CompileError, fn ->
|
regexes = MRF.subdomains_regex(["unsafe.tld", "*.other.tld"])
|
||||||
assert capture_log(MRF.subdomains_regex(["*unsafe.tld"])) =~
|
|
||||||
"MRF: Invalid subdomain Regex: *unsafe.tld"
|
refute MRF.subdomain_match?(regexes, "unsafextld")
|
||||||
end
|
refute MRF.subdomain_match?(regexes, "otherxtld")
|
||||||
|
refute MRF.subdomain_match?(regexes, "sub.otherxtld")
|
||||||
|
end
|
||||||
|
|
||||||
|
test "accidental wildcards and regex syntax match nothing" do
|
||||||
|
regexes = MRF.subdomains_regex(["*unsafe.tld", "bad(.tld"])
|
||||||
|
|
||||||
|
refute MRF.subdomain_match?(regexes, "unsafe.tld")
|
||||||
|
refute MRF.subdomain_match?(regexes, "sub.unsafe.tld")
|
||||||
|
refute MRF.subdomain_match?(regexes, "bad.tld")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue