Harden ParadeDB search result safety

Re-apply public Create visibility checks when resolving backend hits, improve UUID/FlakeId id parsing, and add tests for poisoned hits and UUID-string ids. Also tighten the docker integration test with localhost-only binding and config restoration.
This commit is contained in:
Lain Soykaf 2026-03-05 16:36:22 +04:00
commit 74da3e01f7
No known key found for this signature in database
3 changed files with 111 additions and 6 deletions

View file

@ -14,7 +14,7 @@ defmodule Pleroma.Search.ParadeDBIntegrationTest do
alias Pleroma.Search.ParadeDB
alias Pleroma.Web.CommonAPI
@image "paradedb/paradedb:latest"
@image System.get_env("PARADEDB_TEST_IMAGE", "paradedb/paradedb:latest")
@db_user "postgres"
@db_password "postgres"
@db_name "paradedb"
@ -46,7 +46,7 @@ defmodule Pleroma.Search.ParadeDBIntegrationTest do
"-e",
"POSTGRES_DB=#{@db_name}",
"-p",
"5432",
"127.0.0.1::5432",
"-d",
@image
])
@ -75,9 +75,18 @@ defmodule Pleroma.Search.ParadeDBIntegrationTest do
{:ok, _pid} = start_supervised({Pleroma.Search.ParadeDB.Repo, pool_size: 1})
previous_table = Pleroma.Config.get([Pleroma.Search.ParadeDB, :table], nil)
table = "pleroma_search_documents_it_#{System.unique_integer([:positive])}"
Pleroma.Config.put([Pleroma.Search.ParadeDB, :table], table)
on_exit(fn ->
if is_nil(previous_table) do
Pleroma.Config.delete([Pleroma.Search.ParadeDB, :table])
else
Pleroma.Config.put([Pleroma.Search.ParadeDB, :table], previous_table)
end
end)
{:ok,
%{
container: container,

View file

@ -172,5 +172,68 @@ defmodule Pleroma.Search.ParadeDBTest do
assert [%{id: ^activity2_id}, %{id: ^activity1_id}] =
ParadeDB.search(nil, "swamp", limit: 40, offset: 0)
end
test "search excludes non-public activities from backend hits" do
user = insert(:user)
{:ok, public_activity} =
CommonAPI.post(user, %{status: "public swamp", visibility: "public"})
{:ok, private_activity} =
CommonAPI.post(user, %{status: "private swamp", visibility: "private"})
public_activity_id = public_activity.id
{:ok, dumped_public_id} = FlakeId.Ecto.CompatType.dump(public_activity.id)
{:ok, dumped_private_id} = FlakeId.Ecto.CompatType.dump(private_activity.id)
ClientMock
|> expect(:query, fn sql, params ->
assert sql =~ "SELECT id FROM pleroma_search_documents"
assert ["swamp", _limit, _offset] = params
{:ok, %{rows: [[dumped_private_id], [dumped_public_id]]}}
end)
Config
|> expect(:get, 2, fn
[Pleroma.Search.ParadeDB, :client_impl], nil ->
ClientMock
[Pleroma.Search.ParadeDB, :table], "pleroma_search_documents" ->
"pleroma_search_documents"
end)
assert [%{id: ^public_activity_id}] = ParadeDB.search(nil, "swamp", limit: 40, offset: 0)
end
test "search supports backend ids returned as uuid strings" do
user = insert(:user)
{:ok, activity} = CommonAPI.post(user, %{status: "uuid swamp", visibility: "public"})
activity_id = activity.id
{:ok, dumped_id} = FlakeId.Ecto.CompatType.dump(activity_id)
{:ok, uuid_string} = Ecto.UUID.load(dumped_id)
ClientMock
|> expect(:query, fn sql, params ->
assert sql =~ "SELECT id FROM pleroma_search_documents"
assert ["swamp", _limit, _offset] = params
{:ok, %{rows: [[uuid_string]]}}
end)
Config
|> expect(:get, 2, fn
[Pleroma.Search.ParadeDB, :client_impl], nil ->
ClientMock
[Pleroma.Search.ParadeDB, :table], "pleroma_search_documents" ->
"pleroma_search_documents"
end)
assert [%{id: ^activity_id}] = ParadeDB.search(nil, "swamp", limit: 40, offset: 0)
end
end
end