Skip to content

SLOP-373: fix SQL injection in groupfilter/categoryfilter/ClearPendingReviews - #1

Open
tosfos wants to merge 1 commit into
masterfrom
SLOP-373-fix-sql-injection
Open

SLOP-373: fix SQL injection in groupfilter/categoryfilter/ClearPendingReviews#1
tosfos wants to merge 1 commit into
masterfrom
SLOP-373-fix-sql-injection

Conversation

@tosfos

@tosfos tosfos commented Aug 23, 2026

Copy link
Copy Markdown

Summary

Fixes three SQL injection sites where user-controlled values were interpolated verbatim into raw SQL strings (all inherited from upstream wikimedia/mediawiki-extensions-WatchAnalytics):

  1. includes/UserWatchesQuery.php$this->userGroupFilter (from public ?groupfilter= on Special:WatchAnalytics) was spliced into the user_groups RIGHT JOIN condition inside double quotes. Payload x" OR 1=1) UNION SELECT user_password FROM user-- - executed a full UNION breakout (verified live against MariaDB: returned password hashes from the user table).
  2. includes/WatchesQuery.php::setCategoryFilterQueryInfo() — same pattern for ?categoryfilter= against categorylinks.cl_to.
  3. specials/SpecialClearPendingReviews.php::doSearchQuery()category, page, start, and end form fields were interpolated into WHERE clauses with single quotes (c.cl_to='$category', LIKE '$page%', < $end). Requires the clearreviews right, but any holder could read/modify arbitrary data.

Fix

Replace raw string interpolation with MediaWiki parameterized constructs that quote automatically:

  • Array join conditions ([ 'w.wl_user = ug.ug_user', 'ug.ug_group' => $filter ]) → rendered via makeList/addQuotes.
  • $dbw->buildLike( $page, $dbw->anyString() ) for the title prefix match.
  • $dbw->addQuotes() for the start/end timestamp comparisons.
    Also null-coalesce missing category/page form fields (PHP 8 warning source).

Verification

  • php -l clean on PHP 7.4-cli and 8.2-cli for all three files.
  • Before/after harness against a live MariaDB 10.11 (/tmp harness): old join condition executes the UNION payload returning bob, hashA, hashB; fixed query treats the payload as a literal group name and returns no breakout rows. Old ClearPendingReviews LIKE ' OR 1='1 matched every row; fixed version matches only the literal prefix.
  • No behavior change for benign inputs: conditions render identically for normal filter values.

Closes SLOP-373.

…gReviews

Replace raw string interpolation of user-controlled values into SQL
join conditions and WHERE clauses with MediaWiki parameterized
conditions (array conds -> quoted values, buildLike for prefix match,
addQuotes for timestamps).

Sites fixed:
- UserWatchesQuery::getQueryInfo() groupfilter join (?groupfilter=)
- WatchesQuery::setCategoryFilterQueryInfo() (?categoryfilter=)
- SpecialClearPendingReviews::doSearchQuery() category/page/start/end
@coderabbitai

coderabbitai Bot commented Aug 23, 2026

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: cd954aa2-b13b-48dc-afa7-e89a2e355954


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant