SECURITY: Prevent leaking hidden usernames in Watchlist/RecentChanges

CVE-2025-61646

If an individual editor makes consecutive revisions on a single page,
and only some are marked as hidden username, the non-hidden ones will
reveal the (username hidden) true identity.

Enable the "Group changes by page in recent changes and watchlist" and
"Use non-JavaScript interface" preferences to reproduce the issue. See
the referenced Phabricator tasks for more details.

The solution here is to separate hidden and visible editors from the
grouping on the frontend side, using existing helper functions.

Bug: T398706
Change-Id: I1408fe7712ffef3ba76294d8483c7b7624a0d11c
This commit is contained in:
Dillon Hardy 2025-09-10 09:47:24 -04:00 committed by Reedy
parent f7c717b09a
commit 2a0451f75d

View file

@ -808,10 +808,33 @@ class EnhancedChangesList extends ChangesList {
$blockOut = '';
foreach ( $this->rc_cache as $block ) {
if ( count( $block ) < 2 ) {
$blockOut .= $this->recentChangesBlockLine( array_shift( $block ) );
} else {
$blockOut .= $this->recentChangesBlockGroup( $block );
$visibleBlock = [];
$hiddenBlock = [];
// T398706: Filter the block to prevent leaking hidden usernames
foreach ( $block as $rcObj ) {
if ( static::isDeleted( $rcObj, RevisionRecord::DELETED_USER ) ) {
$hiddenBlock[] = $rcObj;
} else {
$visibleBlock[] = $rcObj;
}
}
$visibleCount = count( $visibleBlock );
if ( $visibleCount > 0 ) {
$blockOut .= $visibleCount > 1 ?
$this->recentChangesBlockGroup( $visibleBlock ) :
$this->recentChangesBlockLine( array_shift( $visibleBlock ) );
}
$hiddenCount = count( $hiddenBlock );
if ( $hiddenCount > 0 ) {
$blockOut .= $hiddenCount > 1 ?
$this->recentChangesBlockGroup( $hiddenBlock ) :
$this->recentChangesBlockLine( array_shift( $hiddenBlock ) );
}
}