Skip to content

[2.x] fix(search): harden pgsql search configuration handling#4663

Merged
imorland merged 1 commit into
2.xfrom
im/pgsql-search-config-injection
May 14, 2026
Merged

[2.x] fix(search): harden pgsql search configuration handling#4663
imorland merged 1 commit into
2.xfrom
im/pgsql-search-config-injection

Conversation

@imorland
Copy link
Copy Markdown
Member

Summary

  • The pgsql_search_configuration setting was interpolated directly into the SQL strings built by Discussion\Search\FulltextFilter::pgsql() and Post\Filter\FulltextFilter::pgsql(), allowing a second-order SQL injection: an admin could write a payload to the setting and any subsequent search would execute it.
  • Switched to ?::regconfig parameter binding for the search configuration. The value is now treated as data, and Postgres rejects unknown configuration names at the cast — defence in depth for free.
  • Added a Postgres-only integration test that exercises the reported attack chain end-to-end. It uses the SQLSTATE returned by Postgres as an unambiguous signal: 42883 (operator-does-not-exist on tsvector || pg_sleep) means the payload was treated as code; 42602 (invalid name syntax from the regconfig cast) means it was treated as data. Verified to fail against the un-patched code and pass against the patched code.

Scope

  • Admin-only (the setting is writable only with admin privileges).
  • Postgres-only — MySQL/MariaDB and SQLite use different fulltext paths that already parameterise correctly.
  • Flarum 1.x has no Postgres support and is unaffected.
  • Non-breaking: SQL semantics are preserved; no public API changes.

Test plan

  • composer test:integration against Postgres 16 with the new test passes.
  • Existing fulltext tests (FulltextSearch|FulltextFilter|ListWithFulltext) pass against Postgres with the fix.
  • Full tests/integration/api/discussions suite (39 tests) passes against Postgres.
  • New test confirmed to fail against the un-patched code (detecting SQLSTATE 42883) — i.e. the test would have caught the regression.

Credit

Reported via the Flarum.org foundation contact form by userb1ank.

Bind the `pgsql_search_configuration` setting via `?::regconfig`
instead of interpolating it into the SQL string. The value is now
treated as data; Postgres rejects invalid configuration names at
the cast.
@imorland imorland requested a review from a team as a code owner May 14, 2026 21:01
@imorland imorland added this to the 2.0.0-rc.2 milestone May 14, 2026
@imorland imorland merged commit d01c320 into 2.x May 14, 2026
25 checks passed
@imorland imorland deleted the im/pgsql-search-config-injection branch May 14, 2026 21:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant