From 3f812fe5174d43dc4f435038aec9523f0867990b Mon Sep 17 00:00:00 2001 From: Ferdinand Thiessen Date: Wed, 20 May 2026 22:21:37 +0200 Subject: [PATCH] refactor: migrate to event listeners to resolve deprecations `OCP\Security\IContentSecurityPolicyManager` is deprecated, thus we have to migrate to the listeners approach. So this migrates both the CSP handling and the scripts handling to the listeners. Also added stubs instead of baseline entries. Signed-off-by: Ferdinand Thiessen --- lib/AppInfo/Application.php | 46 +++------------ .../AddContentSecurityPolicyListener.php | 45 ++++++++++++++ .../BeforeTemplateRenderedListener.php | 58 +++++++++++++++++++ psalm.xml | 7 ++- tests/psalm-baseline.xml | 9 +-- ..._csp_ContentSecurityPolicyNonceManager.php | 25 ++++++++ 6 files changed, 142 insertions(+), 48 deletions(-) create mode 100644 lib/Listeners/AddContentSecurityPolicyListener.php create mode 100644 lib/Listeners/BeforeTemplateRenderedListener.php create mode 100644 tests/stubs/oc_security_csp_ContentSecurityPolicyNonceManager.php diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php index fcbd45f..e232184 100644 --- a/lib/AppInfo/Application.php +++ b/lib/AppInfo/Application.php @@ -9,17 +9,15 @@ namespace OCA\JSLoader\AppInfo; -use OC\Security\CSP\ContentSecurityPolicy; -use OC\Security\CSP\ContentSecurityPolicyNonceManager; +use OCA\JSLoader\Listeners\AddContentSecurityPolicyListener; +use OCA\JSLoader\Listeners\BeforeTemplateRenderedListener; use OCP\AppFramework\App; use OCP\AppFramework\Bootstrap\IBootContext; use OCP\AppFramework\Bootstrap\IBootstrap; use OCP\AppFramework\Bootstrap\IRegistrationContext; -use OCP\IAppConfig; -use OCP\IURLGenerator; -use OCP\Security\IContentSecurityPolicyManager; -use OCP\Server; -use OCP\Util; +use OCP\AppFramework\Http\Events\BeforeLoginTemplateRenderedEvent; +use OCP\AppFramework\Http\Events\BeforeTemplateRenderedEvent; +use OCP\Security\CSP\AddContentSecurityPolicyEvent; class Application extends App implements IBootstrap { public const APP_ID = 'jsloader'; @@ -29,38 +27,12 @@ public function __construct(array $urlParams = []) { } public function register(IRegistrationContext $context): void { - // nothing to do here + $context->registerEventListener(AddContentSecurityPolicyEvent::class, AddContentSecurityPolicyListener::class); + $context->registerEventListener(BeforeTemplateRenderedEvent::class, BeforeTemplateRenderedListener::class); + $context->registerEventListener(BeforeLoginTemplateRenderedEvent::class, BeforeTemplateRenderedListener::class); } public function boot(IBootContext $context): void { - $appConfig = Server::get(IAppConfig::class); - $urlGenerator = Server::get(IURLGenerator::class); - $contentSecurityPolicyManager = Server::get(IContentSecurityPolicyManager::class); - $contentSecurityPolicyNonceManager = Server::get(ContentSecurityPolicyNonceManager::class); - - $snippet = $appConfig->getValueString(self::APP_ID, 'snippet', ''); - if ($snippet !== '') { - $linkToJs = $urlGenerator->linkToRoute('jsloader.JS.script', [ - 'v' => $appConfig->getValueString(self::APP_ID, 'cachebuster', '0'), - ]); - - Util::addHeader( - 'script', - [ - 'src' => $linkToJs, - 'nonce' => $contentSecurityPolicyNonceManager->getNonce() - ], '' - ); - - // whitelist the URL to allow loading JS from this external domain - $url = $appConfig->getValueString(self::APP_ID, 'url'); - if ($url !== '') { - $policy = new ContentSecurityPolicy(); - $policy->addAllowedScriptDomain($url); - $policy->addAllowedImageDomain($url); - $policy->addAllowedConnectDomain($url); - $contentSecurityPolicyManager->addDefaultPolicy($policy); - } - } + // nop } } diff --git a/lib/Listeners/AddContentSecurityPolicyListener.php b/lib/Listeners/AddContentSecurityPolicyListener.php new file mode 100644 index 0000000..9c1e148 --- /dev/null +++ b/lib/Listeners/AddContentSecurityPolicyListener.php @@ -0,0 +1,45 @@ + + */ +class AddContentSecurityPolicyListener implements IEventListener { + + public function __construct( + private readonly \OCP\IAppConfig $appConfig, + ) { + } + + public function handle(Event $event): void { + if (!$event instanceof AddContentSecurityPolicyEvent) { + return; + } + + // whitelist the URL to allow loading JS from this external domain + $url = $this->appConfig->getValueString(Application::APP_ID, 'url'); + if ($url !== '') { + $policy = new ContentSecurityPolicy(); + $policy->addAllowedScriptDomain($url); + $policy->addAllowedImageDomain($url); + $policy->addAllowedConnectDomain($url); + $event->addPolicy($policy); + } + } +} diff --git a/lib/Listeners/BeforeTemplateRenderedListener.php b/lib/Listeners/BeforeTemplateRenderedListener.php new file mode 100644 index 0000000..05fa176 --- /dev/null +++ b/lib/Listeners/BeforeTemplateRenderedListener.php @@ -0,0 +1,58 @@ + + */ +class BeforeTemplateRenderedListener implements IEventListener { + + public function __construct( + private readonly IAppConfig $appConfig, + private readonly IURLGenerator $urlGenerator, + private readonly ContentSecurityPolicyNonceManager $contentSecurityPolicyNonceManager, + ) { + } + + public function handle(Event $event): void { + if (!($event instanceof BeforeTemplateRenderedEvent) && !($event instanceof BeforeLoginTemplateRenderedEvent)) { + return; + } + + $snippet = $this->appConfig->getValueString(Application::APP_ID, 'snippet', ''); + if ($snippet === '') { + return; + } + + $linkToJs = $this->urlGenerator->linkToRoute('jsloader.JS.script', [ + 'v' => $this->appConfig->getValueString(Application::APP_ID, 'cachebuster', '0'), + ]); + + Util::addHeader( + 'script', + [ + 'src' => $linkToJs, + 'nonce' => $this->contentSecurityPolicyNonceManager->getNonce() + ], '' + ); + } +} diff --git a/psalm.xml b/psalm.xml index d736243..47c58da 100644 --- a/psalm.xml +++ b/psalm.xml @@ -1,6 +1,6 @@ - - + + + diff --git a/tests/psalm-baseline.xml b/tests/psalm-baseline.xml index 5792e59..5c064bb 100644 --- a/tests/psalm-baseline.xml +++ b/tests/psalm-baseline.xml @@ -1,9 +1,2 @@ - - - - - - - - + diff --git a/tests/stubs/oc_security_csp_ContentSecurityPolicyNonceManager.php b/tests/stubs/oc_security_csp_ContentSecurityPolicyNonceManager.php new file mode 100644 index 0000000..b3d46ed --- /dev/null +++ b/tests/stubs/oc_security_csp_ContentSecurityPolicyNonceManager.php @@ -0,0 +1,25 @@ +