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()]
|
||||
def subdomains_regex(domains) when is_list(domains) do
|
||||
for domain <- domains do
|
||||
try do
|
||||
target = String.replace(domain, "*.", "(.*\\.)*")
|
||||
~r<^#{target}$>i
|
||||
rescue
|
||||
e ->
|
||||
Logger.error("MRF: Invalid subdomain Regex: #{domain}")
|
||||
reraise e, __STACKTRACE__
|
||||
end
|
||||
{prefix, rest} =
|
||||
case domain do
|
||||
"*." <> rest -> {"(.*\\.)?", rest}
|
||||
_ -> {"", domain}
|
||||
end
|
||||
|
||||
~r<^#{prefix}#{Regex.escape(rest)}$>i
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -6,8 +6,6 @@ defmodule Pleroma.Web.ActivityPub.MRFTest do
|
|||
use ExUnit.Case
|
||||
use Pleroma.Tests.Helpers
|
||||
|
||||
import ExUnit.CaptureLog
|
||||
|
||||
alias Pleroma.Web.ActivityPub.MRF
|
||||
|
||||
defp regexes_match!([], []), do: true
|
||||
|
|
@ -21,14 +19,14 @@ defmodule Pleroma.Web.ActivityPub.MRFTest do
|
|||
test "subdomains_regex/1" do
|
||||
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
|
||||
|
||||
describe "subdomain_match/2" do
|
||||
test "common domains" do
|
||||
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, "unsafe2.tld")
|
||||
|
|
@ -39,7 +37,7 @@ defmodule Pleroma.Web.ActivityPub.MRFTest do
|
|||
test "wildcard domains with one subdomain" do
|
||||
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, "sub.unsafe.tld")
|
||||
|
|
@ -50,7 +48,7 @@ defmodule Pleroma.Web.ActivityPub.MRFTest do
|
|||
test "wildcard domains with two subdomains" do
|
||||
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, "sub.sub.unsafe.tld")
|
||||
|
|
@ -61,7 +59,7 @@ defmodule Pleroma.Web.ActivityPub.MRFTest do
|
|||
test "matches are case-insensitive" do
|
||||
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, "UNSAFE2.TLD")
|
||||
|
|
@ -72,11 +70,20 @@ defmodule Pleroma.Web.ActivityPub.MRFTest do
|
|||
refute MRF.subdomain_match?(regexes, "example.com")
|
||||
end
|
||||
|
||||
test "logs sensible error on accidental wildcard" do
|
||||
assert_raise Regex.CompileError, fn ->
|
||||
assert capture_log(MRF.subdomains_regex(["*unsafe.tld"])) =~
|
||||
"MRF: Invalid subdomain Regex: *unsafe.tld"
|
||||
end
|
||||
test "dots only match dots" do
|
||||
regexes = MRF.subdomains_regex(["unsafe.tld", "*.other.tld"])
|
||||
|
||||
refute MRF.subdomain_match?(regexes, "unsafextld")
|
||||
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
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue