CVE-2024-27915: Insufficient Information (NVD-CWE-noinfo)
value: Sulu is a PHP content management system. Starting in verson 2.2.0 and prior to version 2.4.17 and 2.5.13, access to pages is granted regardless of role permissions for webspaces which have a security system configured and permission check enabled. Webspaces without do not have this issue. The problem is patched in versions 2.4.17 and 2.5.13. Some workarounds are available. One may apply the patch to `vendor/symfony/security-http/HttpUtils.php` manually or avoid installing `symfony/security-http` versions greater equal than `v5.4.30` or `v6.3.6`.
CWE description: There is insufficient information about the issue to classify it; details are unkown or unspecified.
Published: 2024-03-06T20:15Z
Repository: https://github.com/sulu/sulu
Language: PHP
Files changed: phpstan-baseline.neon, src/Sulu/Bundle/CoreBundle/Resources/config/request_analyzer.xml, src/Sulu/Bundle/SecurityBundle/EventListener/SystemListener.php, src/Sulu/Bundle/SecurityBundle/Resources/config/services.xml, src/Sulu/Bundle/SecurityBundle/Tests/Unit/EventListener/SystemListenerTest.php, src/Sulu/Bundle/WebsiteBundle/EventListener/RouterListener.php, src/Sulu/Bundle/WebsiteBundle/EventListener/SecurityListener.php, src/Sulu/Bundle/WebsiteBundle/Resources/config/services.xml, src/Sulu/Bundle/WebsiteBundle/Resources/config/website.xml, src/Sulu/Bundle/WebsiteBundle/Routing/ContentRouteProvider.php, src/Sulu/Bundle/WebsiteBundle/Tests/Application/Kernel.php, src/Sulu/Bundle/WebsiteBundle/Tests/Application/config/config_with_security.yml, src/Sulu/Bundle/WebsiteBundle/Tests/Application/config/routing_website.yml, src/Sulu/Bundle/WebsiteBundle/Tests/Application/config/webspaces/sulu_lo.xml, src/Sulu/Bundle/WebsiteBundle/Tests/Functional/EventListener/SecurityListenerTest.php, src/Sulu/Bundle/WebsiteBundle/Tests/Unit/Routing/ContentRouteProviderTest.php, src/Sulu/Component/Webspace/Analyzer/Attributes/SystemRequestProcessor.php
Commit date: 2024-03-04 13:53:32 +0100
Commit message: Merge pull request from GHSA-jr83-m233-gg6p

* Fix setting of Webspace Security System before Symfony RouteListener

* Fix Redirect for Security System checker error

* Add test case to avoid regression

* Add test for none secured pages

* Fix some PHP 7.2 issues

* Remmove website based system listener test

* Activate security only for specific test

* Remove old ContentRouteProviderTests

* Fix phpcs

* Skip security listener test for older symfony security system

* Fix code style

* Fix phpstan issues

* Throw deprecation when requestAnalyzer is still given

* Fix code style

* Mark new service as internal and final
Diff: diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon
index bea5b36cd13..95390ef9363 100644
--- a/phpstan-baseline.neon
+++ b/phpstan-baseline.neon
@@ -29619,16 +29619,16 @@ parameters:
 			message: "#^Method Sulu\\\\Bundle\\\\SecurityBundle\\\\EventListener\\\\SuluSecurityListener\\:\\:onKernelController\\(\\) has no return type specified\\.$#"
 			count: 1
 			path: src/Sulu/Bundle/SecurityBundle/EventListener/SuluSecurityListener.php
 
 		-
-			message: "#^If condition is always true\\.$#"
+			message: "#^Method Sulu\\\\Bundle\\\\SecurityBundle\\\\EventListener\\\\SystemListener\\:\\:onKernelRequest\\(\\) has no return type specified\\.$#"
 			count: 1
 			path: src/Sulu/Bundle/SecurityBundle/EventListener/SystemListener.php
 
 		-
-			message: "#^Method Sulu\\\\Bundle\\\\SecurityBundle\\\\EventListener\\\\SystemListener\\:\\:onKernelRequest\\(\\) has no return type specified\\.$#"
+			message: "#^Property Sulu\\\\Bundle\\\\SecurityBundle\\\\EventListener\\\\SystemListener\\:\\:\\$requestAnalyzer is never read, only written\\.$#"
 			count: 1
 			path: src/Sulu/Bundle/SecurityBundle/EventListener/SystemListener.php
 
 		-
 			message: "#^Call to an undefined method Symfony\\\\Contracts\\\\Translation\\\\LocaleAwareInterface\\|Symfony\\\\Contracts\\\\Translation\\\\TranslatorInterface\\:\\:setLocale\\(\\)\\.$#"
@@ -30548,15 +30548,10 @@ parameters:
 		-
 			message: "#^Method Sulu\\\\Bundle\\\\SecurityBundle\\\\EventListener\\\\SuluSecurityListenerTest\\:\\:testMethodPermissionMapping\\(\\) has parameter \\$permission with no type specified\\.$#"
 			count: 1
 			path: src/Sulu/Bundle/SecurityBundle/Tests/Unit/EventListener/SuluSecurityListenerTest.php
 
-		-
-			message: "#^Method Sulu\\\\Bundle\\\\SecurityBundle\\\\EventListener\\\\SystemListenerTest\\:\\:provideSetWebsiteSystem\\(\\) has no return type specified\\.$#"
-			count: 1
-			path: src/Sulu/Bundle/SecurityBundle/Tests/Unit/EventListener/SystemListenerTest.php
-
 		-
 			message: "#^Class Sulu\\\\Bundle\\\\SecurityBundle\\\\Security\\\\AuthenticationEntryPoint does not have a constructor and must be instantiated without any parameters\\.$#"
 			count: 1
 			path: src/Sulu/Bundle/SecurityBundle/Tests/Unit/Security/AuthenticationEntryPointTest.php
 
@@ -34918,10 +34913,15 @@ parameters:
 		-
 			message: "#^Property Sulu\\\\Bundle\\\\WebsiteBundle\\\\Routing\\\\ContentRouteProvider\\:\\:\\$defaultOptions type has no value type specified in iterable type array\\.$#"
 			count: 1
 			path: src/Sulu/Bundle/WebsiteBundle/Routing/ContentRouteProvider.php
 
+		-
+			message: "#^Property Sulu\\\\Bundle\\\\WebsiteBundle\\\\Routing\\\\ContentRouteProvider\\:\\:\\$securityChecker is never read, only written\\.$#"
+			count: 1
+			path: src/Sulu/Bundle/WebsiteBundle/Routing/ContentRouteProvider.php
+
 		-
 			message: "#^Strict comparison using \\=\\=\\= between null and string will always evaluate to false\\.$#"
 			count: 1
 			path: src/Sulu/Bundle/WebsiteBundle/Routing/ContentRouteProvider.php
 
diff --git a/src/Sulu/Bundle/CoreBundle/Resources/config/request_analyzer.xml b/src/Sulu/Bundle/CoreBundle/Resources/config/request_analyzer.xml
index fb0e06f8f99..4656971c50f 100644
--- a/src/Sulu/Bundle/CoreBundle/Resources/config/request_analyzer.xml
+++ b/src/Sulu/Bundle/CoreBundle/Resources/config/request_analyzer.xml
@@ -49,10 +49,16 @@
                  class="Sulu\Component\Webspace\Analyzer\Attributes\SegmentRequestProcessor">
             <argument>%sulu_website.segment_cookie_name%</argument>
             <tag name="sulu.context" context="website"/>
             <tag name="sulu.request_attributes" priority="-64"/>
         </service>
+        <service id="sulu_core.request_processor.system"
+                 class="Sulu\Component\Webspace\Analyzer\Attributes\SystemRequestProcessor">
+            <argument type="service" id="sulu_security.system_store" />
+            <argument>%sulu.context%</argument>
+            <tag name="sulu.request_attributes" priority="-32"/>
+        </service>
         <service id="sulu_core.request_processor.date_time"
                  class="Sulu\Component\Webspace\Analyzer\Attributes\DateTimeRequestProcessor">
             <tag name="sulu.context" context="website"/>
             <tag name="sulu.request_attributes" priority="0"/>
         </service>
diff --git a/src/Sulu/Bundle/SecurityBundle/EventListener/SystemListener.php b/src/Sulu/Bundle/SecurityBundle/EventListener/SystemListener.php
index 63ae275f50e..fcc7fadc67e 100644
--- a/src/Sulu/Bundle/SecurityBundle/EventListener/SystemListener.php
+++ b/src/Sulu/Bundle/SecurityBundle/EventListener/SystemListener.php
@@ -25,25 +25,30 @@ class SystemListener implements EventSubscriberInterface
      * @var SystemStoreInterface
      */
     private $systemStore;
 
     /**
-     * @var RequestAnalyzerInterface
+     * @var RequestAnalyzerInterface|null
      */
     private $requestAnalyzer;
 
     /**
      * @var string
      */
     private $context;
 
     public function __construct(
         SystemStoreInterface $systemStore,
-        RequestAnalyzerInterface $requestAnalyzer,
+        ?RequestAnalyzerInterface $requestAnalyzer,
         string $context
     ) {
         $this->systemStore = $systemStore;
+
+        if (null !== $requestAnalyzer) {
+            @trigger_deprecation('sulu/sulu', '2.4', 'The argument "%s" in class "%s" is deprecated and not longer required set `null` instead.', RequestAnalyzerInterface::class, __CLASS__);
+        }
+
         $this->requestAnalyzer = $requestAnalyzer;
         $this->context = $context;
     }
 
     public static function getSubscribedEvents(): array
@@ -56,17 +61,7 @@ class SystemListener implements EventSubscriberInterface
         if (SuluKernel::CONTEXT_ADMIN === $this->context) {
             $this->systemStore->setSystem(Admin::SULU_ADMIN_SECURITY_SYSTEM);
 
             return;
         }
-
-        $webspace = $this->requestAnalyzer->getWebspace();
-        if ($webspace) {
-            $security = $webspace->getSecurity();
-            if ($security) {
-                $this->systemStore->setSystem($security->getSystem());
-
-                return;
-            }
-        }
     }
 }
diff --git a/src/Sulu/Bundle/SecurityBundle/Resources/config/services.xml b/src/Sulu/Bundle/SecurityBundle/Resources/config/services.xml
index 5c7e716685a..a4fb982e0de 100644
--- a/src/Sulu/Bundle/SecurityBundle/Resources/config/services.xml
+++ b/src/Sulu/Bundle/SecurityBundle/Resources/config/services.xml
@@ -304,11 +304,11 @@
             <tag name="sulu.context" context="admin"/>
         </service>
 
         <service id="sulu_security.system_listener" class="Sulu\Bundle\SecurityBundle\EventListener\SystemListener">
             <argument type="service" id="sulu_security.system_store" />
-            <argument type="service" id="sulu_core.webspace.request_analyzer"/>
+            <argument>null</argument>
             <argument>%sulu.context%</argument>
 
             <tag name="kernel.event_subscriber"/>
         </service>
 
diff --git a/src/Sulu/Bundle/SecurityBundle/Tests/Unit/EventListener/SystemListenerTest.php b/src/Sulu/Bundle/SecurityBundle/Tests/Unit/EventListener/SystemListenerTest.php
index 91911c87700..9ba0de60f38 100644
--- a/src/Sulu/Bundle/SecurityBundle/Tests/Unit/EventListener/SystemListenerTest.php
+++ b/src/Sulu/Bundle/SecurityBundle/Tests/Unit/EventListener/SystemListenerTest.php
@@ -13,12 +13,10 @@ namespace Sulu\Bundle\SecurityBundle\EventListener;
 
 use PHPUnit\Framework\TestCase;
 use Prophecy\Prophecy\ObjectProphecy;
 use Sulu\Bundle\SecurityBundle\System\SystemStoreInterface;
 use Sulu\Component\Webspace\Analyzer\RequestAnalyzerInterface;
-use Sulu\Component\Webspace\Security;
-use Sulu\Component\Webspace\Webspace;
 use Symfony\Component\HttpKernel\Event\RequestEvent;
 
 class SystemListenerTest extends TestCase
 {
     /**
@@ -44,37 +42,10 @@ class SystemListenerTest extends TestCase
         $systemListener->onKernelRequest($requestEvent->reveal());
 
         $this->systemStore->setSystem('Sulu')->shouldBeCalled();
     }
 
-    public function provideSetWebsiteSystem()
-    {
-        return [
-            ['sulu-test'],
-            ['sulu-blog'],
-        ];
-    }
-
-    /**
-     * @dataProvider provideSetWebsiteSystem
-     */
-    public function testSetWebsiteSystem(string $system): void
-    {
-        $systemListener = $this->createSystemListener('website');
-        $requestEvent = $this->prophesize(RequestEvent::class);
-
-        $webspace = new Webspace();
-        $security = new Security();
-        $security->setSystem($system);
-        $webspace->setSecurity($security);
-        $this->requestAnalyzer->getWebspace()->willReturn($webspace);
-
-        $systemListener->onKernelRequest($requestEvent->reveal());
-
-        $this->systemStore->setSystem($system)->shouldBeCalled();
-    }
-
     private function createSystemListener(string $context): SystemListener
     {
         return new SystemListener($this->systemStore->reveal(), $this->requestAnalyzer->reveal(), $context);
     }
 }
diff --git a/src/Sulu/Bundle/WebsiteBundle/EventListener/RouterListener.php b/src/Sulu/Bundle/WebsiteBundle/EventListener/RouterListener.php
index b95e2e4f55e..b33d128a13c 100644
--- a/src/Sulu/Bundle/WebsiteBundle/EventListener/RouterListener.php
+++ b/src/Sulu/Bundle/WebsiteBundle/EventListener/RouterListener.php
@@ -52,10 +52,11 @@ class RouterListener implements EventSubscriberInterface
         $request = $event->getRequest();
 
         // This call is required in all cases, because the default router needs our webspace information
         // Would be nice to also only call this if the _requestAnalyzer attribute is set, but it's set on the next line
         $this->requestAnalyzer->analyze($request);
+
         $this->baseRouteListener->onKernelRequest($event);
         if (false !== $request->attributes->getBoolean(static::REQUEST_ANALYZER, true)) {
             $this->requestAnalyzer->validate($request);
         }
     }
diff --git a/src/Sulu/Bundle/WebsiteBundle/EventListener/SecurityListener.php b/src/Sulu/Bundle/WebsiteBundle/EventListener/SecurityListener.php
new file mode 100644
index 00000000000..bc6346e5c34
--- /dev/null
+++ b/src/Sulu/Bundle/WebsiteBundle/EventListener/SecurityListener.php
@@ -0,0 +1,83 @@
+<?php
+
+/*
+ * This file is part of Sulu.
+ *
+ * (c) Sulu GmbH
+ *
+ * This source file is subject to the MIT license that is bundled
+ * with this source code in the file LICENSE.
+ */
+
+namespace Sulu\Bundle\WebsiteBundle\EventListener;
+
+use Sulu\Bundle\PageBundle\Document\BasePageDocument;
+use Sulu\Component\Content\Compat\Structure\PageBridge;
+use Sulu\Component\DocumentManager\Subscriber\EventSubscriberInterface;
+use Sulu\Component\Security\Authorization\PermissionTypes;
+use Sulu\Component\Security\Authorization\SecurityCheckerInterface;
+use Sulu\Component\Security\Authorization\SecurityCondition;
+use Sulu\Component\Webspace\Analyzer\Attributes\RequestAttributes;
+use Symfony\Component\HttpKernel\Event\RequestEvent;
+use Symfony\Component\HttpKernel\KernelEvents;
+
+class SecurityListener implements EventSubscriberInterface
+{
+    /**
+     * @var SecurityCheckerInterface|null
+     */
+    private $securityChecker;
+
+    public function __construct(
+        ?SecurityCheckerInterface $securityChecker = null
+    ) {
+        $this->securityChecker = $securityChecker;
+    }
+
+    public static function getSubscribedEvents(): array
+    {
+        return [
+            KernelEvents::REQUEST => [
+                ['onKernelRequest', 7], // set the security listener after the firewall and after the routing listener
+            ],
+        ];
+    }
+
+    public function onKernelRequest(RequestEvent $event): void
+    {
+        $request = $event->getRequest();
+
+        if (null === $this->securityChecker) {
+            return;
+        }
+
+        $requestAttributes = $request->attributes->get('_sulu');
+        if (!$requestAttributes instanceof RequestAttributes) {
+            return;
+        }
+
+        $webspace = $requestAttributes->getAttribute('webspace');
+
+        $structure = $request->attributes->get('structure');
+        if (!$structure instanceof PageBridge) {
+            return;
+        }
+
+        $document = $structure->getDocument();
+        if (!$document instanceof BasePageDocument) {
+            return;
+        }
+
+        if ($webspace->hasWebsiteSecurity()) {
+            $this->securityChecker->checkPermission(
+                new SecurityCondition(
+                    'sulu.webspaces.' . $document->getWebspaceName(),
+                    $document->getLocale(),
+                    \get_class($document),
+                    $document->getUuid()
+                ),
+                PermissionTypes::VIEW
+            );
+        }
+    }
+}
diff --git a/src/Sulu/Bundle/WebsiteBundle/Resources/config/services.xml b/src/Sulu/Bundle/WebsiteBundle/Resources/config/services.xml
index 36dbe42159c..5a17af96060 100644
--- a/src/Sulu/Bundle/WebsiteBundle/Resources/config/services.xml
+++ b/src/Sulu/Bundle/WebsiteBundle/Resources/config/services.xml
@@ -297,10 +297,17 @@
             <argument type="service" id="translator"/>
 
             <tag name="kernel.event_subscriber"/>
         </service>
 
+        <service id="sulu_website.event_listener.security_listener"
+                 class="Sulu\Bundle\WebsiteBundle\EventListener\SecurityListener">
+            <argument type="service" id="sulu_security.security_checker" on-invalid="null"/>
+
+            <tag name="kernel.event_subscriber"/>
+        </service>
+
         <!-- reference-store -->
         <service id="sulu_website.reference_store_pool"
                  class="Sulu\Bundle\WebsiteBundle\ReferenceStore\ReferenceStorePool">
             <argument type="collection"/>
         </service>
diff --git a/src/Sulu/Bundle/WebsiteBundle/Resources/config/website.xml b/src/Sulu/Bundle/WebsiteBundle/Resources/config/website.xml
index 477eb511d7a..d802201ca2b 100644
--- a/src/Sulu/Bundle/WebsiteBundle/Resources/config/website.xml
+++ b/src/Sulu/Bundle/WebsiteBundle/Resources/config/website.xml
@@ -19,11 +19,11 @@
             <argument type="service" id="sulu_document_manager.document_inspector"/>
             <argument type="service" id="sulu.content.resource_locator.strategy_pool"/>
             <argument type="service" id="sulu.content.structure_manager"/>
             <argument type="service" id="sulu_core.webspace.webspace_manager"/>
             <argument type="service" id="sulu_core.webspace.request_analyzer"/>
-            <argument type="service" id="sulu_security.security_checker" on-invalid="null"/>
+            <argument>null</argument><!-- not longer required -->
             <argument type="collection" />
 
             <tag name="sulu.context" context="website"/>
         </service>
 
diff --git a/src/Sulu/Bundle/WebsiteBundle/Routing/ContentRouteProvider.php b/src/Sulu/Bundle/WebsiteBundle/Routing/ContentRouteProvider.php
index d5136ae85d1..b8ac82148b1 100644
--- a/src/Sulu/Bundle/WebsiteBundle/Routing/ContentRouteProvider.php
+++ b/src/Sulu/Bundle/WebsiteBundle/Routing/ContentRouteProvider.php
@@ -22,20 +22,19 @@ use Sulu\Component\Content\Document\Behavior\WebspaceBehavior;
 use Sulu\Component\Content\Document\RedirectType;
 use Sulu\Component\Content\Exception\ResourceLocatorMovedException;
 use Sulu\Component\Content\Exception\ResourceLocatorNotFoundException;
 use Sulu\Component\Content\Types\ResourceLocator\Strategy\ResourceLocatorStrategyPoolInterface;
 use Sulu\Component\DocumentManager\DocumentManagerInterface;
-use Sulu\Component\Security\Authorization\PermissionTypes;
 use Sulu\Component\Security\Authorization\SecurityCheckerInterface;
-use Sulu\Component\Security\Authorization\SecurityCondition;
 use Sulu\Component\Webspace\Analyzer\Attributes\RequestAttributes;
 use Sulu\Component\Webspace\Analyzer\RequestAnalyzerInterface;
 use Sulu\Component\Webspace\Manager\WebspaceManagerInterface;
 use Symfony\Cmf\Component\Routing\RouteProviderInterface;
 use Symfony\Component\HttpFoundation\Request;
 use Symfony\Component\Routing\Route;
 use Symfony\Component\Routing\RouteCollection;
+use Webmozart\Assert\Assert;
 
 /**
  * The PortalRouteProvider should load the dynamic routes created by Sulu.
  */
 class ContentRouteProvider implements RouteProviderInterface
@@ -95,10 +94,11 @@ class ContentRouteProvider implements RouteProviderInterface
         $this->resourceLocatorStrategyPool = $resourceLocatorStrategyPool;
         $this->structureManager = $structureManager;
         $this->webspaceManager = $webspaceManager;
         $this->requestAnalyzer = $requestAnalyzer;
         $this->securityChecker = $securityChecker;
+        Assert::null($securityChecker, 'The security checker should be called by the SecurityListener not the ContentRouteProvider.'); // people who overwrite the ContentRouteProvider should make aware of that they also need to refactor this
         $this->defaultOptions = $defaultOptions;
     }
 
     public function getRouteCollectionForRequest(Request $request)
     {
@@ -164,22 +164,10 @@ class ContentRouteProvider implements RouteProviderInterface
                 // If the title is empty the document does not exist in this locale
                 // Necessary because of https://github.com/sulu/sulu/issues/2724, otherwise locale could be checked
                 return $collection;
             }
 
