Merge "Re-enable Generic.Files.OneObjectStructurePerFile sniff"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Sat, 19 May 2018 21:26:35 +0000 (21:26 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Sat, 19 May 2018 21:26:35 +0000 (21:26 +0000)
includes/OutputPage.php
includes/resourceloader/ResourceLoaderClientHtml.php
includes/resourceloader/ResourceLoaderModule.php
includes/resourceloader/ResourceLoaderStartUpModule.php
tests/phpunit/ResourceLoaderTestCase.php
tests/phpunit/includes/resourceloader/ResourceLoaderClientHtmlTest.php
tests/phpunit/includes/resourceloader/ResourceLoaderStartUpModuleTest.php

index dd1a4db..c7028db 100644 (file)
@@ -2443,7 +2443,7 @@ class OutputPage extends ContextSource {
                if ( $this->mArticleBodyOnly ) {
                        echo $this->mBodytext;
                } else {
-                       // Enable safe mode if requested
+                       // Enable safe mode if requested (T152169)
                        if ( $this->getRequest()->getBool( 'safemode' ) ) {
                                $this->disallowUserJs();
                        }
@@ -2862,6 +2862,16 @@ class OutputPage extends ContextSource {
                        $rlClient = new ResourceLoaderClientHtml( $context, [
                                'target' => $this->getTarget(),
                                'nonce' => $this->getCSPNonce(),
+                               // When 'safemode', disallowUserJs(), or reduceAllowedModules() is used
+                               // to only restrict modules to ORIGIN_CORE (ie. disallow ORIGIN_USER), the list of
+                               // modules enqueud for loading on this page is filtered to just those.
+                               // However, to make sure we also apply the restriction to dynamic dependencies and
+                               // lazy-loaded modules at run-time on the client-side, pass 'safemode' down to the
+                               // StartupModule so that the client-side registry will not contain any restricted
+                               // modules either. (T152169, T185303)
+                               'safemode' => ( $this->getAllowedModules( ResourceLoaderModule::TYPE_COMBINED )
+                                       <= ResourceLoaderModule::ORIGIN_CORE_INDIVIDUAL
+                               ) ? '1' : null,
                        ] );
                        $rlClient->setConfig( $this->getJSVars() );
                        $rlClient->setModules( $this->getModules( /*filter*/ true ) );
index 479a263..3ba63cf 100644 (file)
@@ -57,7 +57,8 @@ class ResourceLoaderClientHtml {
        /**
         * @param ResourceLoaderContext $context
         * @param array $options [optional] Array of options
-        *  - 'target': Custom parameter passed to StartupModule.
+        *  - 'target': Parameter for modules=startup request, see ResourceLoaderStartUpModule.
+        *  - 'safemode': Parameter for modules=startup request, see ResourceLoaderStartUpModule.
         *  - 'nonce': From OutputPage::getCSPNonce().
         */
        public function __construct( ResourceLoaderContext $context, array $options = [] ) {
@@ -65,6 +66,7 @@ class ResourceLoaderClientHtml {
                $this->resourceLoader = $context->getResourceLoader();
                $this->options = $options + [
                        'target' => null,
+                       'safemode' => null,
                        'nonce' => null,
                ];
        }
@@ -344,9 +346,12 @@ class ResourceLoaderClientHtml {
 
                // Async scripts. Once the startup is loaded, inline RLQ scripts will run.
                // Pass-through a custom 'target' from OutputPage (T143066).
-               $startupQuery = $this->options['target'] !== null
-                       ? [ 'target' => (string)$this->options['target'] ]
-                       : [];
+               $startupQuery = [];
+               foreach ( [ 'target', 'safemode' ] as $param ) {
+                       if ( $this->options[$param] !== null ) {
+                               $startupQuery[$param] = (string)$this->options[$param];
+                       }
+               }
                $chunks[] = $this->getLoad(
                        'startup',
                        ResourceLoaderModule::TYPE_SCRIPTS,
index 7351cb3..a0a4e58 100644 (file)
@@ -318,9 +318,9 @@ abstract class ResourceLoaderModule implements LoggerAwareInterface {
        }
 
        /**
-        * Get the origin of this module. Should only be overridden for foreign modules.
+        * Get the source of this module. Should only be overridden for foreign modules.
         *
-        * @return string Origin name, 'local' for local modules
+        * @return string Source name, 'local' for local modules
         */
        public function getSource() {
                // Stub, override expected
index 2e3c6fc..6b92f77 100644 (file)
  * the ability to vary based extra query parameters, in addition to those
  * from ResourceLoaderContext:
  *
- * - target: Only register modules in the client allowed within this target.
+ * - target: Only register modules in the client intended for this target.
  *   Default: "desktop".
  *   See also: OutputPage::setTarget(), ResourceLoaderModule::getTargets().
+ *
+ * - safemode: Only register modules that have ORIGIN_CORE as their origin.
+ *   This effectively disables ORIGIN_USER modules. (T185303)
+ *   See also: OutputPage::disallowUserJs()
  */
 class ResourceLoaderStartUpModule extends ResourceLoaderModule {
 
@@ -208,6 +212,7 @@ class ResourceLoaderStartUpModule extends ResourceLoaderModule {
                // Future developers: Use WebRequest::getRawVal() instead getVal().
                // The getVal() method performs slow Language+UTF logic. (f303bb9360)
                $target = $context->getRequest()->getRawVal( 'target', 'desktop' );
+               $safemode = $context->getRequest()->getRawVal( 'safemode' ) === '1';
                // Bypass target filter if this request is Special:JavaScriptTest.
                // To prevent misuse in production, this is only allowed if testing is enabled server-side.
                $byPassTargetFilter = $this->getConfig()->get( 'EnableJavaScriptTest' ) && $target === 'test';
@@ -220,7 +225,10 @@ class ResourceLoaderStartUpModule extends ResourceLoaderModule {
                foreach ( $resourceLoader->getModuleNames() as $name ) {
                        $module = $resourceLoader->getModule( $name );
                        $moduleTargets = $module->getTargets();
-                       if ( !$byPassTargetFilter && !in_array( $target, $moduleTargets ) ) {
+                       if (
+                               ( !$byPassTargetFilter && !in_array( $target, $moduleTargets ) )
+                               || ( $safemode && $module->getOrigin() > ResourceLoaderModule::ORIGIN_CORE_INDIVIDUAL )
+                       ) {
                                continue;
                        }
 
index d5c14a2..f0c78ec 100644 (file)
@@ -31,12 +31,14 @@ abstract class ResourceLoaderTestCase extends MediaWikiTestCase {
                        'skin' => 'vector',
                        'modules' => 'startup',
                        'only' => 'scripts',
+                       'safemode' => null,
                ];
                $resourceLoader = $rl ?: new ResourceLoader();
                $request = new FauxRequest( [
                                'lang' => $options['lang'],
                                'modules' => $options['modules'],
                                'only' => $options['only'],
+                               'safemode' => $options['safemode'],
                                'skin' => $options['skin'],
                                'target' => 'phpunit',
                ] );
index 5b5c484..9b03c5c 100644 (file)
@@ -258,6 +258,25 @@ Deprecation message.' ]
                $this->assertEquals( $expected, $client->getHeadHtml() );
        }
 
+       /**
+        * Confirm that 'safemode' is passed down to startup.
+        *
+        * @covers ResourceLoaderClientHtml::getHeadHtml
+        */
+       public function testGetHeadHtmlWithSafemode() {
+               $client = new ResourceLoaderClientHtml(
+                       self::makeContext(),
+                       [ 'safemode' => '1' ]
+               );
+
+               // phpcs:disable Generic.Files.LineLength
+               $expected = '<script>document.documentElement.className = document.documentElement.className.replace( /(^|\s)client-nojs(\s|$)/, "$1client-js$2" );</script>' . "\n"
+                       . '<script async="" src="/w/load.php?debug=false&amp;lang=nl&amp;modules=startup&amp;only=scripts&amp;safemode=1&amp;skin=fallback"></script>';
+               // phpcs:enable
+
+               $this->assertEquals( $expected, $client->getHeadHtml() );
+       }
+
        /**
         * Confirm that a null 'target' is the same as no target.
         *
index 564f50b..ca4fb34 100644 (file)
@@ -162,6 +162,75 @@ mw.loader.register( [
         "test.blank",
         "{blankVer}"
     ]
+] );'
+                       ] ],
+                       [ [
+                               'msg' => 'Safemode disabled (default; register all modules)',
+                               'modules' => [
+                                       // Default origin: ORIGIN_CORE_SITEWIDE
+                                       'test.blank' => new ResourceLoaderTestModule(),
+                                       'test.core-generated' => new ResourceLoaderTestModule( [
+                                               'origin' => ResourceLoaderModule::ORIGIN_CORE_INDIVIDUAL
+                                       ] ),
+                                       'test.sitewide' => new ResourceLoaderTestModule( [
+                                               'origin' => ResourceLoaderModule::ORIGIN_USER_SITEWIDE
+                                       ] ),
+                                       'test.user' => new ResourceLoaderTestModule( [
+                                               'origin' => ResourceLoaderModule::ORIGIN_USER_INDIVIDUAL
+                                       ] ),
+                               ],
+                               'out' => '
+mw.loader.addSource( {
+    "local": "/w/load.php"
+} );
+mw.loader.register( [
+    [
+        "test.blank",
+        "{blankVer}"
+    ],
+    [
+        "test.core-generated",
+        "{blankVer}"
+    ],
+    [
+        "test.sitewide",
+        "{blankVer}"
+    ],
+    [
+        "test.user",
+        "{blankVer}"
+    ]
+] );'
+                       ] ],
+                       [ [
+                               'msg' => 'Safemode enabled (filter modules with user/site origin)',
+                               'extraQuery' => [ 'safemode' => '1' ],
+                               'modules' => [
+                                       // Default origin: ORIGIN_CORE_SITEWIDE
+                                       'test.blank' => new ResourceLoaderTestModule(),
+                                       'test.core-generated' => new ResourceLoaderTestModule( [
+                                               'origin' => ResourceLoaderModule::ORIGIN_CORE_INDIVIDUAL
+                                       ] ),
+                                       'test.sitewide' => new ResourceLoaderTestModule( [
+                                               'origin' => ResourceLoaderModule::ORIGIN_USER_SITEWIDE
+                                       ] ),
+                                       'test.user' => new ResourceLoaderTestModule( [
+                                               'origin' => ResourceLoaderModule::ORIGIN_USER_INDIVIDUAL
+                                       ] ),
+                               ],
+                               'out' => '
+mw.loader.addSource( {
+    "local": "/w/load.php"
+} );
+mw.loader.register( [
+    [
+        "test.blank",
+        "{blankVer}"
+    ],
+    [
+        "test.core-generated",
+        "{blankVer}"
+    ]
 ] );'
                        ] ],
                        [ [
@@ -394,7 +463,8 @@ mw.loader.register( [
                        $this->setMwGlobals( 'wgResourceLoaderSources', $case['sources'] );
                }
 
-               $context = $this->getResourceLoaderContext();
+               $extraQuery = isset( $case['extraQuery'] ) ? $case['extraQuery'] : [];
+               $context = $this->getResourceLoaderContext( $extraQuery );
                $rl = $context->getResourceLoader();
                $rl->register( $case['modules'] );
                $module = new ResourceLoaderStartUpModule();