Merge pull request 'Filters: treat phrases as literal text' (#8020) from lambadalambda/pleroma:fix/filter-regex-escape into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/8020
This commit is contained in:
commit
f4e8834e65
4 changed files with 81 additions and 6 deletions
1
changelog.d/filter-regex-escape.fix
Normal file
1
changelog.d/filter-regex-escape.fix
Normal file
|
|
@ -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
|
||||
1
changelog.d/filter-whole-word.fix
Normal file
1
changelog.d/filter-whole-word.fix
Normal file
|
|
@ -0,0 +1 @@
|
|||
Filters with whole_word set to false now also match inside words
|
||||
|
|
@ -208,19 +208,30 @@ 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, 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, whole-word filters only get a
|
||||
# word boundary where the phrase starts or ends with a word character.
|
||||
defp phrase_regex(%__MODULE__{phrase: phrase, whole_word: false}, _boundary),
|
||||
do: Regex.escape(phrase)
|
||||
|
||||
defp phrase_regex(%__MODULE__{phrase: 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
|
||||
|
|
|
|||
|
|
@ -190,4 +190,66 @@ 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 "matches inside words when whole_word is false" do
|
||||
filters = [%Filter{phrase: "cat", whole_word: false}, %Filter{phrase: "dog"}]
|
||||
pg = Filter.compose_regex(filters)
|
||||
re = Filter.compose_regex(filters, :re)
|
||||
|
||||
for {text, expected} <- [{"a catalog", true}, {"a hotdog", false}, {"a dog", true}] 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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue