Skip to content
Snippets Groups Projects
Commit 8c582d47 authored by Geoff Appleby's avatar Geoff Appleby
Browse files

Issue #3099423: Helper for appending sources to directives with fallback

parent 2d9452c1
No related branches found
Tags 7.x-1.0
No related merge requests found
......@@ -313,6 +313,43 @@ class Csp {
$this->directives[$name] = array_merge($this->directives[$name], $value);
}
/**
* Append to a directive if it or a fallback directive is enabled.
*
* If the specified directive is not enabled but one of its fallback
* directives is, it will be initialized with the same value as the fallback
* before appending the new value.
*
* If none of the specified directive's fallbacks are enabled, the directive
* will not be enabled.
*
* @param string $name
* The directive name.
* @param array|string $value
* The directive value.
*/
public function fallbackAwareAppendIfEnabled($name, $value) {
self::validateDirectiveName($name);
if (!$this->hasDirective($name)) {
// Duplicate the closest fallback directive with a value.
foreach (self::getDirectiveFallbackList($name) as $fallback) {
if ($this->hasDirective($fallback)) {
$fallbackSourceList = $this->getDirective($fallback);
if (in_array(static::POLICY_NONE, $fallbackSourceList)) {
$fallbackSourceList = [];
}
$this->setDirective($name, $fallbackSourceList);
break;
}
}
}
if ($this->hasDirective($name)) {
$this->appendDirective($name, $value);
}
}
/**
* Remove a directive from the policy.
*
......
......@@ -259,6 +259,101 @@ class CspTest extends UnitTestCase {
);
}
/**
* Appending to a directive if it or a fallback is enabled.
*
* @covers ::fallbackAwareAppendIfEnabled
*/
public function testFallbackAwareAppendIfEnabled() {
// If no relevant directives are enabled, they should not change.
$policy = new Csp();
$policy->setDirective('style-src', Csp::POLICY_SELF);
$policy->fallbackAwareAppendIfEnabled('script-src-attr', Csp::POLICY_UNSAFE_INLINE);
$this->assertFalse($policy->hasDirective('default-src'));
$this->assertFalse($policy->hasDirective('script-src'));
$this->assertFalse($policy->hasDirective('script-src-attr'));
// Script-src-attr should copy value from default-src. Script-src should
// not be changed.
$policy = new Csp();
$policy->setDirective('default-src', Csp::POLICY_SELF);
$policy->fallbackAwareAppendIfEnabled('script-src-attr', Csp::POLICY_UNSAFE_INLINE);
$this->assertEquals(
[Csp::POLICY_SELF],
$policy->getDirective('default-src')
);
$this->assertFalse($policy->hasDirective('script-src'));
$this->assertEquals(
[Csp::POLICY_SELF, Csp::POLICY_UNSAFE_INLINE],
$policy->getDirective('script-src-attr')
);
// Script-src-attr should copy value from script-src.
$policy = new Csp();
$policy->setDirective('script-src', Csp::POLICY_SELF);
$policy->fallbackAwareAppendIfEnabled('script-src-attr', Csp::POLICY_UNSAFE_INLINE);
$this->assertFalse($policy->hasDirective('default-src'));
$this->assertEquals(
[Csp::POLICY_SELF],
$policy->getDirective('script-src')
);
$this->assertEquals(
[Csp::POLICY_SELF, Csp::POLICY_UNSAFE_INLINE],
$policy->getDirective('script-src-attr')
);
// Script-src-attr should only append to existing value if enabled.
$policy = new Csp();
$policy->setDirective('script-src', Csp::POLICY_SELF);
$policy->setDirective('script-src-attr', []);
$policy->fallbackAwareAppendIfEnabled('script-src-attr', Csp::POLICY_UNSAFE_INLINE);
$this->assertFalse($policy->hasDirective('default-src'));
$this->assertEquals(
[Csp::POLICY_SELF],
$policy->getDirective('script-src')
);
$this->assertEquals(
[Csp::POLICY_UNSAFE_INLINE],
$policy->getDirective('script-src-attr')
);
}
/**
* Appending to a directive if its fallback includes 'none'.
*
* @covers ::fallbackAwareAppendIfEnabled
*/
public function testFallbackAwareAppendIfEnabledNone() {
// New directive should be enabled with only provided value.
$policy = new Csp();
$policy->setDirective('default-src', Csp::POLICY_NONE);
$policy->fallbackAwareAppendIfEnabled(
'script-src-attr',
Csp::POLICY_UNSAFE_INLINE
);
$this->assertEquals(
[Csp::POLICY_NONE],
$policy->getDirective('default-src')
);
$this->assertFalse($policy->hasDirective('script-src'));
$this->assertEquals(
[Csp::POLICY_UNSAFE_INLINE],
$policy->getDirective('script-src-attr')
);
// Additional values in fallback should be ignored if 'none' is present.
$policy = new Csp();
$policy->setDirective('script-src', [Csp::POLICY_NONE, 'https://example.org']);
$policy->fallbackAwareAppendIfEnabled(
'script-src-attr',
Csp::POLICY_UNSAFE_INLINE
);
$this->assertEquals(
[Csp::POLICY_UNSAFE_INLINE],
$policy->getDirective('script-src-attr')
);
}
/**
* Test that source values are not repeated in the header.
*
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment