* @return string html <script> and <style> tags
*/
protected function makeResourceLoaderLink( $modules, $only, $useESI = false, array $extraQuery = array(), $loadCall = false ) {
- global $wgResourceLoaderUseESI, $wgResourceLoaderInlinePrivateModules;
+ global $wgResourceLoaderUseESI;
if ( !count( $modules ) ) {
return '';
continue;
}
- // Support inlining of private modules if configured as such. Note that these
- // modules should be loaded from getHeadScripts() before the first loader call.
- // Otherwise other modules can't properly use them as dependencies (bug 30914)
- if ( $group === 'private' && $wgResourceLoaderInlinePrivateModules ) {
+ // Inline private modules. These can't be loaded through load.php for security
+ // reasons, see bug 34907. Note that these modules should be loaded from
+ // getHeadScripts() before the first loader call. Otherwise other modules can't
+ // properly use them as dependencies (bug 30914)
+ if ( $group === 'private' ) {
if ( $only == ResourceLoaderModule::TYPE_STYLES ) {
$links .= Html::inlineStyle(
$resourceLoader->makeModuleResponse( $context, $modules )
$cache->set( $key, $result );
} catch ( Exception $exception ) {
// Return exception as a comment
- $result = "/*\n{$exception->__toString()}\n*/\n";
+ $result = $this->makeComment( $exception->__toString() );
}
wfProfileOut( __METHOD__ );
ob_start();
wfProfileIn( __METHOD__ );
- $exceptions = '';
+ $errors = '';
// Split requested modules into two groups, modules and missing
$modules = array();
$missing = array();
foreach ( $context->getModules() as $name ) {
if ( isset( $this->moduleInfos[$name] ) ) {
+ $module = $this->getModule( $name );
+ // Do not allow private modules to be loaded from the web.
+ // This is a security issue, see bug 34907.
+ if ( $module->getGroup() === 'private' ) {
+ $errors .= $this->makeComment( "Cannot show private module \"$name\"" );
+ continue;
+ }
$modules[$name] = $this->getModule( $name );
} else {
$missing[] = $name;
$this->preloadModuleInfo( array_keys( $modules ), $context );
} catch( Exception $e ) {
// Add exception to the output as a comment
- $exceptions .= "/*\n{$e->__toString()}\n*/\n";
+ $errors .= $this->makeComment( $e->__toString() );
}
wfProfileIn( __METHOD__.'-getModifiedTime' );
- $private = false;
// To send Last-Modified and support If-Modified-Since, we need to detect
// the last modified time
$mtime = wfTimestamp( TS_UNIX, $wgCacheEpoch );
* @var $module ResourceLoaderModule
*/
try {
- // Bypass Squid and other shared caches if the request includes any private modules
- if ( $module->getGroup() === 'private' ) {
- $private = true;
- }
// Calculate maximum modified time
$mtime = max( $mtime, $module->getModifiedTime( $context ) );
} catch ( Exception $e ) {
// Add exception to the output as a comment
- $exceptions .= "/*\n{$e->__toString()}\n*/\n";
+ $errors .= $this->makeComment( $e->__toString() );
}
}
wfProfileOut( __METHOD__.'-getModifiedTime' );
// Send content type and cache related headers
- $this->sendResponseHeaders( $context, $mtime, $private );
+ $this->sendResponseHeaders( $context, $mtime );
// If there's an If-Modified-Since header, respond with a 304 appropriately
if ( $this->tryRespondLastModified( $context, $mtime ) ) {
$response = $this->makeModuleResponse( $context, $modules, $missing );
// Prepend comments indicating exceptions
- $response = $exceptions . $response;
+ $response = $errors . $response;
// Capture any PHP warnings from the output buffer and append them to the
// response in a comment if we're in debug mode.
if ( $context->getDebug() && strlen( $warnings = ob_get_contents() ) ) {
- $response = "/*\n$warnings\n*/\n" . $response;
+ $response = $this->makeComment( $warnings ) . $response;
}
// Remove the output buffer and output the response
ob_end_clean();
echo $response;
- // Save response to file cache unless there are private modules or errors
- if ( isset( $fileCache ) && !$private && !$exceptions && !$missing ) {
+ // Save response to file cache unless there are errors
+ if ( isset( $fileCache ) && !$errors && !$missing ) {
// Cache single modules...and other requests if there are enough hits
if ( ResourceFileCache::useFileCache( $context ) ) {
if ( $fileCache->isCacheWorthy() ) {
* Send content type and last modified headers to the client.
* @param $context ResourceLoaderContext
* @param $mtime string TS_MW timestamp to use for last-modified
- * @param $private bool True iff response contains any private modules
* @return void
*/
- protected function sendResponseHeaders( ResourceLoaderContext $context, $mtime, $private ) {
+ protected function sendResponseHeaders( ResourceLoaderContext $context, $mtime ) {
global $wgResourceLoaderMaxage;
// If a version wasn't specified we need a shorter expiry time for updates
// to propagate to clients quickly
header( 'Cache-Control: private, no-cache, must-revalidate' );
header( 'Pragma: no-cache' );
} else {
- if ( $private ) {
- header( "Cache-Control: private, max-age=$maxage" );
- $exp = $maxage;
- } else {
- header( "Cache-Control: public, max-age=$maxage, s-maxage=$smaxage" );
- $exp = min( $maxage, $smaxage );
- }
+ header( "Cache-Control: public, max-age=$maxage, s-maxage=$smaxage" );
+ $exp = min( $maxage, $smaxage );
header( 'Expires: ' . wfTimestamp( TS_RFC2822, $exp + time() ) );
}
}
return false; // cache miss
}
+ protected function makeComment( $text ) {
+ $encText = str_replace( '*/', '* /', $text );
+ return "/*\n$encText\n*/\n";
+ }
+
/**
* Generates code for a response
*
$blobs = MessageBlobStore::get( $this, $modules, $context->getLanguage() );
} catch ( Exception $e ) {
// Add exception to the output as a comment
- $exceptions .= "/*\n{$e->__toString()}\n*/\n";
+ $exceptions .= $this->makeComment( $e->__toString() );
}
} else {
$blobs = array();
}
} catch ( Exception $e ) {
// Add exception to the output as a comment
- $exceptions .= "/*\n{$e->__toString()}\n*/\n";
+ $exceptions .= $this->makeComment( $e->__toString() );
// Register module as missing
$missing[] = $name;