diff --git a/composer/composer/autoload_classmap.php b/composer/composer/autoload_classmap.php index 2e92308c27..cb1bac3a62 100644 --- a/composer/composer/autoload_classmap.php +++ b/composer/composer/autoload_classmap.php @@ -40,6 +40,7 @@ 'OCA\\Richdocuments\\Listener\\AddContentSecurityPolicyListener' => $baseDir . '/../lib/Listener/AddContentSecurityPolicyListener.php', 'OCA\\Richdocuments\\Listener\\AddFeaturePolicyListener' => $baseDir . '/../lib/Listener/AddFeaturePolicyListener.php', 'OCA\\Richdocuments\\Listener\\BeforeFetchPreviewListener' => $baseDir . '/../lib/Listener/BeforeFetchPreviewListener.php', + 'OCA\\Richdocuments\\Listener\\BeforeTemplateRenderedListener' => $baseDir . '/../lib/Listener/BeforeTemplateRenderedListener.php', 'OCA\\Richdocuments\\Listener\\FileCreatedFromTemplateListener' => $baseDir . '/../lib/Listener/FileCreatedFromTemplateListener.php', 'OCA\\Richdocuments\\Listener\\LoadViewerListener' => $baseDir . '/../lib/Listener/LoadViewerListener.php', 'OCA\\Richdocuments\\Listener\\ReferenceListener' => $baseDir . '/../lib/Listener/ReferenceListener.php', diff --git a/composer/composer/autoload_static.php b/composer/composer/autoload_static.php index 401deeb4e8..8646d28e76 100644 --- a/composer/composer/autoload_static.php +++ b/composer/composer/autoload_static.php @@ -55,6 +55,7 @@ class ComposerStaticInitRichdocuments 'OCA\\Richdocuments\\Listener\\AddContentSecurityPolicyListener' => __DIR__ . '/..' . '/../lib/Listener/AddContentSecurityPolicyListener.php', 'OCA\\Richdocuments\\Listener\\AddFeaturePolicyListener' => __DIR__ . '/..' . '/../lib/Listener/AddFeaturePolicyListener.php', 'OCA\\Richdocuments\\Listener\\BeforeFetchPreviewListener' => __DIR__ . '/..' . '/../lib/Listener/BeforeFetchPreviewListener.php', + 'OCA\\Richdocuments\\Listener\\BeforeTemplateRenderedListener' => __DIR__ . '/..' . '/../lib/Listener/BeforeTemplateRenderedListener.php', 'OCA\\Richdocuments\\Listener\\FileCreatedFromTemplateListener' => __DIR__ . '/..' . '/../lib/Listener/FileCreatedFromTemplateListener.php', 'OCA\\Richdocuments\\Listener\\LoadViewerListener' => __DIR__ . '/..' . '/../lib/Listener/LoadViewerListener.php', 'OCA\\Richdocuments\\Listener\\ReferenceListener' => __DIR__ . '/..' . '/../lib/Listener/ReferenceListener.php', diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php index 5b33f4fbcd..a85104bcaf 100644 --- a/lib/AppInfo/Application.php +++ b/lib/AppInfo/Application.php @@ -31,6 +31,7 @@ use OCA\Richdocuments\Listener\AddContentSecurityPolicyListener; use OCA\Richdocuments\Listener\AddFeaturePolicyListener; use OCA\Richdocuments\Listener\BeforeFetchPreviewListener; +use OCA\Richdocuments\Listener\BeforeTemplateRenderedListener; use OCA\Richdocuments\Listener\FileCreatedFromTemplateListener; use OCA\Richdocuments\Listener\LoadViewerListener; use OCA\Richdocuments\Listener\ReferenceListener; @@ -53,6 +54,7 @@ use OCP\AppFramework\Bootstrap\IBootContext; use OCP\AppFramework\Bootstrap\IBootstrap; use OCP\AppFramework\Bootstrap\IRegistrationContext; +use OCP\AppFramework\Http\Events\BeforeTemplateRenderedEvent; use OCP\Collaboration\Reference\RenderReferenceEvent; use OCP\Files\Template\FileCreatedFromTemplateEvent; use OCP\Files\Template\ITemplateManager; @@ -84,6 +86,7 @@ public function register(IRegistrationContext $context): void { $context->registerEventListener(ShareLinkAccessedEvent::class, ShareLinkListener::class); $context->registerEventListener(BeforePreviewFetchedEvent::class, BeforeFetchPreviewListener::class); $context->registerEventListener(RenderReferenceEvent::class, ReferenceListener::class); + $context->registerEventListener(BeforeTemplateRenderedEvent::class, BeforeTemplateRenderedListener::class); $context->registerReferenceProvider(OfficeTargetReferenceProvider::class); $context->registerSensitiveMethods(WopiMapper::class, [ 'getPathForToken', diff --git a/lib/Listener/AddContentSecurityPolicyListener.php b/lib/Listener/AddContentSecurityPolicyListener.php index 3b7c309836..ced4bb2664 100644 --- a/lib/Listener/AddContentSecurityPolicyListener.php +++ b/lib/Listener/AddContentSecurityPolicyListener.php @@ -25,6 +25,7 @@ namespace OCA\Richdocuments\Listener; use OCA\Richdocuments\AppConfig; +use OCA\Richdocuments\Service\CapabilitiesService; use OCP\AppFramework\Http\EmptyContentSecurityPolicy; use OCP\EventDispatcher\Event; use OCP\EventDispatcher\IEventListener; @@ -36,6 +37,7 @@ class AddContentSecurityPolicyListener implements IEventListener { public function __construct( private IRequest $request, private AppConfig $config, + private CapabilitiesService $capabilitiesService, ) { } @@ -52,6 +54,10 @@ public function handle(Event $event): void { $policy->addAllowedFrameDomain("'self'"); $policy->addAllowedFrameDomain("nc:"); + if ($this->capabilitiesService->hasWASMSupport()) { + $policy->allowEvalWasm(true); + } + foreach ($this->config->getDomainList() as $url) { $policy->addAllowedFrameDomain($url); $policy->addAllowedFormActionDomain($url); diff --git a/lib/Listener/BeforeTemplateRenderedListener.php b/lib/Listener/BeforeTemplateRenderedListener.php new file mode 100644 index 0000000000..acad7dd035 --- /dev/null +++ b/lib/Listener/BeforeTemplateRenderedListener.php @@ -0,0 +1,30 @@ + */ +class BeforeTemplateRenderedListener implements IEventListener { + private CapabilitiesService $capabilitiesService; + + public function __construct(CapabilitiesService $capabilitiesService) { + $this->capabilitiesService = $capabilitiesService; + } + + public function handle(Event $event): void { + if (!$event instanceof BeforeTemplateRenderedEvent) { + return; + } + + if ($this->capabilitiesService->hasWASMSupport()) { + $event->getResponse()->addHeader('Cross-Origin-Opener-Policy', 'same-origin'); + $event->getResponse()->addHeader('Cross-Origin-Embedder-Policy', 'require-corp'); + } + } +} diff --git a/lib/Service/CapabilitiesService.php b/lib/Service/CapabilitiesService.php index df9df32b80..7bd316f5c9 100644 --- a/lib/Service/CapabilitiesService.php +++ b/lib/Service/CapabilitiesService.php @@ -114,6 +114,10 @@ public function hasZoteroSupport(): bool { return $this->getCapabilities()['hasZoteroSupport'] ?? false; } + public function hasWASMSupport(): bool { + return $this->getCapabilities()['hasWASMSupport'] ?? false; + } + public function getProductName(): string { $theme = $this->config->getAppValue(Application::APPNAME, 'theme', 'nextcloud'); diff --git a/tests/lib/Listener/AddContentSecurityPolicyListenerTest.php b/tests/lib/Listener/AddContentSecurityPolicyListenerTest.php index e4d7924bc5..13908360c0 100644 --- a/tests/lib/Listener/AddContentSecurityPolicyListenerTest.php +++ b/tests/lib/Listener/AddContentSecurityPolicyListenerTest.php @@ -27,6 +27,7 @@ use OC\Security\CSP\ContentSecurityPolicyManager; use OCA\Richdocuments\AppConfig; use OCA\Richdocuments\Listener\AddContentSecurityPolicyListener; +use OCA\Richdocuments\Service\CapabilitiesService; use OCA\Richdocuments\Service\FederationService; use OCP\App\IAppManager; use OCP\AppFramework\Http\ContentSecurityPolicy; @@ -53,6 +54,7 @@ class AddContentSecurityPolicyListenerTest extends TestCase { private $gsConfig; /** @var FederationService|MockObject */ private $federationService; + private CapabilitiesService|MockObject $capabilitiesService; private AddContentSecurityPolicyListener $listener; public function setUp(): void { @@ -76,11 +78,12 @@ public function setUp(): void { ]) ->onlyMethods(['getCollaboraUrlPublic', 'getGlobalScaleTrustedHosts']) ->getMock(); - + $this->capabilitiesService = $this->createMock(CapabilitiesService::class); $this->listener = new AddContentSecurityPolicyListener( $this->request, $this->config, + $this->capabilitiesService, ); } @@ -228,4 +231,24 @@ public static function assertArrayUnordered($expected, $actual, $msg = '') { sort($actual); self::assertSame($expected, $actual, $msg); } + + public function testWasm() { + $this->expectPageLoad(); + $this->capabilitiesService->method('hasWASMSupport') + ->willReturn(true); + + $policy = $this->getMergedPolicy(); + + self::assertTrue(str_contains($policy->buildPolicy(), ' \'wasm-unsafe-eval\'')); + } + + public function testNoWasm() { + $this->expectPageLoad(); + $this->capabilitiesService->method('hasWASMSupport') + ->willReturn(false); + + $policy = $this->getMergedPolicy(); + + self::assertTrue(!str_contains($policy->buildPolicy(), ' \'wasm-unsafe-eval\'')); + } }