-            if ($this->securityChecker && $portal->getWebspace()->hasWebsiteSecurity()) {
-                $this->securityChecker->checkPermission(
-                    new SecurityCondition(
-                        'sulu.webspaces.' . $document->getWebspaceName(),
-                        $document->getLocale(),
-                        \get_class($document),
-                        $document->getUuid()
-                    ),
-                    PermissionTypes::VIEW
-                );
-            }
-
             if (\preg_match('/\/$/', $resourceLocator) && ('/' !== $resourceLocator || $prefix)) {
                 // redirect page to page without slash at the end
                 $url = $prefix . \rtrim($resourceLocator, '/');
                 if ($request->getQueryString()) {
                     $url .= '?' . $request->getQueryString();
diff --git a/src/Sulu/Bundle/WebsiteBundle/Tests/Application/Kernel.php b/src/Sulu/Bundle/WebsiteBundle/Tests/Application/Kernel.php
index 4c654d0486d..089b8fb1b13 100644
--- a/src/Sulu/Bundle/WebsiteBundle/Tests/Application/Kernel.php
+++ b/src/Sulu/Bundle/WebsiteBundle/Tests/Application/Kernel.php
@@ -10,27 +10,66 @@
  */
 
 namespace Sulu\Bundle\WebsiteBundle\Tests\Application;
 
 use Sulu\Bundle\TestBundle\Kernel\SuluTestKernel;
+use Symfony\Bundle\SecurityBundle\SecurityBundle;
 use Symfony\Component\Config\Loader\LoaderInterface;
 
 class Kernel extends SuluTestKernel
 {
+    /**
+     * @var string
+     */
+    private $appContext;
+
     /**
      * @param string $environment
      * @param bool $debug
      * @param string $suluContext
      */
     public function __construct($environment, $debug, $suluContext = self::CONTEXT_ADMIN)
     {
-        parent::__construct($environment, $debug, $suluContext);
+        $envParts = \explode('_', $environment, 2);
+        $this->appContext = $envParts[1] ?? '';
+
+        parent::__construct($envParts[0], $debug, $suluContext);
     }
 
     public function registerContainerConfiguration(LoaderInterface $loader): void
     {
         parent::registerContainerConfiguration($loader);
 
         $context = $this->getContext();
         $loader->load(__DIR__ . '/config/config_' . $context . '.yml');
+        if ('' !== $this->appContext) {
+            $loader->load(__DIR__ . '/config/config_' . $this->appContext . '.yml');
+        }
+    }
+
+    public function registerBundles(): iterable
+    {
+        $bundles = parent::registerBundles();
+
+        if ('with_security' === $this->appContext) {
+            $bundles[] = new SecurityBundle();
+        }
+
+        return $bundles;
+    }
+
+    /**
+     * @return string
+     */
+    public function getCacheDir()
+    {
+        return parent::getCacheDir() . \ltrim('/' . $this->appContext);
+    }
+
+    /**
+     * @return string
+     */
+    public function getCommonCacheDir()
+    {
+        return parent::getCommonCacheDir() . \ltrim('/' . $this->appContext);
     }
 }
diff --git a/src/Sulu/Bundle/WebsiteBundle/Tests/Application/config/config_with_security.yml b/src/Sulu/Bundle/WebsiteBundle/Tests/Application/config/config_with_security.yml
new file mode 100644
index 00000000000..1611557269b
--- /dev/null
+++ b/src/Sulu/Bundle/WebsiteBundle/Tests/Application/config/config_with_security.yml
@@ -0,0 +1,40 @@
+security:
+    enable_authenticator_manager: true
+
+    access_decision_manager:
+        strategy: unanimous
+        allow_if_all_abstain: true
+
+    # https://symfony.com/doc/current/security.html#registering-the-user-hashing-passwords
+    password_hashers:
+        Sulu\Bundle\SecurityBundle\Entity\User: bcrypt
+
+    # https://symfony.com/doc/current/security.html#loading-the-user-the-user-provider
+    providers:
+        sulu:
+            id: sulu_security.user_provider
+
+    firewalls:
+        website:
+            pattern: ^/
+            lazy: true
+            provider: sulu
+            # The login and logout routes need to be created.
+            # For an advanced user management with registration and opt-in emails have a look at the:
+            # https://github.com/sulu/SuluCommunityBundle
+            # Also have a look at the user context based caching when you output user role specific data
+            # https://docs.sulu.io/en/2.2/cookbook/user-context-caching.html
+            form_login:
+                login_path: login
+                check_path: login
+            logout:
+                path: logout
+                target: /
+            remember_me:
+                secret:   "%kernel.secret%"
+                lifetime: 604800 # 1 week in seconds
+                path:     /
+
+sulu_security:
+    checker:
+        enabled: true
diff --git a/src/Sulu/Bundle/WebsiteBundle/Tests/Application/config/routing_website.yml b/src/Sulu/Bundle/WebsiteBundle/Tests/Application/config/routing_website.yml
index 8c053f59c49..fb901b9d640 100644
--- a/src/Sulu/Bundle/WebsiteBundle/Tests/Application/config/routing_website.yml
+++ b/src/Sulu/Bundle/WebsiteBundle/Tests/Application/config/routing_website.yml
@@ -13,5 +13,11 @@ sulu_media:
     resource: "@SuluMediaBundle/Resources/config/routing_website.yml"
 
 _portal_loader_test:
     resource: "routing_portal_loader_test.yml"
     type: portal
+
+login:
+    path: /login
+    controller:    Symfony\Bundle\FrameworkBundle\Controller\TemplateController
+    defaults:
+        template: login.html.twig
diff --git a/src/Sulu/Bundle/WebsiteBundle/Tests/Application/config/webspaces/sulu_lo.xml b/src/Sulu/Bundle/WebsiteBundle/Tests/Application/config/webspaces/sulu_lo.xml
index 5655035edb4..e3eccee0adf 100644
--- a/src/Sulu/Bundle/WebsiteBundle/Tests/Application/config/webspaces/sulu_lo.xml
+++ b/src/Sulu/Bundle/WebsiteBundle/Tests/Application/config/webspaces/sulu_lo.xml
@@ -87,6 +87,5 @@
                 </environment>
             </environments>
         </portal>
     </portals>
 </webspace>
-
diff --git a/src/Sulu/Bundle/WebsiteBundle/Tests/Functional/EventListener/SecurityListenerTest.php b/src/Sulu/Bundle/WebsiteBundle/Tests/Functional/EventListener/SecurityListenerTest.php
new file mode 100644
index 00000000000..68d9b49fff1
--- /dev/null
+++ b/src/Sulu/Bundle/WebsiteBundle/Tests/Functional/EventListener/SecurityListenerTest.php
@@ -0,0 +1,152 @@
+<?php
+
+/*
+ * This file is part of Sulu.
+ *
+ * (c) Sulu GmbH
+ *
+ * This source file is subject to the MIT license that is bundled
+ * with this source code in the file LICENSE.
+ */
+
+namespace Sulu\Bundle\WebsiteBundle\Tests\Functional;
+
+use Sulu\Bundle\PageBundle\Document\BasePageDocument;
+use Sulu\Bundle\PageBundle\Document\PageDocument;
+use Sulu\Bundle\SecurityBundle\Entity\AccessControl;
+use Sulu\Bundle\SecurityBundle\Entity\Permission;
+use Sulu\Bundle\SecurityBundle\Entity\Role;
+use Sulu\Bundle\TestBundle\Testing\SuluTestCase;
+use Sulu\Bundle\WebsiteBundle\Tests\Application\Kernel;
+use Sulu\Component\Content\Document\Behavior\SecurityBehavior;
+use Sulu\Component\Content\Document\WorkflowStage;
+use Symfony\Bundle\FrameworkBundle\KernelBrowser;
+
+/**
+ * @runTestsInSeparateProcesses This is necessary because the kernel is booted with different configurations.
+ */
+class SecurityListenerTest extends SuluTestCase
+{
+    /**
+     * @var KernelBrowser
+     */
+    private $client;
+
+    protected function setUp(): void
+    {
+        if (Kernel::VERSION_ID < 50000) { // @phpstan-ignore-line
+            $this->markTestSkipped('This test is only for Symfony 5.0 and above');
+        }
+
+        $this->client = $this->createWebsiteClient(['environment' => 'test_with_security']);
+        $this->purgeDatabase();
+        $this->initPhpcr();
+    }
+
+    public function testNoPermissions(): void
+    {
+        $pageDocument = $this->createSecuredPage();
+
+        $this->client->request('GET', 'http://sulu.lo/');
+        $response = $this->client->getResponse();
+
+        $this->assertSame(200, $response->getStatusCode());
+        $this->assertNull($response->headers->get('Location'));
+    }
+
+    public function testRedirectToLoginWhenNoAccess(): void
+    {
+        $pageDocument = $this->createSecuredPage();
+
+        $this->client->request('GET', 'http://sulu.lo/secure-area');
+        $response = $this->client->getResponse();
+
+        $this->assertSame(302, $response->getStatusCode());
+        $this->assertSame('http://sulu.lo/login', $response->headers->get('Location'));
+    }
+
+    private function createSecuredPage(): BasePageDocument
+    {
+        $documentManager = $this->getContainer()->get('sulu_document_manager.document_manager');
+        $entityManager = $this->getEntityManager();
+
+        $pageData = [
+            'locale' => 'en',
+            'title' => 'Secure Area',
+            'url' => '/secure-area',
+            'article' => '<p>Some sample text for this super secret area.</p>',
+            'structureType' => 'default',
+        ];
+
+        $extensionData = [
+            'seo' => [],
+            'excerpt' => [],
+        ];
+
+        /** @var PageDocument $pageDocument */
+        $pageDocument = $documentManager->create('page');
+
+        $pageDocument->setNavigationContexts([]);
+        $pageDocument->setLocale($pageData['locale']);
+        $pageDocument->setTitle($pageData['title']);
+        $pageDocument->setResourceSegment($pageData['url']);
+        $pageDocument->setStructureType($pageData['structureType']);
+        $pageDocument->setWorkflowStage(WorkflowStage::PUBLISHED);
+        $pageDocument->getStructure()->bind($pageData);
+        $pageDocument->setAuthor(1);
+        $pageDocument->setExtensionsData($extensionData);
+        $pageDocument->setPermissions([
+            1 => [ // do not allow anonymous users to access this page
+                'view' => false,
+            ],
+        ]);
+
+        $documentManager->persist(
+            $pageDocument,
+            'en',
+            ['parent_path' => '/cmf/sulu_io/contents']
+        );
+        $documentManager->flush();
+
+        // We need to add access control here as we do not have the document id before
+        $role = new Role();
+        $role->setName('Anonymous User Website');
+        $role->setSystem('sulu_io');
+        $role->setAnonymous(true);
+
+        $permission = new Permission();
+        $permission->setRole($role);
+        $permission->setPermissions(127);
+        $permission->setContext('sulu.webspaces.sulu_io');
+        $role->addPermission($permission);
+
+        $accessControl = new AccessControl();
+        $accessControl->setPermissions(0);
+        $accessControl->setEntityId($pageDocument->getUuid());
+        $accessControl->setEntityClass(SecurityBehavior::class);
+        $accessControl->setRole($role);
+
+        $entityManager->persist($permission);
+        $entityManager->persist($role);
+        $entityManager->persist($accessControl);
+        $entityManager->flush();
+
+        $pageDocument->setPermissions([
+            $role->getId() => [ // do not allow anonymous users to access this page
+                'view' => false,
+            ],
+        ]);
+        $documentManager->persist(
+            $pageDocument,
+            'en',
+            ['parent_path' => '/cmf/sulu_io/contents']
+        );
+        $documentManager->flush();
+        $documentManager->publish($pageDocument, 'en');
+
+        $documentManager->clear();
+        $entityManager->clear();
+
+        return $pageDocument;
+    }
+}
diff --git a/src/Sulu/Bundle/WebsiteBundle/Tests/Unit/Routing/ContentRouteProviderTest.php b/src/Sulu/Bundle/WebsiteBundle/Tests/Unit/Routing/ContentRouteProviderTest.php
index f55d26f95bd..1b1b4181241 100644
--- a/src/Sulu/Bundle/WebsiteBundle/Tests/Unit/Routing/ContentRouteProviderTest.php
+++ b/src/Sulu/Bundle/WebsiteBundle/Tests/Unit/Routing/ContentRouteProviderTest.php
@@ -33,19 +33,16 @@ use Sulu\Component\DocumentManager\Behavior\Mapping\TitleBehavior;
 use Sulu\Component\DocumentManager\Behavior\Mapping\UuidBehavior;
 use Sulu\Component\DocumentManager\Document\UnknownDocument;
 use Sulu\Component\DocumentManager\DocumentManagerInterface;
 use Sulu\Component\DocumentManager\Metadata;
 use Sulu\Component\Localization\Localization;
-use Sulu\Component\Security\Authorization\PermissionTypes;
 use Sulu\Component\Security\Authorization\SecurityCheckerInterface;
-use Sulu\Component\Security\Authorization\SecurityCondition;
 use Sulu\Component\Webspace\Analyzer\Attributes\RequestAttributes;
 use Sulu\Component\Webspace\Analyzer\RequestAnalyzer;
 use Sulu\Component\Webspace\Analyzer\RequestAnalyzerInterface;
 use Sulu\Component\Webspace\Manager\WebspaceManagerInterface;
 use Sulu\Component\Webspace\Portal;
-use Sulu\Component\Webspace\Security;
 use Sulu\Component\Webspace\Segment;
 use Sulu\Component\Webspace\Webspace;
 use Symfony\Component\HttpFoundation\Request;
 use Symfony\Component\Routing\Route;
 
@@ -182,165 +179,10 @@ class ContentRouteProviderTest extends TestCase
         $this->assertCount(1, $routes);
         $this->assertEquals($pageBridge->reveal(), $defaults['structure']);
         $this->assertEquals(false, $defaults['partial']);
     }
 
-    public function testSecurityChecker(): void
-    {
-        $attributes = $this->prophesize(RequestAttributes::class);
-
-        $attributes->getAttribute('localization', null)->willReturn(new Localization('de'));
-
-        $portal = new Portal();
-        $portal->setKey('portal');
-        $webspace = new Webspace();
-        $webspace->setKey('webspace');
-        $webspace->setTheme('theme');
-        $security = new Security();
-        $security->setSystem('website');
-        $security->setPermissionCheck(true);
-        $webspace->setSecurity($security);
-        $portal->setWebspace($webspace);
-        $attributes->getAttribute('portal', null)->willReturn($portal);
-
-        $attributes->getAttribute('matchType', null)->willReturn(RequestAnalyzer::MATCH_TYPE_FULL);
-        $attributes->getAttribute('resourceLocator', null)->willReturn(null);
-        $attributes->getAttribute('resourceLocatorPrefix', null)->willReturn('/de');
-
-        $this->resourceLocatorStrategy->loadByResourceLocator('', 'webspace', 'de')->willReturn('some-uuid');
-
-        $document = $this->prophesize(TitleBehavior::class)
-            ->willImplement(ExtensionBehavior::class)
-            ->willImplement(RedirectTypeBehavior::class)
-            ->willImplement(StructureBehavior::class)
-            ->willImplement(WebspaceBehavior::class)
-            ->willImplement(UuidBehavior::class);
-        $document->getUuid()->willReturn('some-uuid');
-        $document->getTitle()->willReturn('some-title');
-        $document->getWebspaceName()->willReturn('webspace');
-        $document->getLocale()->willReturn('de');
-        $document->getRedirectType()->willReturn(RedirectType::NONE);
-        $document->getStructureType()->willReturn('default');
-        $document->getUuid()->willReturn('some-uuid');
-        $document->getExtensionsData()->willReturn(['excerpt' => ['segments' => null]]);
-        $this->documentManager->find('some-uuid', 'de', ['load_ghost_content' => false])->willReturn($document->reveal());
-
-        $metadata = new Metadata();
-        $metadata->setAlias('page');
-        $structureMetadata = new StructureMetadata();
-        $this->documentInspector->getMetadata($document->reveal())->willReturn($metadata);
-        $this->documentInspector->getStructureMetadata($document->reveal())->willReturn($structureMetadata);
-
-        $pageBridge = $this->prophesize(PageBridge::class);
-        $pageBridge->getController()->willReturn('::Controller');
-        $this->structureManager->wrapStructure('page', $structureMetadata)->willReturn($pageBridge->reveal());
-
-        $request = new Request(
-            [],
-            [],
-            ['_sulu' => $attributes->reveal()],
-            [],
-            [],
-            ['REQUEST_URI' => \rawurlencode('/de')]
-        );
-
-        $pageBridge->setDocument($document->reveal())->shouldBeCalled();
-
-        $securityChecker = $this->prophesize(SecurityCheckerInterface::class);
-        $securityChecker->checkPermission(Argument::that(function(SecurityCondition $securityCondition) use ($document) {
-            $this->assertSame('some-uuid', $securityCondition->getObjectId());
-            $this->assertSame(\get_class($document->reveal()), $securityCondition->getObjectType());
-            $this->assertSame('de', $securityCondition->getLocale());
-            $this->assertSame('sulu.webspaces.webspace', $securityCondition->getSecurityContext());
-
-            return true;
-        }), PermissionTypes::VIEW)->shouldBeCalled();
-
-        $contentRouteProvider = $this->createContentRouteProvider($securityChecker->reveal());
-        $routes = $contentRouteProvider->getRouteCollectionForRequest($request);
-
-        $defaults = $routes->getIterator()->current()->getDefaults();
-
-        $this->assertCount(1, $routes);
-        $this->assertEquals($pageBridge->reveal(), $defaults['structure']);
-        $this->assertEquals(false, $defaults['partial']);
-    }
-
-    public function testSecurityCheckerWithoutPermissionCheck(): void
-    {
-        $attributes = $this->prophesize(RequestAttributes::class);
-
-        $attributes->getAttribute('localization', null)->willReturn(new Localization('de'));
-
-        $portal = new Portal();
-        $portal->setKey('portal');
-        $webspace = new Webspace();
-        $webspace->setKey('webspace');
-        $webspace->setTheme('theme');
-        $security = new Security();
-        $security->setSystem('website');
-        $security->setPermissionCheck(false);
-        $webspace->setSecurity($security);
-        $portal->setWebspace($webspace);
-        $attributes->getAttribute('portal', null)->willReturn($portal);
-
-        $attributes->getAttribute('matchType', null)->willReturn(RequestAnalyzer::MATCH_TYPE_FULL);
-        $attributes->getAttribute('resourceLocator', null)->willReturn(null);
-        $attributes->getAttribute('resourceLocatorPrefix', null)->willReturn('/de');
-
-        $this->resourceLocatorStrategy->loadByResourceLocator('', 'webspace', 'de')->willReturn('some-uuid');
-
-        $document = $this->prophesize(TitleBehavior::class)
-            ->willImplement(ExtensionBehavior::class)
-            ->willImplement(RedirectTypeBehavior::class)
-            ->willImplement(StructureBehavior::class)
-            ->willImplement(WebspaceBehavior::class)
-            ->willImplement(UuidBehavior::class);
-        $document->getUuid()->willReturn('some-uuid');
-        $document->getTitle()->willReturn('some-title');
-        $document->getWebspaceName()->willReturn('webspace');
-        $document->getLocale()->willReturn('de');
-        $document->getRedirectType()->willReturn(RedirectType::NONE);
-        $document->getStructureType()->willReturn('default');
-        $document->getUuid()->willReturn('some-uuid');
-        $document->getExtensionsData()->willReturn(['excerpt' => ['segments' => null]]);
-        $this->documentManager->find('some-uuid', 'de', ['load_ghost_content' => false])->willReturn($document->reveal());
-
-        $metadata = new Metadata();
-        $metadata->setAlias('page');
-        $structureMetadata = new StructureMetadata();
-        $this->documentInspector->getMetadata($document->reveal())->willReturn($metadata);
-        $this->documentInspector->getStructureMetadata($document->reveal())->willReturn($structureMetadata);
-
-        $pageBridge = $this->prophesize(PageBridge::class);
-        $pageBridge->getController()->willReturn('::Controller');
-        $this->structureManager->wrapStructure('page', $structureMetadata)->willReturn($pageBridge->reveal());
-
-        $request = new Request(
-            [],
-            [],
-            ['_sulu' => $attributes->reveal()],
-            [],
-            [],
-            ['REQUEST_URI' => \rawurlencode('/de')]
-        );
-
-        $pageBridge->setDocument($document->reveal())->shouldBeCalled();
-
-        $securityChecker = $this->prophesize(SecurityCheckerInterface::class);
-        $securityChecker->checkPermission(Argument::cetera())->shouldNotBeCalled();
-
-        $contentRouteProvider = $this->createContentRouteProvider($securityChecker->reveal());
-        $routes = $contentRouteProvider->getRouteCollectionForRequest($request);
-
-        $defaults = $routes->getIterator()->current()->getDefaults();
-
-        $this->assertCount(1, $routes);
-        $this->assertEquals($pageBridge->reveal(), $defaults['structure']);
-        $this->assertEquals(false, $defaults['partial']);
-    }
-
     public function testGetCollectionForRequestWithWrongSegment(): void
     {
         $attributes = $this->prophesize(RequestAttributes::class);
 
         $attributes->getAttribute('localization', null)->willReturn(new Localization('de'));
diff --git a/src/Sulu/Component/Webspace/Analyzer/Attributes/SystemRequestProcessor.php b/src/Sulu/Component/Webspace/Analyzer/Attributes/SystemRequestProcessor.php
new file mode 100644
index 00000000000..1a289f6e78a
--- /dev/null
+++ b/src/Sulu/Component/Webspace/Analyzer/Attributes/SystemRequestProcessor.php
@@ -0,0 +1,76 @@
+<?php
+
+/*
+ * This file is part of Sulu.
+ *
+ * (c) Sulu GmbH
+ *
+ * This source file is subject to the MIT license that is bundled
+ * with this source code in the file LICENSE.
+ */
+
+namespace Sulu\Component\Webspace\Analyzer\Attributes;
+
+use Sulu\Bundle\AdminBundle\Admin\Admin;
+use Sulu\Bundle\SecurityBundle\System\SystemStoreInterface;
+use Sulu\Component\HttpKernel\SuluKernel;
+use Sulu\Component\Webspace\PortalInformation;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * @internal
+ * @final
+ *
+ * This class is internal overriding or extending should not be required instead create an own RequestProcessor.
+ */
+class SystemRequestProcessor implements RequestProcessorInterface
+{
+    /**
+     * @var SystemStoreInterface
+     */
+    private $systemStore;
+
+    /**
+     * @var string
+     */
+    private $context;
+
+    public function __construct(
+        SystemStoreInterface $systemStore,
+        string $context
+    ) {
+        $this->systemStore = $systemStore;
+        $this->context = $context;
+    }
+
+    public function process(Request $request, RequestAttributes $requestAttributes)
+    {
+        $attributes = [];
+        if (SuluKernel::CONTEXT_ADMIN === $this->context) {
+            $this->systemStore->setSystem(Admin::SULU_ADMIN_SECURITY_SYSTEM);
+            $attributes['system'] = Admin::SULU_ADMIN_SECURITY_SYSTEM;
+
+            return new RequestAttributes($attributes);
+        }
+        $portalInformation = $requestAttributes->getAttribute('portalInformation');
+
+        if (!$portalInformation instanceof PortalInformation) {
+            return new RequestAttributes($attributes);
+        }
+
+        $webspace = $portalInformation->getWebspace();
+
+        $security = $webspace->getSecurity();
+        if ($security) {
+            $attributes['system'] = $security->getSystem();
+            $this->systemStore->setSystem($attributes['system']);
+        }
+
+        return new RequestAttributes($attributes);
+    }
+
+    public function validate(RequestAttributes $attributes)
+    {
+        return true;
+    }
+}

Vulnerable code: 			message: "#^If condition is always true\\.$#"
			message: "#^Method Sulu\\\\Bundle\\\\SecurityBundle\\\\EventListener\\\\SystemListener\\:\\:onKernelRequest\\(\\) has no return type specified\\.$#"
		-
			message: "#^Method Sulu\\\\Bundle\\\\SecurityBundle\\\\EventListener\\\\SystemListenerTest\\:\\:provideSetWebsiteSystem\\(\\) has no return type specified\\.$#"
			count: 1
			path: src/Sulu/Bundle/SecurityBundle/Tests/Unit/EventListener/SystemListenerTest.php

     * @var RequestAnalyzerInterface
        RequestAnalyzerInterface $requestAnalyzer,

        $webspace = $this->requestAnalyzer->getWebspace();
        if ($webspace) {
            $security = $webspace->getSecurity();
            if ($security) {
                $this->systemStore->setSystem($security->getSystem());

                return;
            }
        }
            <argument type="service" id="sulu_core.webspace.request_analyzer"/>
use Sulu\Component\Webspace\Security;
use Sulu\Component\Webspace\Webspace;
    public function provideSetWebsiteSystem()
    {
        return [
            ['sulu-test'],
            ['sulu-blog'],
        ];
    }

    /**
     * @dataProvider provideSetWebsiteSystem
     */
    public function testSetWebsiteSystem(string $system): void
    {
        $systemListener = $this->createSystemListener('website');
        $requestEvent = $this->prophesize(RequestEvent::class);

        $webspace = new Webspace();
        $security = new Security();
        $security->setSystem($system);
        $webspace->setSecurity($security);
        $this->requestAnalyzer->getWebspace()->willReturn($webspace);

        $systemListener->onKernelRequest($requestEvent->reveal());

        $this->systemStore->setSystem($system)->shouldBeCalled();
    }

            <argument type="service" id="sulu_security.security_checker" on-invalid="null"/>
use Sulu\Component\Security\Authorization\PermissionTypes;
use Sulu\Component\Security\Authorization\SecurityCondition;
            if ($this->securityChecker && $portal->getWebspace()->hasWebsiteSecurity()) {
                $this->securityChecker->checkPermission(
                    new SecurityCondition(
                        'sulu.webspaces.' . $document->getWebspaceName(),
                        $document->getLocale(),
                        \get_class($document),
                        $document->getUuid()
                    ),
                    PermissionTypes::VIEW
                );
            }

        parent::__construct($environment, $debug, $suluContext);

use Sulu\Component\Security\Authorization\PermissionTypes;
use Sulu\Component\Security\Authorization\SecurityCondition;
use Sulu\Component\Webspace\Security;
    public function testSecurityChecker(): void
    {
        $attributes = $this->prophesize(RequestAttributes::class);

        $attributes->getAttribute('localization', null)->willReturn(new Localization('de'));

        $portal = new Portal();
        $portal->setKey('portal');
        $webspace = new Webspace();
        $webspace->setKey('webspace');
        $webspace->setTheme('theme');
        $security = new Security();
        $security->setSystem('website');
        $security->setPermissionCheck(true);
        $webspace->setSecurity($security);
        $portal->setWebspace($webspace);
        $attributes->getAttribute('portal', null)->willReturn($portal);

        $attributes->getAttribute('matchType', null)->willReturn(RequestAnalyzer::MATCH_TYPE_FULL);
        $attributes->getAttribute('resourceLocator', null)->willReturn(null);
        $attributes->getAttribute('resourceLocatorPrefix', null)->willReturn('/de');

        $this->resourceLocatorStrategy->loadByResourceLocator('', 'webspace', 'de')->willReturn('some-uuid');

        $document = $this->prophesize(TitleBehavior::class)
            ->willImplement(ExtensionBehavior::class)
            ->willImplement(RedirectTypeBehavior::class)
            ->willImplement(StructureBehavior::class)
            ->willImplement(WebspaceBehavior::class)
            ->willImplement(UuidBehavior::class);
        $document->getUuid()->willReturn('some-uuid');
        $document->getTitle()->willReturn('some-title');
        $document->getWebspaceName()->willReturn('webspace');
        $document->getLocale()->willReturn('de');
        $document->getRedirectType()->willReturn(RedirectType::NONE);
        $document->getStructureType()->willReturn('default');
        $document->getUuid()->willReturn('some-uuid');
        $document->getExtensionsData()->willReturn(['excerpt' => ['segments' => null]]);
        $this->documentManager->find('some-uuid', 'de', ['load_ghost_content' => false])->willReturn($document->reveal());

        $metadata = new Metadata();
        $metadata->setAlias('page');
        $structureMetadata = new StructureMetadata();
        $this->documentInspector->getMetadata($document->reveal())->willReturn($metadata);
        $this->documentInspector->getStructureMetadata($document->reveal())->willReturn($structureMetadata);

        $pageBridge = $this->prophesize(PageBridge::class);
        $pageBridge->getController()->willReturn('::Controller');
        $this->structureManager->wrapStructure('page', $structureMetadata)->willReturn($pageBridge->reveal());

        $request = new Request(
            [],
            [],
            ['_sulu' => $attributes->reveal()],
            [],
            [],
            ['REQUEST_URI' => \rawurlencode('/de')]
        );

        $pageBridge->setDocument($document->reveal())->shouldBeCalled();

        $securityChecker = $this->prophesize(SecurityCheckerInterface::class);
        $securityChecker->checkPermission(Argument::that(function(SecurityCondition $securityCondition) use ($document) {
            $this->assertSame('some-uuid', $securityCondition->getObjectId());
            $this->assertSame(\get_class($document->reveal()), $securityCondition->getObjectType());
            $this->assertSame('de', $securityCondition->getLocale());
            $this->assertSame('sulu.webspaces.webspace', $securityCondition->getSecurityContext());

            return true;
        }), PermissionTypes::VIEW)->shouldBeCalled();

        $contentRouteProvider = $this->createContentRouteProvider($securityChecker->reveal());
        $routes = $contentRouteProvider->getRouteCollectionForRequest($request);

        $defaults = $routes->getIterator()->current()->getDefaults();

        $this->assertCount(1, $routes);
        $this->assertEquals($pageBridge->reveal(), $defaults['structure']);
        $this->assertEquals(false, $defaults['partial']);
    }

    public function testSecurityCheckerWithoutPermissionCheck(): void
    {
        $attributes = $this->prophesize(RequestAttributes::class);

        $attributes->getAttribute('localization', null)->willReturn(new Localization('de'));

        $portal = new Portal();
        $portal->setKey('portal');
        $webspace = new Webspace();
        $webspace->setKey('webspace');
        $webspace->setTheme('theme');
        $security = new Security();
        $security->setSystem('website');
        $security->setPermissionCheck(false);
        $webspace->setSecurity($security);
        $portal->setWebspace($webspace);
        $attributes->getAttribute('portal', null)->willReturn($portal);

        $attributes->getAttribute('matchType', null)->willReturn(RequestAnalyzer::MATCH_TYPE_FULL);
        $attributes->getAttribute('resourceLocator', null)->willReturn(null);
        $attributes->getAttribute('resourceLocatorPrefix', null)->willReturn('/de');

        $this->resourceLocatorStrategy->loadByResourceLocator('', 'webspace', 'de')->willReturn('some-uuid');

        $document = $this->prophesize(TitleBehavior::class)
            ->willImplement(ExtensionBehavior::class)
            ->willImplement(RedirectTypeBehavior::class)
            ->willImplement(StructureBehavior::class)
            ->willImplement(WebspaceBehavior::class)
            ->willImplement(UuidBehavior::class);
        $document->getUuid()->willReturn('some-uuid');
        $document->getTitle()->willReturn('some-title');
        $document->getWebspaceName()->willReturn('webspace');
        $document->getLocale()->willReturn('de');
        $document->getRedirectType()->willReturn(RedirectType::NONE);
        $document->getStructureType()->willReturn('default');
        $document->getUuid()->willReturn('some-uuid');
        $document->getExtensionsData()->willReturn(['excerpt' => ['segments' => null]]);
        $this->documentManager->find('some-uuid', 'de', ['load_ghost_content' => false])->willReturn($document->reveal());

        $metadata = new Metadata();
        $metadata->setAlias('page');
        $structureMetadata = new StructureMetadata();
        $this->documentInspector->getMetadata($document->reveal())->willReturn($metadata);
        $this->documentInspector->getStructureMetadata($document->reveal())->willReturn($structureMetadata);

        $pageBridge = $this->prophesize(PageBridge::class);
        $pageBridge->getController()->willReturn('::Controller');
        $this->structureManager->wrapStructure('page', $structureMetadata)->willReturn($pageBridge->reveal());

        $request = new Request(
            [],
            [],
            ['_sulu' => $attributes->reveal()],
            [],
            [],
            ['REQUEST_URI' => \rawurlencode('/de')]
        );

        $pageBridge->setDocument($document->reveal())->shouldBeCalled();

        $securityChecker = $this->prophesize(SecurityCheckerInterface::class);
        $securityChecker->checkPermission(Argument::cetera())->shouldNotBeCalled();

        $contentRouteProvider = $this->createContentRouteProvider($securityChecker->reveal());
        $routes = $contentRouteProvider->getRouteCollectionForRequest($request);

        $defaults = $routes->getIterator()->current()->getDefaults();

        $this->assertCount(1, $routes);
        $this->assertEquals($pageBridge->reveal(), $defaults['structure']);
        $this->assertEquals(false, $defaults['partial']);
    }

Fixed code: 			message: "#^Method Sulu\\\\Bundle\\\\SecurityBundle\\\\EventListener\\\\SystemListener\\:\\:onKernelRequest\\(\\) has no return type specified\\.$#"
			message: "#^Property Sulu\\\\Bundle\\\\SecurityBundle\\\\EventListener\\\\SystemListener\\:\\:\\$requestAnalyzer is never read, only written\\.$#"
		-
			message: "#^Property Sulu\\\\Bundle\\\\WebsiteBundle\\\\Routing\\\\ContentRouteProvider\\:\\:\\$securityChecker is never read, only written\\.$#"
			count: 1
			path: src/Sulu/Bundle/WebsiteBundle/Routing/ContentRouteProvider.php

        <service id="sulu_core.request_processor.system"
                 class="Sulu\Component\Webspace\Analyzer\Attributes\SystemRequestProcessor">
            <argument type="service" id="sulu_security.system_store" />
            <argument>%sulu.context%</argument>
            <tag name="sulu.request_attributes" priority="-32"/>
        </service>
     * @var RequestAnalyzerInterface|null
        ?RequestAnalyzerInterface $requestAnalyzer,

        if (null !== $requestAnalyzer) {
            @trigger_deprecation('sulu/sulu', '2.4', 'The argument "%s" in class "%s" is deprecated and not longer required set `null` instead.', RequestAnalyzerInterface::class, __CLASS__);
        }

            <argument>null</argument>

<?php

/*
 * This file is part of Sulu.
 *
 * (c) Sulu GmbH
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */

namespace Sulu\Bundle\WebsiteBundle\EventListener;

use Sulu\Bundle\PageBundle\Document\BasePageDocument;
use Sulu\Component\Content\Compat\Structure\PageBridge;
use Sulu\Component\DocumentManager\Subscriber\EventSubscriberInterface;
use Sulu\Component\Security\Authorization\PermissionTypes;
use Sulu\Component\Security\Authorization\SecurityCheckerInterface;
use Sulu\Component\Security\Authorization\SecurityCondition;
use Sulu\Component\Webspace\Analyzer\Attributes\RequestAttributes;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\HttpKernel\KernelEvents;

class SecurityListener implements EventSubscriberInterface
{
    /**
     * @var SecurityCheckerInterface|null
     */
    private $securityChecker;

    public function __construct(
        ?SecurityCheckerInterface $securityChecker = null
    ) {
        $this->securityChecker = $securityChecker;
    }

    public static function getSubscribedEvents(): array
    {
        return [
            KernelEvents::REQUEST => [
                ['onKernelRequest', 7], // set the security listener after the firewall and after the routing listener
            ],
        ];
    }

    public function onKernelRequest(RequestEvent $event): void
    {
        $request = $event->getRequest();

        if (null === $this->securityChecker) {
            return;
        }

        $requestAttributes = $request->attributes->get('_sulu');
        if (!$requestAttributes instanceof RequestAttributes) {
            return;
        }

        $webspace = $requestAttributes->getAttribute('webspace');

        $structure = $request->attributes->get('structure');
        if (!$structure instanceof PageBridge) {
            return;
        }

        $document = $structure->getDocument();
        if (!$document instanceof BasePageDocument) {
            return;
        }

        if ($webspace->hasWebsiteSecurity()) {
            $this->securityChecker->checkPermission(
                new SecurityCondition(
                    'sulu.webspaces.' . $document->getWebspaceName(),
                    $document->getLocale(),
                    \get_class($document),
                    $document->getUuid()
                ),
                PermissionTypes::VIEW
            );
        }
    }
}
        <service id="sulu_website.event_listener.security_listener"
                 class="Sulu\Bundle\WebsiteBundle\EventListener\SecurityListener">
            <argument type="service" id="sulu_security.security_checker" on-invalid="null"/>

            <tag name="kernel.event_subscriber"/>
        </service>

            <argument>null</argument><!-- not longer required -->
use Webmozart\Assert\Assert;
        Assert::null($securityChecker, 'The security checker should be called by the SecurityListener not the ContentRouteProvider.'); // people who overwrite the ContentRouteProvider should make aware of that they also need to refactor this
use Symfony\Bundle\SecurityBundle\SecurityBundle;
    /**
     * @var string
     */
    private $appContext;

        $envParts = \explode('_', $environment, 2);
        $this->appContext = $envParts[1] ?? '';

        parent::__construct($envParts[0], $debug, $suluContext);
        if ('' !== $this->appContext) {
            $loader->load(__DIR__ . '/config/config_' . $this->appContext . '.yml');
        }
    }

    public function registerBundles(): iterable
    {
        $bundles = parent::registerBundles();

        if ('with_security' === $this->appContext) {
            $bundles[] = new SecurityBundle();
        }

        return $bundles;
    }

    /**
     * @return string
     */
    public function getCacheDir()
    {
        return parent::getCacheDir() . \ltrim('/' . $this->appContext);
    }

    /**
     * @return string
     */
    public function getCommonCacheDir()
    {
        return parent::getCommonCacheDir() . \ltrim('/' . $this->appContext);
security:
    enable_authenticator_manager: true

    access_decision_manager:
        strategy: unanimous
        allow_if_all_abstain: true

    # https://symfony.com/doc/current/security.html#registering-the-user-hashing-passwords
    password_hashers:
        Sulu\Bundle\SecurityBundle\Entity\User: bcrypt

    # https://symfony.com/doc/current/security.html#loading-the-user-the-user-provider
    providers:
        sulu:
            id: sulu_security.user_provider

    firewalls:
        website:
            pattern: ^/
            lazy: true
            provider: sulu
            # The login and logout routes need to be created.
            # For an advanced user management with registration and opt-in emails have a look at the:
            # https://github.com/sulu/SuluCommunityBundle
            # Also have a look at the user context based caching when you output user role specific data
            # https://docs.sulu.io/en/2.2/cookbook/user-context-caching.html
            form_login:
                login_path: login
                check_path: login
            logout:
                path: logout
                target: /
            remember_me:
                secret:   "%kernel.secret%"
                lifetime: 604800 # 1 week in seconds
                path:     /

sulu_security:
    checker:
        enabled: true

login:
    path: /login
    controller:    Symfony\Bundle\FrameworkBundle\Controller\TemplateController
    defaults:
        template: login.html.twig
<?php

/*
 * This file is part of Sulu.
 *
 * (c) Sulu GmbH
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */

namespace Sulu\Bundle\WebsiteBundle\Tests\Functional;

use Sulu\Bundle\PageBundle\Document\BasePageDocument;
use Sulu\Bundle\PageBundle\Document\PageDocument;
use Sulu\Bundle\SecurityBundle\Entity\AccessControl;
use Sulu\Bundle\SecurityBundle\Entity\Permission;
use Sulu\Bundle\SecurityBundle\Entity\Role;
use Sulu\Bundle\TestBundle\Testing\SuluTestCase;
use Sulu\Bundle\WebsiteBundle\Tests\Application\Kernel;
use Sulu\Component\Content\Document\Behavior\SecurityBehavior;
use Sulu\Component\Content\Document\WorkflowStage;
use Symfony\Bundle\FrameworkBundle\KernelBrowser;

/**
 * @runTestsInSeparateProcesses This is necessary because the kernel is booted with different configurations.
 */
class SecurityListenerTest extends SuluTestCase
{
    /**
     * @var KernelBrowser
     */
    private $client;

    protected function setUp(): void
    {
        if (Kernel::VERSION_ID < 50000) { // @phpstan-ignore-line
            $this->markTestSkipped('This test is only for Symfony 5.0 and above');
        }

        $this->client = $this->createWebsiteClient(['environment' => 'test_with_security']);
        $this->purgeDatabase();
        $this->initPhpcr();
    }

    public function testNoPermissions(): void
    {
        $pageDocument = $this->createSecuredPage();

        $this->client->request('GET', 'http://sulu.lo/');
        $response = $this->client->getResponse();

        $this->assertSame(200, $response->getStatusCode());
        $this->assertNull($response->headers->get('Location'));
    }

    public function testRedirectToLoginWhenNoAccess(): void
    {
        $pageDocument = $this->createSecuredPage();

        $this->client->request('GET', 'http://sulu.lo/secure-area');
        $response = $this->client->getResponse();

        $this->assertSame(302, $response->getStatusCode());
        $this->assertSame('http://sulu.lo/login', $response->headers->get('Location'));
    }

    private function createSecuredPage(): BasePageDocument
    {
        $documentManager = $this->getContainer()->get('sulu_document_manager.document_manager');
        $entityManager = $this->getEntityManager();

        $pageData = [
            'locale' => 'en',
            'title' => 'Secure Area',
            'url' => '/secure-area',
            'article' => '<p>Some sample text for this super secret area.</p>',
            'structureType' => 'default',
        ];

        $extensionData = [
            'seo' => [],
            'excerpt' => [],
        ];

        /** @var PageDocument $pageDocument */
        $pageDocument = $documentManager->create('page');

        $pageDocument->setNavigationContexts([]);
        $pageDocument->setLocale($pageData['locale']);
        $pageDocument->setTitle($pageData['title']);
        $pageDocument->setResourceSegment($pageData['url']);
        $pageDocument->setStructureType($pageData['structureType']);
        $pageDocument->setWorkflowStage(WorkflowStage::PUBLISHED);
        $pageDocument->getStructure()->bind($pageData);
        $pageDocument->setAuthor(1);
        $pageDocument->setExtensionsData($extensionData);
        $pageDocument->setPermissions([
            1 => [ // do not allow anonymous users to access this page
                'view' => false,
            ],
        ]);

        $documentManager->persist(
            $pageDocument,
            'en',
            ['parent_path' => '/cmf/sulu_io/contents']
        );
        $documentManager->flush();

        // We need to add access control here as we do not have the document id before
        $role = new Role();
        $role->setName('Anonymous User Website');
        $role->setSystem('sulu_io');
        $role->setAnonymous(true);

        $permission = new Permission();
        $permission->setRole($role);
        $permission->setPermissions(127);
        $permission->setContext('sulu.webspaces.sulu_io');
        $role->addPermission($permission);

        $accessControl = new AccessControl();
        $accessControl->setPermissions(0);
        $accessControl->setEntityId($pageDocument->getUuid());
        $accessControl->setEntityClass(SecurityBehavior::class);
        $accessControl->setRole($role);

        $entityManager->persist($permission);
        $entityManager->persist($role);
        $entityManager->persist($accessControl);
        $entityManager->flush();

        $pageDocument->setPermissions([
            $role->getId() => [ // do not allow anonymous users to access this page
                'view' => false,
            ],
        ]);
        $documentManager->persist(
            $pageDocument,
            'en',
            ['parent_path' => '/cmf/sulu_io/contents']
        );
        $documentManager->flush();
        $documentManager->publish($pageDocument, 'en');

        $documentManager->clear();
        $entityManager->clear();

        return $pageDocument;
    }
}
<?php

/*
 * This file is part of Sulu.
 *
 * (c) Sulu GmbH
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */

namespace Sulu\Component\Webspace\Analyzer\Attributes;

use Sulu\Bundle\AdminBundle\Admin\Admin;
use Sulu\Bundle\SecurityBundle\System\SystemStoreInterface;
use Sulu\Component\HttpKernel\SuluKernel;
use Sulu\Component\Webspace\PortalInformation;
use Symfony\Component\HttpFoundation\Request;

/**
 * @internal
 * @final
 *
 * This class is internal overriding or extending should not be required instead create an own RequestProcessor.
 */
class SystemRequestProcessor implements RequestProcessorInterface
{
    /**
     * @var SystemStoreInterface
     */
    private $systemStore;

    /**
     * @var string
     */
    private $context;

    public function __construct(
        SystemStoreInterface $systemStore,
        string $context
    ) {
        $this->systemStore = $systemStore;
        $this->context = $context;
    }

    public function process(Request $request, RequestAttributes $requestAttributes)
    {
        $attributes = [];
        if (SuluKernel::CONTEXT_ADMIN === $this->context) {
            $this->systemStore->setSystem(Admin::SULU_ADMIN_SECURITY_SYSTEM);
            $attributes['system'] = Admin::SULU_ADMIN_SECURITY_SYSTEM;

            return new RequestAttributes($attributes);
        }
        $portalInformation = $requestAttributes->getAttribute('portalInformation');

        if (!$portalInformation instanceof PortalInformation) {
            return new RequestAttributes($attributes);
        }

        $webspace = $portalInformation->getWebspace();

        $security = $webspace->getSecurity();
        if ($security) {
            $attributes['system'] = $security->getSystem();
            $this->systemStore->setSystem($attributes['system']);
        }

        return new RequestAttributes($attributes);
    }

    public function validate(RequestAttributes $attributes)
    {
        return true;
    }
}