$additionalSelfUrls = $this->getAdditionalSelfUrls();
$additionalSelfUrlsScript = $this->getAdditionalSelfUrlsScript();
- $nonceSrc = "'nonce-" . $this->nonce . "'";
// If no default-src is sent at all, it
// seems browsers (or at least some), interpret
$cssSrc = false;
$imgSrc = false;
$scriptSrc = [ "'unsafe-eval'", "'self'" ];
- if ( $mode !== self::FULL_MODE_RESTRICTED ) {
+ if (
+ $mode !== self::FULL_MODE_RESTRICTED &&
+ ( !isset( $policyConfig['useNonces'] ) || $policyConfig['useNonces'] )
+ ) {
+ $nonceSrc = "'nonce-" . $this->nonce . "'";
$scriptSrc[] = $nonceSrc;
}
$scriptSrc = array_merge( $scriptSrc, $additionalSelfUrlsScript );
}
/**
- * Is CSP currently enabled (i.e. Should we set nonce attribute)
+ * Should we set nonce attribute
*
* @param Config $config Configuration object
* @return bool
*/
- public static function isEnabled( Config $config ) {
- return $config->get( 'CSPHeader' ) !== false
- || $config->get( 'CSPReportOnlyHeader' ) !== false;
+ public static function isNonceRequired( Config $config ) {
+ $configs = [
+ $config->get( 'CSPHeader' ),
+ $config->get( 'CSPReportOnlyHeader' )
+ ];
+ foreach ( $configs as $headerConfig ) {
+ if (
+ $headerConfig === true ||
+ ( is_array( $headerConfig ) &&
+ !isset( $headerConfig['useNonces'] ) ) ||
+ ( is_array( $headerConfig ) &&
+ isset( $headerConfig['useNonces'] ) &&
+ $headerConfig['useNonces'] )
+ ) {
+ return true;
+ }
+ }
+ return false;
}
}
* $wgCrossSiteAJAXdomains as an allowed load sources.
* 'unsafeFallback' Add unsafe-inline as a script source, as a fallback for
* browsers that do not understand nonce-sources [default on].
+ * 'useNonces' Require nonces on all inline scripts. If disabled and 'unsafeFallback'
+ * is on, then all inline scripts will be allowed [default true].
* 'script-src' Array of additional places that are allowed to have JS be loaded from.
* 'report-uri' true to use MW api [default], false to disable, string for alternate uri
* @warning May cause slowness on windows due to slow random number generator.
if ( $nonce !== null ) {
$attrs['nonce'] = $nonce;
} else {
- if ( ContentSecurityPolicy::isEnabled( RequestContext::getMain()->getConfig() ) ) {
+ if ( ContentSecurityPolicy::isNonceRequired( RequestContext::getMain()->getConfig() ) ) {
wfWarn( "no nonce set on script. CSP will break it" );
}
}
if ( $nonce !== null ) {
$attrs['nonce'] = $nonce;
} else {
- if ( ContentSecurityPolicy::isEnabled( RequestContext::getMain()->getConfig() ) ) {
+ if ( ContentSecurityPolicy::isNonceRequired( RequestContext::getMain()->getConfig() ) ) {
wfWarn( "no nonce set on script. CSP will break it" );
}
}
* @since 1.32
*/
public function getCSPNonce() {
- if ( !ContentSecurityPolicy::isEnabled( $this->getConfig() ) ) {
+ if ( !ContentSecurityPolicy::isNonceRequired( $this->getConfig() ) ) {
return false;
}
if ( $this->CSPNonce === null ) {
// @codingStandardsIgnoreStart Generic.Files.LineLength
return [
[ false, '', '', '' ],
+ [
+ [ 'useNonces' => false ],
+ "script-src 'unsafe-eval' 'self' 'unsafe-inline' sister-site.somewhere.com *.wikipedia.org; default-src * data: blob:; style-src * data: blob: 'unsafe-inline'; report-uri /w/api.php?action=cspreport&format=json&",
+ "script-src 'unsafe-eval' 'self' 'unsafe-inline' sister-site.somewhere.com *.wikipedia.org; default-src * data: blob:; style-src * data: blob: 'unsafe-inline'; report-uri /w/api.php?action=cspreport&format=json&reportonly=1&",
+ "script-src 'unsafe-eval' 'self' sister-site.somewhere.com *.wikipedia.org; default-src * data: blob:; style-src * data: blob: 'unsafe-inline'"
+ ],
[
true,
"script-src 'unsafe-eval' 'self' 'nonce-secret' 'unsafe-inline' sister-site.somewhere.com *.wikipedia.org; default-src * data: blob:; style-src * data: blob: 'unsafe-inline'; report-uri /w/api.php?action=cspreport&format=json&",
/**
* @dataProvider providerCSPIsEnabled
- * @covers ContentSecurityPolicy::isEnabled
+ * @covers ContentSecurityPolicy::isNonceRequired
*/
public function testCSPIsEnabled( $main, $reportOnly, $expected ) {
global $wgCSPReportOnlyHeader, $wgCSPHeader;
global $wgCSPHeader;
$oldReport = wfSetVar( $wgCSPReportOnlyHeader, $reportOnly );
$oldMain = wfSetVar( $wgCSPHeader, $main );
- $res = ContentSecurityPolicy::isEnabled( RequestContext::getMain()->getConfig() );
+ $res = ContentSecurityPolicy::isNonceRequired( RequestContext::getMain()->getConfig() );
wfSetVar( $wgCSPReportOnlyHeader, $oldReport );
wfSetVar( $wgCSPHeader, $oldMain );
$this->assertEquals( $res, $expected );
[ false, [], true ],
[ [], false, true ],
[ [ 'default-src' => [ 'foo.example.com' ] ], false, true ],
+ [ [ 'useNonces' => false ], [ 'useNonces' => false ], false ],
+ [ [ 'useNonces' => true ], [ 'useNonces' => false ], true ],
+ [ [ 'useNonces' => false ], [ 'useNonces' => true ], true ],
];
}
}