From a62b002aad767b92db2e07ebe3c7dee740de7ce1 Mon Sep 17 00:00:00 2001 From: Mohamed El Mrabet Date: Sun, 16 Nov 2025 21:45:56 +0100 Subject: [PATCH] Refactor: Move CspNonceProvider to Model and deprecate old Helper class --- .../AdminAnalytics/ViewModel/Metadata.php | 4 +- .../Magento/Csp/Helper/CspNonceProvider.php | 70 +++------------ app/code/Magento/Csp/Helper/InlineUtil.php | 1 + .../Magento/Csp/Model/CspNonceProvider.php | 86 +++++++++++++++++++ .../Test/Unit/Model/CspNonceProviderTest.php | 70 +++++++++++++++ .../Magento/Csp/ViewModel/NonceProvider.php | 2 +- app/code/Magento/Paypal/Model/Config.php | 2 +- .../Paypal/Test/Unit/Model/ConfigTest.php | 2 +- .../Magento/Csp/Helper/InlineUtilTest.php | 2 + .../CspNonceProviderMock.php | 3 +- 10 files changed, 178 insertions(+), 64 deletions(-) create mode 100644 app/code/Magento/Csp/Model/CspNonceProvider.php create mode 100644 app/code/Magento/Csp/Test/Unit/Model/CspNonceProviderTest.php rename dev/tests/integration/testsuite/Magento/Csp/{Helper => Model}/CspNonceProviderMock.php (95%) diff --git a/app/code/Magento/AdminAnalytics/ViewModel/Metadata.php b/app/code/Magento/AdminAnalytics/ViewModel/Metadata.php index 95a01006fa107..9c664d7e2b61d 100644 --- a/app/code/Magento/AdminAnalytics/ViewModel/Metadata.php +++ b/app/code/Magento/AdminAnalytics/ViewModel/Metadata.php @@ -8,12 +8,12 @@ namespace Magento\AdminAnalytics\ViewModel; +use Magento\Backend\Model\Auth\Session; use Magento\Config\Model\Config\Backend\Admin\Custom; -use Magento\Csp\Helper\CspNonceProvider; +use Magento\Csp\Model\CspNonceProvider; use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\App\ObjectManager; use Magento\Framework\App\ProductMetadataInterface; -use Magento\Backend\Model\Auth\Session; use Magento\Framework\App\State; use Magento\Framework\View\Element\Block\ArgumentInterface; use Magento\Store\Model\Information; diff --git a/app/code/Magento/Csp/Helper/CspNonceProvider.php b/app/code/Magento/Csp/Helper/CspNonceProvider.php index 2111974ae9ca0..6f791e5357786 100644 --- a/app/code/Magento/Csp/Helper/CspNonceProvider.php +++ b/app/code/Magento/Csp/Helper/CspNonceProvider.php @@ -8,79 +8,33 @@ namespace Magento\Csp\Helper; -use Magento\Csp\Model\Collector\DynamicCollector; -use Magento\Csp\Model\Policy\FetchPolicy; -use Magento\Framework\Exception\LocalizedException; -use Magento\Framework\Math\Random; +use Magento\Csp\Model\CspNonceProvider as CspNonceProviderModel; /** - * This helper class is used to provide nonce for CSP - * - * It also adds a nonce to the CSP header. + * @deprecated This class was moved to Magento\Csp\Model\CspNonceProvider. + * It is kept for backward compatibility and will be removed in a future major version. + * @see \Magento\Csp\Model\CspNonceProvider */ class CspNonceProvider { /** - * @var string + * @var CspNonceProviderModel */ - private const NONCE_LENGTH = 32; + private CspNonceProviderModel $model; /** - * @var string + * @param CspNonceProviderModel $model */ - private string $nonce; - - /** - * @var Random - */ - private Random $random; - - /** - * @var DynamicCollector - */ - private DynamicCollector $dynamicCollector; - - /** - * @param Random $random - * @param DynamicCollector $dynamicCollector - */ - public function __construct( - Random $random, - DynamicCollector $dynamicCollector - ) { - $this->random = $random; - $this->dynamicCollector = $dynamicCollector; + public function __construct(CspNonceProviderModel $model) + { + $this->model = $model; } /** - * Generate nonce and add it to the CSP header - * - * @return string - * @throws LocalizedException + * @deprecated */ public function generateNonce(): string { - if (empty($this->nonce)) { - $this->nonce = $this->random->getRandomString( - self::NONCE_LENGTH, - Random::CHARS_DIGITS . Random::CHARS_LOWERS - ); - - $policy = new FetchPolicy( - 'script-src', - false, - [], - [], - false, - false, - false, - [$this->nonce], - [] - ); - - $this->dynamicCollector->add($policy); - } - - return base64_encode($this->nonce); + return $this->model->generateNonce(); } } diff --git a/app/code/Magento/Csp/Helper/InlineUtil.php b/app/code/Magento/Csp/Helper/InlineUtil.php index 27c9970295ab2..75b0b17d52ba6 100644 --- a/app/code/Magento/Csp/Helper/InlineUtil.php +++ b/app/code/Magento/Csp/Helper/InlineUtil.php @@ -11,6 +11,7 @@ use Magento\Csp\Api\InlineUtilInterface; use Magento\Csp\Model\Collector\ConfigCollector; use Magento\Csp\Model\Collector\DynamicCollector; +use Magento\Csp\Model\CspNonceProvider; use Magento\Csp\Model\Policy\FetchPolicy; use Magento\Framework\App\ObjectManager; use Magento\Framework\View\Helper\SecureHtmlRender\EventHandlerData; diff --git a/app/code/Magento/Csp/Model/CspNonceProvider.php b/app/code/Magento/Csp/Model/CspNonceProvider.php new file mode 100644 index 0000000000000..4d152bc64b6b2 --- /dev/null +++ b/app/code/Magento/Csp/Model/CspNonceProvider.php @@ -0,0 +1,86 @@ +random = $random; + $this->dynamicCollector = $dynamicCollector; + } + + /** + * Generate nonce and add it to the CSP header + * + * @return string + * @throws LocalizedException + */ + public function generateNonce(): string + { + if (empty($this->nonce)) { + $this->nonce = $this->random->getRandomString( + self::NONCE_LENGTH, + Random::CHARS_DIGITS . Random::CHARS_LOWERS + ); + + $policy = new FetchPolicy( + 'script-src', + false, + [], + [], + false, + false, + false, + [$this->nonce], + [] + ); + + $this->dynamicCollector->add($policy); + } + + return base64_encode($this->nonce); + } +} diff --git a/app/code/Magento/Csp/Test/Unit/Model/CspNonceProviderTest.php b/app/code/Magento/Csp/Test/Unit/Model/CspNonceProviderTest.php new file mode 100644 index 0000000000000..467fb828b47ce --- /dev/null +++ b/app/code/Magento/Csp/Test/Unit/Model/CspNonceProviderTest.php @@ -0,0 +1,70 @@ +randomMock = $this->createMock(Random::class); + $this->collectorMock = $this->createMock(DynamicCollector::class); + + $this->provider = new CspNonceProvider( + $this->randomMock, + $this->collectorMock + ); + } + + public function testGenerateNonce(): void + { + $nonce = 'abc123nonce'; + $this->randomMock + ->method('getRandomString') + ->willReturn($nonce); + $this->collectorMock + ->expects($this->once()) + ->method('add') + ->with($this->callback(function ($policy) use ($nonce) { + return $policy instanceof FetchPolicy + && in_array($nonce, $policy->getNonceValues()); + })); + + $result = $this->provider->generateNonce(); + $this->assertEquals(base64_encode($nonce), $result); + } + + public function testGenerateNonceCalledTwiceReturnsSameValue(): void + { + $nonce = 'firstnonce'; + + $this->randomMock + ->method('getRandomString') + ->willReturn($nonce); + + $this->collectorMock + ->expects($this->once()) + ->method('add'); + + $firstCall = $this->provider->generateNonce(); + $secondCall = $this->provider->generateNonce(); + + $this->assertSame($firstCall, $secondCall); + } +} diff --git a/app/code/Magento/Csp/ViewModel/NonceProvider.php b/app/code/Magento/Csp/ViewModel/NonceProvider.php index fe1acc9589017..b9c860cefcb42 100644 --- a/app/code/Magento/Csp/ViewModel/NonceProvider.php +++ b/app/code/Magento/Csp/ViewModel/NonceProvider.php @@ -7,7 +7,7 @@ namespace Magento\Csp\ViewModel; -use Magento\Csp\Helper\CspNonceProvider; +use Magento\Csp\Model\CspNonceProvider; use Magento\Framework\Exception\LocalizedException; use Magento\Framework\View\Element\Block\ArgumentInterface; diff --git a/app/code/Magento/Paypal/Model/Config.php b/app/code/Magento/Paypal/Model/Config.php index 010fe84cca311..e23fa8c01e5ec 100644 --- a/app/code/Magento/Paypal/Model/Config.php +++ b/app/code/Magento/Paypal/Model/Config.php @@ -6,7 +6,7 @@ namespace Magento\Paypal\Model; -use Magento\Csp\Helper\CspNonceProvider; +use Magento\Csp\Model\CspNonceProvider; use Magento\Framework\App\ObjectManager; use Magento\Payment\Helper\Formatter; diff --git a/app/code/Magento/Paypal/Test/Unit/Model/ConfigTest.php b/app/code/Magento/Paypal/Test/Unit/Model/ConfigTest.php index f54b1ec990512..a07f2e29fd89b 100644 --- a/app/code/Magento/Paypal/Test/Unit/Model/ConfigTest.php +++ b/app/code/Magento/Paypal/Test/Unit/Model/ConfigTest.php @@ -7,7 +7,7 @@ namespace Magento\Paypal\Test\Unit\Model; -use Magento\Csp\Helper\CspNonceProvider; +use Magento\Csp\Model\CspNonceProvider; use Magento\Directory\Helper\Data; use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; diff --git a/dev/tests/integration/testsuite/Magento/Csp/Helper/InlineUtilTest.php b/dev/tests/integration/testsuite/Magento/Csp/Helper/InlineUtilTest.php index cd6e850de3b0a..3d1e0120021a3 100644 --- a/dev/tests/integration/testsuite/Magento/Csp/Helper/InlineUtilTest.php +++ b/dev/tests/integration/testsuite/Magento/Csp/Helper/InlineUtilTest.php @@ -7,9 +7,11 @@ namespace Magento\Csp\Helper; +use Magento\Csp\Model\CspNonceProviderMock; use Magento\Csp\Api\Data\PolicyInterface; use Magento\Csp\Model\Collector\DynamicCollector; use Magento\Csp\Model\Collector\DynamicCollectorMock; +use Magento\Csp\Model\CspNonceProvider; use Magento\Csp\Model\Policy\FetchPolicy; use Magento\Framework\View\Helper\SecureHtmlRenderer; use Magento\TestFramework\Helper\Bootstrap; diff --git a/dev/tests/integration/testsuite/Magento/Csp/Helper/CspNonceProviderMock.php b/dev/tests/integration/testsuite/Magento/Csp/Model/CspNonceProviderMock.php similarity index 95% rename from dev/tests/integration/testsuite/Magento/Csp/Helper/CspNonceProviderMock.php rename to dev/tests/integration/testsuite/Magento/Csp/Model/CspNonceProviderMock.php index 75ec9abbe1770..0817436c5780f 100644 --- a/dev/tests/integration/testsuite/Magento/Csp/Helper/CspNonceProviderMock.php +++ b/dev/tests/integration/testsuite/Magento/Csp/Model/CspNonceProviderMock.php @@ -5,9 +5,10 @@ */ declare(strict_types=1); -namespace Magento\Csp\Helper; +namespace Magento\Csp\Model; use Magento\Csp\Model\Collector\DynamicCollector; +use Magento\Csp\Model\CspNonceProvider; use Magento\Csp\Model\Policy\FetchPolicy; use Magento\Framework\Math\Random;