X-Git-Url: https://git.heureux-cyclage.org/?p=lhc%2Fweb%2Fwiklou.git;a=blobdiff_plain;f=includes%2Factions%2FRawAction.php;h=159e7081a4c00e49b653f599154f10f51c5c9342;hp=5bf24f60e683ccb0b3393d7310aa4b13d3619c6f;hb=90232b6f36ee5a1473f2e865cc7a72d0014db4c7;hpb=57eaa2bf04ce1b48bd89c10defe4de5b7d31f047 diff --git a/includes/actions/RawAction.php b/includes/actions/RawAction.php index 5bf24f60e6..159e7081a4 100644 --- a/includes/actions/RawAction.php +++ b/includes/actions/RawAction.php @@ -26,6 +26,8 @@ * @file */ +use MediaWiki\Logger\LoggerFactory; + /** * A simple method to retrieve the plain source of an article, * using "action=raw" in the GET request string. @@ -59,20 +61,19 @@ class RawAction extends FormlessAction { return; // Client cache fresh and headers sent, nothing more to do. } - $gen = $request->getVal( 'gen' ); - if ( $gen == 'css' || $gen == 'js' ) { - $this->gen = true; - } - $contentType = $this->getContentType(); $maxage = $request->getInt( 'maxage', $config->get( 'SquidMaxage' ) ); $smaxage = $request->getIntOrNull( 'smaxage' ); if ( $smaxage === null ) { - if ( $contentType == 'text/css' || $contentType == 'text/javascript' ) { - // CSS/JS raw content has its own CDN max age configuration. - // Note: Title::getCdnUrls() includes action=raw for css/js pages, - // so if using the canonical url, this will get HTCP purges. + if ( + $contentType == 'text/css' || + $contentType == 'application/json' || + $contentType == 'text/javascript' + ) { + // CSS/JSON/JS raw content has its own CDN max age configuration. + // Note: Title::getCdnUrls() includes action=raw for css/json/js + // pages, so if using the canonical url, this will get HTCP purges. $smaxage = intval( $config->get( 'ForcedRawSMaxage' ) ); } else { // No CDN cache for anything else @@ -86,7 +87,6 @@ class RawAction extends FormlessAction { $response->header( $this->getOutput()->getKeyHeader() ); } - $response->header( 'Content-type: ' . $contentType . '; charset=UTF-8' ); // Output may contain user-specific data; // vary generated content for open sessions on private wikis $privateCache = !User::isEveryoneAllowed( 'read' ) && @@ -98,6 +98,33 @@ class RawAction extends FormlessAction { 'Cache-Control: ' . $mode . ', s-maxage=' . $smaxage . ', max-age=' . $maxage ); + // In the event of user JS, don't allow loading a user JS/CSS/Json + // subpage that has no registered user associated with, as + // someone could register the account and take control of the + // JS/CSS/Json page. + $title = $this->getTitle(); + if ( $title->isUserConfigPage() && $contentType !== 'text/x-wiki' ) { + // not using getRootText() as we want this to work + // even if subpages are disabled. + $rootPage = strtok( $title->getText(), '/' ); + $userFromTitle = User::newFromName( $rootPage, 'usable' ); + if ( !$userFromTitle || $userFromTitle->getId() === 0 ) { + $log = LoggerFactory::getInstance( "security" ); + $log->warning( + "Unsafe JS/CSS/Json load - {user} loaded {title} with {ctype}", + [ + 'user' => $this->getUser()->getName(), + 'title' => $title->getPrefixedDBKey(), + 'ctype' => $contentType, + ] + ); + $msg = wfMessage( 'unregistered-user-config' ); + throw new HttpError( 403, $msg ); + } + } + + $response->header( 'Content-type: ' . $contentType . '; charset=UTF-8' ); + $text = $this->getRawText(); // Don't return a 404 response for CSS or JavaScript; @@ -108,7 +135,9 @@ class RawAction extends FormlessAction { $response->statusHeader( 404 ); } - if ( !Hooks::run( 'RawPageViewBeforeOutput', [ &$this, &$text ] ) ) { + // Avoid PHP 7.1 warning of passing $this by reference + $rawAction = $this; + if ( !Hooks::run( 'RawPageViewBeforeOutput', [ &$rawAction, &$text ] ) ) { wfDebug( __METHOD__ . ": RawPageViewBeforeOutput hook broke raw page output.\n" ); } @@ -164,7 +193,7 @@ class RawAction extends FormlessAction { } if ( $content === null || $content === false ) { - // section not found (or section not supported, e.g. for JS and CSS) + // section not found (or section not supported, e.g. for JS, JSON, and CSS) $text = false; } else { $text = $content->getNativeData(); @@ -173,7 +202,7 @@ class RawAction extends FormlessAction { } } - if ( $text !== false && $text !== '' && $request->getVal( 'templates' ) === 'expand' ) { + if ( $text !== false && $text !== '' && $request->getRawVal( 'templates' ) === 'expand' ) { $text = $wgParser->preprocess( $text, $title, @@ -223,10 +252,14 @@ class RawAction extends FormlessAction { * @return string */ public function getContentType() { - $ctype = $this->getRequest()->getVal( 'ctype' ); + // Use getRawVal instead of getVal because we only + // need to match against known strings, there is no + // storing of localised content or other user input. + $ctype = $this->getRequest()->getRawVal( 'ctype' ); if ( $ctype == '' ) { - $gen = $this->getRequest()->getVal( 'gen' ); + // Legacy compatibilty + $gen = $this->getRequest()->getRawVal( 'gen' ); if ( $gen == 'js' ) { $ctype = 'text/javascript'; } elseif ( $gen == 'css' ) { @@ -234,7 +267,14 @@ class RawAction extends FormlessAction { } } - $allowedCTypes = [ 'text/x-wiki', 'text/javascript', 'text/css', 'application/x-zope-edit' ]; + $allowedCTypes = [ + 'text/x-wiki', + 'text/javascript', + 'text/css', + // FIXME: Should we still allow Zope editing? External editing feature was dropped + 'application/x-zope-edit', + 'application/json' + ]; if ( $ctype == '' || !in_array( $ctype, $allowedCTypes ) ) { $ctype = 'text/x-wiki'; }