Page MenuHomePhorge

No OneTemporary

Authored By
Unknown
Size
3 KB
Referenced Files
None
Subscribers
None
diff --git a/changelog.d/filter-regex-escape.fix b/changelog.d/filter-regex-escape.fix
new file mode 100644
index 000000000..e6e011e3a
--- /dev/null
+++ b/changelog.d/filter-regex-escape.fix
@@ -0,0 +1 @@
+Treat filter phrases as literal text; a phrase like "(" no longer breaks timeline and notification queries, and phrases that start or end with punctuation (like "#tag") now match
diff --git a/lib/pleroma/filter.ex b/lib/pleroma/filter.ex
index 77ed64d4f..3ab44efb0 100644
--- a/lib/pleroma/filter.ex
+++ b/lib/pleroma/filter.ex
@@ -208,19 +208,27 @@ defmodule Pleroma.Filter do
end
def compose_regex([_ | _] = filters, format) do
+ boundary = if format == :postgres, do: "\\y", else: "\\b"
+
phrases =
filters
- |> Enum.map(& &1.phrase)
+ |> Enum.map(&phrase_regex(&1.phrase, boundary))
|> Enum.join("|")
case format do
- :postgres ->
- "\\y(#{phrases})\\y"
-
- :re ->
- ~r/\b#{phrases}\b/i
+ :postgres -> "(#{phrases})"
+ :re -> Regex.compile!("(?:#{phrases})", "iu")
end
end
def compose_regex(_, _), do: nil
+
+ # Phrases are literal text. Like Mastodon, only add a word boundary
+ # where the phrase starts or ends with a word character.
+ defp phrase_regex(phrase, boundary) do
+ prefix = if String.match?(phrase, ~r/^\w/u), do: boundary, else: ""
+ suffix = if String.match?(phrase, ~r/\w$/u), do: boundary, else: ""
+
+ prefix <> Regex.escape(phrase) <> suffix
+ end
end
diff --git a/test/pleroma/filter_test.exs b/test/pleroma/filter_test.exs
index 6d33cd22b..5018b30bc 100644
--- a/test/pleroma/filter_test.exs
+++ b/test/pleroma/filter_test.exs
@@ -190,4 +190,55 @@ defmodule Pleroma.FilterTest do
{:error, _} = Filter.update(filter, changes)
end
+
+ describe "compose_regex/2" do
+ defp filters(phrases), do: Enum.map(phrases, &%Filter{phrase: &1})
+
+ defp pg_match?(regex, text) do
+ %{rows: [[match]]} = Repo.query!("SELECT $1::text ~* $2", [text, regex])
+ match
+ end
+
+ test "treats phrases as literal text in Postgres" do
+ regex = Filter.compose_regex(filters(["a.b", "(c"]))
+
+ assert pg_match?(regex, "see a.b here")
+ refute pg_match?(regex, "see axb here")
+ assert pg_match?(regex, "see (c here")
+ end
+
+ test "treats phrases as literal text in Elixir" do
+ regex = Filter.compose_regex(filters(["a.b", "(c"]), :re)
+
+ assert Regex.match?(regex, "see a.b here")
+ refute Regex.match?(regex, "see axb here")
+ assert Regex.match?(regex, "see (c here")
+ end
+
+ test "keeps word boundaries for every phrase in Postgres" do
+ regex = Filter.compose_regex(filters(["cat", "dog"]))
+
+ assert pg_match?(regex, "a dog barks")
+ refute pg_match?(regex, "a catalog")
+ refute pg_match?(regex, "a hotdog")
+ end
+
+ test "only adds word boundaries next to word characters" do
+ pg = Filter.compose_regex(filters(["#tag"]))
+ re = Filter.compose_regex(filters(["#tag"]), :re)
+
+ for {text, expected} <- [{"a #tag here", true}, {"a#tag", true}, {"some #tags", false}] do
+ assert pg_match?(pg, text) == expected
+ assert Regex.match?(re, text) == expected
+ end
+ end
+
+ test "keeps word boundaries for every phrase in Elixir" do
+ regex = Filter.compose_regex(filters(["cat", "dog"]), :re)
+
+ assert Regex.match?(regex, "a dog barks")
+ refute Regex.match?(regex, "a catalog")
+ refute Regex.match?(regex, "a hotdog")
+ end
+ end
end

File Metadata

Mime Type
text/x-diff
Expires
Thu, Oct 29, 5:39 AM (7 h, 18 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1797602
Default Alt Text
(3 KB)

Event Timeline