* @file
*/
+use MediaWiki\Logger\LoggerFactory;
+
/**
* A simple method to retrieve the plain source of an article,
* using "action=raw" in the GET request string.
return false;
}
+ /**
+ * @suppress SecurityCheck-XSS Non html mime type
+ */
function onView() {
$this->getOutput()->disable();
$request = $this->getRequest();
$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
$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' ) &&
'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 ) {
+ $elevated = $this->getUser()->isAllowed( 'editinterface' );
+ $elevatedText = $elevated ? 'by elevated ' : '';
+ $log = LoggerFactory::getInstance( "security" );
+ $log->warning(
+ "Unsafe JS/CSS/Json $elevatedText" . "load - {user} loaded {title} with {ctype}",
+ [
+ 'user' => $this->getUser()->getName(),
+ 'title' => $title->getPrefixedDBKey(),
+ 'ctype' => $contentType,
+ 'elevated' => $elevated
+ ]
+ );
+ $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;
$title = $this->getTitle();
$request = $this->getRequest();
- // If it's a MediaWiki message we can just hit the message cache
- if ( $request->getBool( 'usemsgcache' ) && $title->getNamespace() == NS_MEDIAWIKI ) {
- // The first "true" is to use the database, the second is to use
- // the content langue and the last one is to specify the message
- // key already contains the language in it ("/de", etc.).
- $text = MessageCache::singleton()->get( $title->getDBkey(), true, true, true );
- // If the message doesn't exist, return a blank
- if ( $text === false ) {
- $text = '';
- }
- } else {
- // Get it from the DB
- $rev = Revision::newFromTitle( $title, $this->getOldId() );
- if ( $rev ) {
- $lastmod = wfTimestamp( TS_RFC2822, $rev->getTimestamp() );
- $request->response()->header( "Last-modified: $lastmod" );
-
- // Public-only due to cache headers
- $content = $rev->getContent();
-
- if ( $content === null ) {
- // revision not found (or suppressed)
+ // Get it from the DB
+ $rev = Revision::newFromTitle( $title, $this->getOldId() );
+ if ( $rev ) {
+ $lastmod = wfTimestamp( TS_RFC2822, $rev->getTimestamp() );
+ $request->response()->header( "Last-modified: $lastmod" );
+
+ // Public-only due to cache headers
+ $content = $rev->getContent();
+
+ if ( $content === null ) {
+ // revision not found (or suppressed)
+ $text = false;
+ } elseif ( !$content instanceof TextContent ) {
+ // non-text content
+ wfHttpError( 415, "Unsupported Media Type", "The requested page uses the content model `"
+ . $content->getModel() . "` which is not supported via this interface." );
+ die();
+ } else {
+ // want a section?
+ $section = $request->getIntOrNull( 'section' );
+ if ( $section !== null ) {
+ $content = $content->getSection( $section );
+ }
+
+ if ( $content === null || $content === false ) {
+ // section not found (or section not supported, e.g. for JS, JSON, and CSS)
$text = false;
- } elseif ( !$content instanceof TextContent ) {
- // non-text content
- wfHttpError( 415, "Unsupported Media Type", "The requested page uses the content model `"
- . $content->getModel() . "` which is not supported via this interface." );
- die();
} else {
- // want a section?
- $section = $request->getIntOrNull( 'section' );
- if ( $section !== null ) {
- $content = $content->getSection( $section );
- }
-
- if ( $content === null || $content === false ) {
- // section not found (or section not supported, e.g. for JS and CSS)
- $text = false;
- } else {
- $text = $content->getNativeData();
- }
+ $text = $content->getNativeData();
}
}
}
'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'
];