From 32cc25865873a0c74912e8182cf4e6a7236e230e Mon Sep 17 00:00:00 2001 From: Dreamy Jazz Date: Sun, 18 Feb 2024 23:13:16 +0000 Subject: [PATCH] SECURITY: Limit subpages displayed on Special:MovePage form Why: * Special:MovePage shows the list of subpages for the page provided before the user submits the form. * There is currently no limit on the number of subpages shown and as such loading the move page for a page with tens of thousands of subpages causes request timeouts when trying to generate the link HTML for each subpage. * Special:MovePage does not need to display all the subpages and can limit the list to wgMaximumMovedPages subpages as the user who submits the form would only move that many subpages if they specified to move these subpages. * A user wanting to find the full list can use Special:Prefix Index which provides paging. What: * Provide a $limit to Title::getSubpages for both calls in SpecialMovePage::showSubpages. $limit is defined as $wgMaximumMovedPages plus 1, where the extra subpage is used to determine if the results were truncated similar to IndexPager. * Because i18n modifications in security patches are to be strongly avoided, hard code the message shown when the list of subpages is truncated. This should be replaced with an actual message key when this is publicly uploaded to Gerrit. Bug: T357760 Change-Id: I78fa0b04d2bc82c8deffa2ed5433eb2563c17962 --- includes/specials/SpecialMovePage.php | 30 ++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/includes/specials/SpecialMovePage.php b/includes/specials/SpecialMovePage.php index d5b933d7db4..eed3e74a1d7 100644 --- a/includes/specials/SpecialMovePage.php +++ b/includes/specials/SpecialMovePage.php @@ -985,12 +985,13 @@ class SpecialMovePage extends UnlistedSpecialPage { * @param Title $title Page being moved. */ private function showSubpages( $title ) { + $maximumMovedPages = $this->getConfig()->get( MainConfigNames::MaximumMovedPages ); $nsHasSubpages = $this->nsInfo->hasSubpages( $title->getNamespace() ); - $subpages = $title->getSubpages(); + $subpages = $title->getSubpages( $maximumMovedPages + 1 ); $count = $subpages instanceof TitleArrayFromResult ? $subpages->count() : 0; $titleIsTalk = $title->isTalkPage(); - $subpagesTalk = $title->getTalkPage()->getSubpages(); + $subpagesTalk = $title->getTalkPage()->getSubpages( $maximumMovedPages + 1 ); $countTalk = $subpagesTalk instanceof TitleArrayFromResult ? $subpagesTalk->count() : 0; $totalCount = $count + $countTalk; @@ -1021,7 +1022,19 @@ class SpecialMovePage extends UnlistedSpecialPage { return; } - $out->addWikiMsg( $wikiMsg, $this->getLanguage()->formatNum( $pagecount ) ); + $maximumMovedPages = $this->getConfig()->get( MainConfigNames::MaximumMovedPages ); + + if ( $pagecount > $maximumMovedPages ) { + $subpages = $this->truncateSubpagesList( $subpages ); + // TODO: Replace with a message key once this is uploaded to Gerrit. This is hardcoded to avoid + // having the i18n rebuilt for all deployments due to this security patch. + $out->addWikiTextAsInterface( + "The first $maximumMovedPages {{PLURAL:$maximumMovedPages|subpage|subpages}} " . + ( $noSubpageMsg ? 'for this page' : 'for the corresponding talk page' ) . ' are shown below.' + ); + } else { + $out->addWikiMsg( $wikiMsg, $this->getLanguage()->formatNum( $pagecount ) ); + } $out->addHTML( "\n" ); } + private function truncateSubpagesList( iterable $subpages ): array { + $returnArray = []; + foreach ( $subpages as $subpage ) { + $returnArray[] = $subpage; + if ( count( $returnArray ) >= $this->getConfig()->get( MainConfigNames::MaximumMovedPages ) ) { + break; + } + } + return $returnArray; + } + /** * Return an array of subpages beginning with $search that this special page will accept. * -- 2.34.1