VYPR
Moderate severityNVD Advisory· Published Mar 6, 2024· Updated Apr 16, 2025

Sulu grants access to pages regardless of role permissions

CVE-2024-27915

Description

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.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
sulu/suluPackagist
>= 2.2.0, < 2.4.172.4.17
sulu/suluPackagist
>= 2.5.0-alpha1, < 2.5.132.5.13

Affected products

1

Patches

1
ec9c3f99e153

Merge pull request from GHSA-jr83-m233-gg6p

https://github.com/sulu/suluAlexander SchranzMar 4, 2024via ghsa
17 files changed · +429 224
  • phpstan-baseline.neon+7 7 modified
    @@ -29621,12 +29621,12 @@ parameters:
     			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
     
    @@ -30550,11 +30550,6 @@ parameters:
     			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
    @@ -34920,6 +34915,11 @@ parameters:
     			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
    
  • src/Sulu/Bundle/CoreBundle/Resources/config/request_analyzer.xml+6 0 modified
    @@ -51,6 +51,12 @@
                 <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"/>
    
  • src/Sulu/Bundle/SecurityBundle/EventListener/SystemListener.php+7 12 modified
    @@ -27,7 +27,7 @@ class SystemListener implements EventSubscriberInterface
         private $systemStore;
     
         /**
    -     * @var RequestAnalyzerInterface
    +     * @var RequestAnalyzerInterface|null
          */
         private $requestAnalyzer;
     
    @@ -38,10 +38,15 @@ class SystemListener implements EventSubscriberInterface
     
         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;
         }
    @@ -58,15 +63,5 @@ public function onKernelRequest(RequestEvent $requestEvent)
     
                 return;
             }
    -
    -        $webspace = $this->requestAnalyzer->getWebspace();
    -        if ($webspace) {
    -            $security = $webspace->getSecurity();
    -            if ($security) {
    -                $this->systemStore->setSystem($security->getSystem());
    -
    -                return;
    -            }
    -        }
         }
     }
    
  • src/Sulu/Bundle/SecurityBundle/Resources/config/services.xml+1 1 modified
    @@ -306,7 +306,7 @@
     
             <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"/>
    
  • src/Sulu/Bundle/SecurityBundle/Tests/Unit/EventListener/SystemListenerTest.php+0 29 modified
    @@ -15,8 +15,6 @@
     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
    @@ -46,33 +44,6 @@ public function testSetAdminSystem(): void
             $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);
    
  • src/Sulu/Bundle/WebsiteBundle/EventListener/RouterListener.php+1 0 modified
    @@ -54,6 +54,7 @@ public function onKernelRequest(RequestEvent $event)
             // 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);
    
  • src/Sulu/Bundle/WebsiteBundle/EventListener/SecurityListener.php+83 0 added
    @@ -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
    +            );
    +        }
    +    }
    +}
    
  • src/Sulu/Bundle/WebsiteBundle/Resources/config/services.xml+7 0 modified
    @@ -299,6 +299,13 @@
                 <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">
    
  • src/Sulu/Bundle/WebsiteBundle/Resources/config/website.xml+1 1 modified
    @@ -21,7 +21,7 @@
                 <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"/>
    
  • src/Sulu/Bundle/WebsiteBundle/Routing/ContentRouteProvider.php+2 14 modified
    @@ -24,16 +24,15 @@
     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.
    @@ -97,6 +96,7 @@ public function __construct(
             $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;
         }
     
    @@ -166,18 +166,6 @@ public function getRouteCollectionForRequest(Request $request)
                     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, '/');
    
  • src/Sulu/Bundle/WebsiteBundle/Tests/Application/config/config_with_security.yml+40 0 added
    @@ -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
    
  • src/Sulu/Bundle/WebsiteBundle/Tests/Application/config/routing_website.yml+6 0 modified
    @@ -15,3 +15,9 @@ sulu_media:
     _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
    
  • src/Sulu/Bundle/WebsiteBundle/Tests/Application/config/webspaces/sulu_lo.xml+0 1 modified
    @@ -89,4 +89,3 @@
             </portal>
         </portals>
     </webspace>
    -
    
  • src/Sulu/Bundle/WebsiteBundle/Tests/Application/Kernel.php+40 1 modified
    @@ -12,18 +12,27 @@
     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
    @@ -32,5 +41,35 @@ public function registerContainerConfiguration(LoaderInterface $loader): void
     
             $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);
         }
     }
    
  • src/Sulu/Bundle/WebsiteBundle/Tests/Functional/EventListener/SecurityListenerTest.php+152 0 added
    @@ -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;
    +    }
    +}
    
  • src/Sulu/Bundle/WebsiteBundle/Tests/Unit/Routing/ContentRouteProviderTest.php+0 158 modified
    @@ -35,15 +35,12 @@
     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;
    @@ -184,161 +181,6 @@ public function testGetCollectionForRequest(): void
             $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);
    
  • src/Sulu/Component/Webspace/Analyzer/Attributes/SystemRequestProcessor.php+76 0 added
    @@ -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;
    +    }
    +}
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

4

News mentions

0

No linked articles in our index yet.