From c81092e76e9f72238f9d4ec7381e1b5a33abc57a Mon Sep 17 00:00:00 2001 From: Brian Wolff Date: Fri, 23 Feb 2018 21:31:28 +0000 Subject: [PATCH] SECURITY: Respect revision delete in Special:Redirect Special:Redirect could potentially leak information about revdel'd log entries and users (hideuser) to users who are not supposed to see this information. Bug: T187638 Change-Id: I16d4ec0801e8dda158ee12b8e3ebeae1878e051a --- includes/specials/SpecialRedirect.php | 40 ++++++++++++++++++++++++++++++++--- 1 file changed, 37 insertions(+), 3 deletions(-) diff --git a/includes/specials/SpecialRedirect.php b/includes/specials/SpecialRedirect.php index 3273046..4df031f 100644 --- a/includes/specials/SpecialRedirect.php +++ b/includes/specials/SpecialRedirect.php @@ -79,6 +79,9 @@ class SpecialRedirect extends FormSpecialPage { if ( $user->isAnon() ) { return null; } + if ( $user->isHidden() && !$this->getUser()->isAllowed( 'hideuser' ) ) { + return null; + } $userpage = Title::makeTitle( NS_USER, $username ); return $userpage->getFullURL( '', false, PROTO_CURRENT ); @@ -179,19 +182,24 @@ class SpecialRedirect extends FormSpecialPage { $logparams = [ 'log_id', + 'log_deleted', 'log_timestamp', 'log_type', 'log_user_text', ]; + $user = $this->getUser(); + $dbr = wfGetDB( DB_REPLICA ); + // Exclude logs user does not have right to view + $restrictClause = LogEventsList::getExcludeClause( $dbr, 'user', $user ) ?: '1=1'; // Gets the nested SQL statement which // returns timestamp of the log with the given log ID $inner = $dbr->selectSQLText( 'logging', [ 'log_timestamp' ], - [ 'log_id' => $logid ] + [ 'log_id' => $logid, $restrictClause ] ); // Returns all fields mentioned in $logparams of the logs @@ -199,7 +207,7 @@ class SpecialRedirect extends FormSpecialPage { $logsSameTimestamps = $dbr->select( 'logging', $logparams, - [ "log_timestamp = ($inner)" ] + [ "log_timestamp = ($inner)", $restrictClause ] ); if ( $logsSameTimestamps->numRows() === 0 ) { return null; @@ -213,6 +221,13 @@ class SpecialRedirect extends FormSpecialPage { } } + $fieldToBitsMapping = [ + 'log_type' => LogPage::DELETED_ACTION, + 'log_user_text' => LogPage::DELETED_USER + ]; + + // get rid of log_id and log_deleted. + array_shift( $logparams ); array_shift( $logparams ); // Stores all the rows with the same values in each column @@ -220,6 +235,21 @@ class SpecialRedirect extends FormSpecialPage { foreach ( $logparams as $cond ) { $matchedRows = []; foreach ( $logsSameTimestamps as $row ) { + if ( !LogEventsList::userCan( + $rowMain, $fieldToBitsMapping[$cond], $user + ) ) { + // This field is redacted for main log entry, + // so treat as always matching. + $matchedRows[] = $row; + continue; + } + if ( !LogEventsList::userCan( + $row, $fieldToBitsMapping[$cond], $user + ) ) { + // The comparision row is redacted, but the main + // row is not. Treat this as a non-match. + continue; + } if ( $row->$cond === $rowMain->$cond ) { $matchedRows[] = $row; } @@ -239,7 +269,11 @@ class SpecialRedirect extends FormSpecialPage { ]; foreach ( $logparams as $logKey ) { - $query[$keys[$logKey]] = $matchedRows[0]->$logKey; + if ( LogEventsList::userCan( + $rowMain, $fieldToBitsMapping[$logKey], $user + ) ) { + $query[$keys[$logKey]] = $matchedRows[0]->$logKey; + } } $query['offset'] = $query['offset'] + 1; $url = $query; -- 2.8.1