ContentSecurityPolicyManager.php 2.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172
  1. <?php
  2. declare(strict_types=1);
  3. /**
  4. * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
  5. * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
  6. * SPDX-License-Identifier: AGPL-3.0-only
  7. */
  8. namespace OC\Security\CSP;
  9. use OCP\AppFramework\Http\ContentSecurityPolicy;
  10. use OCP\AppFramework\Http\EmptyContentSecurityPolicy;
  11. use OCP\EventDispatcher\IEventDispatcher;
  12. use OCP\Security\CSP\AddContentSecurityPolicyEvent;
  13. use OCP\Security\IContentSecurityPolicyManager;
  14. class ContentSecurityPolicyManager implements IContentSecurityPolicyManager {
  15. /** @var ContentSecurityPolicy[] */
  16. private array $policies = [];
  17. public function __construct(
  18. private IEventDispatcher $dispatcher,
  19. ) {
  20. }
  21. /** {@inheritdoc} */
  22. public function addDefaultPolicy(EmptyContentSecurityPolicy $policy): void {
  23. $this->policies[] = $policy;
  24. }
  25. /**
  26. * Get the configured default policy. This is not in the public namespace
  27. * as it is only supposed to be used by core itself.
  28. */
  29. public function getDefaultPolicy(): ContentSecurityPolicy {
  30. $event = new AddContentSecurityPolicyEvent($this);
  31. $this->dispatcher->dispatchTyped($event);
  32. $defaultPolicy = new \OC\Security\CSP\ContentSecurityPolicy();
  33. foreach ($this->policies as $policy) {
  34. $defaultPolicy = $this->mergePolicies($defaultPolicy, $policy);
  35. }
  36. return $defaultPolicy;
  37. }
  38. /**
  39. * Merges the first given policy with the second one
  40. */
  41. public function mergePolicies(
  42. ContentSecurityPolicy $defaultPolicy,
  43. EmptyContentSecurityPolicy $originalPolicy,
  44. ): ContentSecurityPolicy {
  45. foreach ((object)(array)$originalPolicy as $name => $value) {
  46. $setter = 'set'.ucfirst($name);
  47. if (\is_array($value)) {
  48. $getter = 'get'.ucfirst($name);
  49. $currentValues = \is_array($defaultPolicy->$getter()) ? $defaultPolicy->$getter() : [];
  50. $defaultPolicy->$setter(array_values(array_unique(array_merge($currentValues, $value))));
  51. } elseif (\is_bool($value)) {
  52. $getter = 'is'.ucfirst($name);
  53. $currentValue = $defaultPolicy->$getter();
  54. // true wins over false
  55. if ($value > $currentValue) {
  56. $defaultPolicy->$setter($value);
  57. }
  58. }
  59. }
  60. return $defaultPolicy;
  61. }
  62. }