diff --git a/app/code/Magento/AdminAnalytics/ViewModel/Metadata.php b/app/code/Magento/AdminAnalytics/ViewModel/Metadata.php index 15d4afef086cd..1d8421e74376a 100644 --- a/app/code/Magento/AdminAnalytics/ViewModel/Metadata.php +++ b/app/code/Magento/AdminAnalytics/ViewModel/Metadata.php @@ -9,7 +9,9 @@ namespace Magento\AdminAnalytics\ViewModel; use Magento\Config\Model\Config\Backend\Admin\Custom; +use Magento\Csp\Helper\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; @@ -21,6 +23,11 @@ */ class Metadata implements ArgumentInterface { + /** + * @var string + */ + private $nonce; + /** * @var State */ @@ -41,22 +48,33 @@ class Metadata implements ArgumentInterface */ private $config; + /** + * @var CspNonceProvider + */ + private $nonceProvider; + /** * @param ProductMetadataInterface $productMetadata * @param Session $authSession * @param State $appState * @param ScopeConfigInterface $config + * @param CspNonceProvider|null $nonceProvider */ public function __construct( ProductMetadataInterface $productMetadata, Session $authSession, State $appState, - ScopeConfigInterface $config + ScopeConfigInterface $config, + CspNonceProvider $nonceProvider = null ) { $this->productMetadata = $productMetadata; $this->authSession = $authSession; $this->appState = $appState; $this->config = $config; + + $this->nonceProvider = $nonceProvider ?: ObjectManager::getInstance()->get(CspNonceProvider::class); + + $this->nonce = $this->nonceProvider->generateNonce(); } /** @@ -156,4 +174,14 @@ public function getCurrentUserRoleName(): string { return $this->authSession->getUser()->getRole()->getRoleName(); } + + /** + * Get a random nonce for each request. + * + * @return string + */ + public function getNonce(): string + { + return $this->nonce; + } } diff --git a/app/code/Magento/AdminAnalytics/composer.json b/app/code/Magento/AdminAnalytics/composer.json index ef3829fd149c6..b3e463f67181f 100644 --- a/app/code/Magento/AdminAnalytics/composer.json +++ b/app/code/Magento/AdminAnalytics/composer.json @@ -1,23 +1,25 @@ { "name": "magento/module-admin-analytics", "description": "N/A", + "type": "magento2-module", + "license": [ + "OSL-3.0", + "AFL-3.0" + ], "config": { "sort-packages": true }, + "version": "100.4.4-p9", "require": { "php": "~7.4.0||~8.1.0", - "magento/framework": "*", - "magento/module-backend": "*", - "magento/module-config": "*", - "magento/module-store": "*", - "magento/module-ui": "*", - "magento/module-release-notification": "*" + "magento/framework": "103.0.*", + "magento/module-backend": "102.0.*", + "magento/module-config": "101.2.*", + "magento/module-store": "101.1.*", + "magento/module-ui": "101.2.*", + "magento/module-release-notification": "100.4.*", + "magento/module-csp": "100.4.*" }, - "type": "magento2-module", - "license": [ - "OSL-3.0", - "AFL-3.0" - ], "autoload": { "files": [ "registration.php" @@ -27,3 +29,4 @@ } } } + diff --git a/app/code/Magento/AdminAnalytics/view/adminhtml/templates/tracking.phtml b/app/code/Magento/AdminAnalytics/view/adminhtml/templates/tracking.phtml index 4b155e1a5ae5b..0ccd4d1b26082 100644 --- a/app/code/Magento/AdminAnalytics/view/adminhtml/templates/tracking.phtml +++ b/app/code/Magento/AdminAnalytics/view/adminhtml/templates/tracking.phtml @@ -6,6 +6,7 @@ /** * @var \Magento\Framework\View\Helper\SecureHtmlRenderer $secureRenderer + * @var \Magento\Framework\Escaper $escaper */ ?> @@ -22,18 +23,25 @@ getMetadata(); +$nonce = $escaper->escapeJs($metadata->getNonce()); $scriptString = ' var adminAnalyticsMetadata = { - "secure_base_url": "' . $block->escapeJs($metadata->getSecureBaseUrlForScope()) . '", - "version": "' . $block->escapeJs($metadata->getMagentoVersion()) . '", - "product_edition": "' . $block->escapeJs($metadata->getProductEdition()) . '", - "user": "' . $block->escapeJs($metadata->getCurrentUser()) . '", - "mode": "' . $block->escapeJs($metadata->getMode()) . '", - "store_name_default": "' . $block->escapeJs($metadata->getStoreNameForScope()) . '", - "admin_user_created": "' . $block->escapeJs($metadata->getCurrentUserCreatedDate()) . '", - "admin_user_logdate": "' . $block->escapeJs($metadata->getCurrentUserLogDate()) . '", - "admin_user_role_name": "' . $block->escapeJs($metadata->getCurrentUserRoleName()) . '" + "secure_base_url": "' . $escaper->escapeJs($metadata->getSecureBaseUrlForScope()) . '", + "version": "' . $escaper->escapeJs($metadata->getMagentoVersion()) . '", + "product_edition": "' . $escaper->escapeJs($metadata->getProductEdition()) . '", + "user": "' . $escaper->escapeJs($metadata->getCurrentUser()) . '", + "mode": "' . $escaper->escapeJs($metadata->getMode()) . '", + "store_name_default": "' . $escaper->escapeJs($metadata->getStoreNameForScope()) . '", + "admin_user_created": "' . $escaper->escapeJs($metadata->getCurrentUserCreatedDate()) . '", + "admin_user_logdate": "' . $escaper->escapeJs($metadata->getCurrentUserLogDate()) . '", + "admin_user_role_name": "' . $escaper->escapeJs($metadata->getCurrentUserRoleName()) . '" }; + + var digitalData = { + "nonce": "' . $nonce . '" + }; + + var cspNonce = "' . $nonce . '"; '; ?> renderTag('script', [], $scriptString, false); ?> diff --git a/app/code/Magento/AdminNotification/Block/Grid/MassAction/MarkAsReadVisibility.php b/app/code/Magento/AdminNotification/Block/Grid/MassAction/MarkAsReadVisibility.php new file mode 100644 index 0000000000000..90dd17908de84 --- /dev/null +++ b/app/code/Magento/AdminNotification/Block/Grid/MassAction/MarkAsReadVisibility.php @@ -0,0 +1,39 @@ +authorization = $authorizationInterface; + } + + /** + * @inheritdoc + */ + public function isVisible() + { + return $this->authorization->isAllowed(MarkAsRead::ADMIN_RESOURCE); + } +} diff --git a/app/code/Magento/AdminNotification/Block/Grid/MassAction/RemoveVisibility.php b/app/code/Magento/AdminNotification/Block/Grid/MassAction/RemoveVisibility.php new file mode 100644 index 0000000000000..1b5190a0f515c --- /dev/null +++ b/app/code/Magento/AdminNotification/Block/Grid/MassAction/RemoveVisibility.php @@ -0,0 +1,39 @@ +authorization = $authorizationInterface; + } + + /** + * @inheritdoc + */ + public function isVisible() + { + return $this->authorization->isAllowed(Remove::ADMIN_RESOURCE); + } +} diff --git a/app/code/Magento/AdminNotification/Block/Grid/Renderer/Actions.php b/app/code/Magento/AdminNotification/Block/Grid/Renderer/Actions.php index f74f62ef000e6..ad40ab6de3953 100644 --- a/app/code/Magento/AdminNotification/Block/Grid/Renderer/Actions.php +++ b/app/code/Magento/AdminNotification/Block/Grid/Renderer/Actions.php @@ -1,15 +1,14 @@ getUrl() ? '' . __('Read Details') . '' : ''; - $markAsReadHtml = !$row->getIsRead() ? '' . __( - 'Mark as Read' - ) . '' : ''; + $markAsReadHtml = !$row->getIsRead() + && $this->_authorization->isAllowed(MarkAsRead::ADMIN_RESOURCE) ? + '' . __( + 'Mark as Read' + ) . '' : ''; + + $removeUrl = $this->getUrl( + '*/*/remove/', + [ + '_current' => true, + 'id' => $row->getNotificationId(), + ActionInterface::PARAM_NAME_URL_ENCODED => $this->_urlHelper->getEncodedUrl() + ] + ); + + $removeHtml = $this->_authorization->isAllowed(Remove::ADMIN_RESOURCE) ? + '' + . __('Remove') . '' : ''; - $encodedUrl = $this->_urlHelper->getEncodedUrl(); return sprintf( - '%s%s%s', + '%s%s%s', $readDetailsHtml, $markAsReadHtml, - $this->getUrl( - '*/*/remove/', - [ - '_current' => true, - 'id' => $row->getNotificationId(), - ActionInterface::PARAM_NAME_URL_ENCODED => $encodedUrl - ] - ), - __('Are you sure?'), - __('Remove') + $removeHtml, ); } } diff --git a/app/code/Magento/AdminNotification/Controller/Adminhtml/Notification/AjaxMarkAsRead.php b/app/code/Magento/AdminNotification/Controller/Adminhtml/Notification/AjaxMarkAsRead.php index be128435b62a1..555efe1439c0e 100644 --- a/app/code/Magento/AdminNotification/Controller/Adminhtml/Notification/AjaxMarkAsRead.php +++ b/app/code/Magento/AdminNotification/Controller/Adminhtml/Notification/AjaxMarkAsRead.php @@ -3,6 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\AdminNotification\Controller\Adminhtml\Notification; use Magento\AdminNotification\Controller\Adminhtml\Notification; @@ -21,6 +23,13 @@ class AjaxMarkAsRead extends Notification implements HttpPostActionInterface */ private $notificationService; + /** + * Authorization level of a basic admin session + * + * @see _isAllowed() + */ + public const ADMIN_RESOURCE = 'Magento_AdminNotification::mark_as_read'; + /** * @param Action\Context $context * @param NotificationService $notificationService diff --git a/app/code/Magento/AdminNotification/Test/Unit/Block/Grid/Renderer/ActionsTest.php b/app/code/Magento/AdminNotification/Test/Unit/Block/Grid/Renderer/ActionsTest.php index 9cc600681b13d..795a822aeba31 100644 --- a/app/code/Magento/AdminNotification/Test/Unit/Block/Grid/Renderer/ActionsTest.php +++ b/app/code/Magento/AdminNotification/Test/Unit/Block/Grid/Renderer/ActionsTest.php @@ -14,6 +14,7 @@ use Magento\AdminNotification\Block\Grid\Renderer\Actions; use Magento\Backend\Block\Context; +use Magento\Framework\AuthorizationInterface; use Magento\Framework\DataObject; use Magento\Framework\Escaper; use Magento\Framework\Url\Helper\Data; @@ -35,7 +36,13 @@ protected function setUp(): void /** @var Escaper|MockObject $escaperMock */ $escaperMock = $this->createMock(Escaper::class); - $escaperMock->expects($this->once())->method('escapeUrl')->willReturn('https://magento.com'); + $escaperMock->expects($this->atLeastOnce())->method('escapeUrl')->willReturn('https://magento.com'); + + /** @var AuthorizationInterface|MockObject $authorizationMock */ + $authorizationMock = $this->getMockForAbstractClass(AuthorizationInterface::class); + $authorizationMock->expects($this->atLeastOnce()) + ->method('isAllowed') + ->willReturn(true); /** @var UrlInterface|MockObject $urlBuilder */ $urlBuilder = $this->getMockForAbstractClass(UrlInterface::class); @@ -43,8 +50,9 @@ protected function setUp(): void /** @var Context|MockObject $contextMock */ $contextMock = $this->createMock(Context::class); - $contextMock->expects($this->once())->method('getEscaper')->willReturn($escaperMock); + $contextMock->expects($this->atLeastOnce())->method('getEscaper')->willReturn($escaperMock); $contextMock->expects($this->once())->method('getUrlBuilder')->willReturn($urlBuilder); + $contextMock->expects($this->once())->method('getAuthorization')->willReturn($authorizationMock); /** @var Data|MockObject $urlHelperMock */ $urlHelperMock = $this->createMock(Data::class); @@ -65,7 +73,7 @@ public function testShouldRenderMessageWhenUrlIsGiven() : void // Ignoring Code Style at this point due to the long HEREDOC // phpcs:disable $expected = <<Read DetailsRemove +Read DetailsRemove HTML; // phpcs:enable diff --git a/app/code/Magento/AdminNotification/composer.json b/app/code/Magento/AdminNotification/composer.json index 28ca1f626a2cd..2775a0aa6a2b7 100644 --- a/app/code/Magento/AdminNotification/composer.json +++ b/app/code/Magento/AdminNotification/composer.json @@ -1,24 +1,25 @@ { "name": "magento/module-admin-notification", "description": "N/A", + "type": "magento2-module", + "license": [ + "OSL-3.0", + "AFL-3.0" + ], "config": { "sort-packages": true }, + "version": "100.4.3-p11", "require": { "php": "~7.4.0||~8.1.0", "lib-libxml": "*", - "magento/framework": "*", - "magento/module-backend": "*", - "magento/module-media-storage": "*", - "magento/module-store": "*", - "magento/module-ui": "*", - "magento/module-config": "*" + "magento/framework": "103.0.*", + "magento/module-backend": "102.0.*", + "magento/module-media-storage": "100.4.*", + "magento/module-store": "101.1.*", + "magento/module-ui": "101.2.*", + "magento/module-config": "101.2.*" }, - "type": "magento2-module", - "license": [ - "OSL-3.0", - "AFL-3.0" - ], "autoload": { "files": [ "registration.php" @@ -28,3 +29,4 @@ } } } + diff --git a/app/code/Magento/AdminNotification/view/adminhtml/layout/adminhtml_notification_block.xml b/app/code/Magento/AdminNotification/view/adminhtml/layout/adminhtml_notification_block.xml index 06fd380cb2a44..e2eea914af688 100644 --- a/app/code/Magento/AdminNotification/view/adminhtml/layout/adminhtml_notification_block.xml +++ b/app/code/Magento/AdminNotification/view/adminhtml/layout/adminhtml_notification_block.xml @@ -61,11 +61,13 @@ Mark as Read */*/massMarkAsRead + Magento\AdminNotification\Block\Grid\MassAction\MarkAsReadVisibility Remove */*/massRemove Are you sure? + Magento\AdminNotification\Block\Grid\MassAction\RemoveVisibility diff --git a/app/code/Magento/AdvancedPricingImportExport/composer.json b/app/code/Magento/AdvancedPricingImportExport/composer.json index 59ea74cf4ddcb..f40a9ef5750c5 100644 --- a/app/code/Magento/AdvancedPricingImportExport/composer.json +++ b/app/code/Magento/AdvancedPricingImportExport/composer.json @@ -1,26 +1,27 @@ { "name": "magento/module-advanced-pricing-import-export", "description": "N/A", + "type": "magento2-module", + "license": [ + "OSL-3.0", + "AFL-3.0" + ], "config": { "sort-packages": true }, + "version": "100.4.4", "require": { "php": "~7.4.0||~8.1.0", - "magento/framework": "*", - "magento/module-catalog": "*", - "magento/module-catalog-import-export": "*", - "magento/module-catalog-inventory": "*", - "magento/module-customer": "*", - "magento/module-eav": "*", - "magento/module-import-export": "*", - "magento/module-store": "*", - "magento/module-directory": "*" + "magento/framework": "103.0.*", + "magento/module-catalog": "104.0.*", + "magento/module-catalog-import-export": "101.1.*", + "magento/module-catalog-inventory": "100.4.*", + "magento/module-customer": "103.0.*", + "magento/module-eav": "102.1.*", + "magento/module-import-export": "101.0.*", + "magento/module-store": "101.1.*", + "magento/module-directory": "100.4.*" }, - "type": "magento2-module", - "license": [ - "OSL-3.0", - "AFL-3.0" - ], "autoload": { "files": [ "registration.php" @@ -30,3 +31,4 @@ } } } + diff --git a/app/code/Magento/AdvancedSearch/Controller/Adminhtml/Search/System/Config/TestConnection.php b/app/code/Magento/AdvancedSearch/Controller/Adminhtml/Search/System/Config/TestConnection.php index be72d0a82822d..3f0168f2332cb 100644 --- a/app/code/Magento/AdvancedSearch/Controller/Adminhtml/Search/System/Config/TestConnection.php +++ b/app/code/Magento/AdvancedSearch/Controller/Adminhtml/Search/System/Config/TestConnection.php @@ -4,22 +4,27 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\AdvancedSearch\Controller\Adminhtml\Search\System\Config; use Magento\Backend\App\Action; use Magento\Backend\App\Action\Context; use Magento\AdvancedSearch\Model\Client\ClientResolver; +use Magento\Framework\App\Action\HttpPostActionInterface; +use Magento\Framework\Controller\Result\Json; use Magento\Framework\Controller\Result\JsonFactory; +use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Filter\StripTags; -class TestConnection extends Action +class TestConnection extends Action implements HttpPostActionInterface { /** * Authorization level of a basic admin session. * * @see _isAllowed() */ - const ADMIN_RESOURCE = 'Magento_CatalogSearch::config_catalog_search'; + public const ADMIN_RESOURCE = 'Magento_Catalog::config_catalog'; /** * @var ClientResolver @@ -57,7 +62,7 @@ public function __construct( /** * Check for connection to server * - * @return \Magento\Framework\Controller\Result\Json + * @return Json */ public function execute() { @@ -69,7 +74,7 @@ public function execute() try { if (empty($options['engine'])) { - throw new \Magento\Framework\Exception\LocalizedException( + throw new LocalizedException( __('Missing search engine parameter.') ); } @@ -77,14 +82,14 @@ public function execute() if ($response) { $result['success'] = true; } - } catch (\Magento\Framework\Exception\LocalizedException $e) { + } catch (LocalizedException $e) { $result['errorMessage'] = $e->getMessage(); } catch (\Exception $e) { $message = __($e->getMessage()); $result['errorMessage'] = $this->tagFilter->filter($message); } - /** @var \Magento\Framework\Controller\Result\Json $resultJson */ + /** @var Json $resultJson */ $resultJson = $this->resultJsonFactory->create(); return $resultJson->setData($result); } diff --git a/app/code/Magento/AdvancedSearch/composer.json b/app/code/Magento/AdvancedSearch/composer.json index 30205c5255cdd..15c30eb4147cc 100644 --- a/app/code/Magento/AdvancedSearch/composer.json +++ b/app/code/Magento/AdvancedSearch/composer.json @@ -1,25 +1,26 @@ { "name": "magento/module-advanced-search", "description": "N/A", + "type": "magento2-module", + "license": [ + "OSL-3.0", + "AFL-3.0" + ], "config": { "sort-packages": true }, + "version": "100.4.2-p11", "require": { - "magento/framework": "*", - "magento/module-backend": "*", - "magento/module-catalog": "*", - "magento/module-catalog-search": "*", - "magento/module-config": "*", - "magento/module-customer": "*", - "magento/module-search": "*", - "magento/module-store": "*", + "magento/framework": "103.0.*", + "magento/module-backend": "102.0.*", + "magento/module-catalog": "104.0.*", + "magento/module-catalog-search": "102.0.*", + "magento/module-config": "101.2.*", + "magento/module-customer": "103.0.*", + "magento/module-search": "101.1.*", + "magento/module-store": "101.1.*", "php": "~7.4.0||~8.1.0" }, - "type": "magento2-module", - "license": [ - "OSL-3.0", - "AFL-3.0" - ], "autoload": { "files": [ "registration.php" @@ -29,3 +30,4 @@ } } } + diff --git a/app/code/Magento/Amqp/composer.json b/app/code/Magento/Amqp/composer.json index c7d8d49fb0003..6f2b360634d3e 100644 --- a/app/code/Magento/Amqp/composer.json +++ b/app/code/Magento/Amqp/composer.json @@ -1,20 +1,21 @@ { "name": "magento/module-amqp", "description": "N/A", + "type": "magento2-module", + "license": [ + "OSL-3.0", + "AFL-3.0" + ], "config": { "sort-packages": true }, + "version": "100.4.2", "require": { - "magento/framework": "*", - "magento/framework-amqp": "*", - "magento/framework-message-queue": "*", + "magento/framework": "103.0.*", + "magento/framework-amqp": "100.4.*", + "magento/framework-message-queue": "100.4.*", "php": "~7.4.0||~8.1.0" }, - "type": "magento2-module", - "license": [ - "OSL-3.0", - "AFL-3.0" - ], "autoload": { "files": [ "registration.php" @@ -24,3 +25,4 @@ } } } + diff --git a/app/code/Magento/AmqpStore/composer.json b/app/code/Magento/AmqpStore/composer.json index 4d4685dda0b51..53d1e8d12db23 100644 --- a/app/code/Magento/AmqpStore/composer.json +++ b/app/code/Magento/AmqpStore/composer.json @@ -1,24 +1,25 @@ { "name": "magento/module-amqp-store", "description": "N/A", + "type": "magento2-module", + "license": [ + "OSL-3.0", + "AFL-3.0" + ], "config": { "sort-packages": true }, + "version": "100.4.2", "require": { - "magento/framework": "*", - "magento/framework-amqp": "*", - "magento/module-store": "*", + "magento/framework": "103.0.*", + "magento/framework-amqp": "100.4.*", + "magento/module-store": "101.1.*", "php": "~7.4.0||~8.1.0" }, "suggest": { - "magento/module-asynchronous-operations": "*", - "magento/framework-message-queue": "*" + "magento/module-asynchronous-operations": "100.4.*", + "magento/framework-message-queue": "100.4.*" }, - "type": "magento2-module", - "license": [ - "OSL-3.0", - "AFL-3.0" - ], "autoload": { "files": [ "registration.php" @@ -28,3 +29,4 @@ } } } + diff --git a/app/code/Magento/Analytics/Test/Mftf/ActionGroup/AssertAdminAdvancedReportingPageUrlActionGroup.xml b/app/code/Magento/Analytics/Test/Mftf/ActionGroup/AssertAdminAdvancedReportingPageUrlActionGroup.xml index ac4fca843a36b..3f0bedd344d24 100644 --- a/app/code/Magento/Analytics/Test/Mftf/ActionGroup/AssertAdminAdvancedReportingPageUrlActionGroup.xml +++ b/app/code/Magento/Analytics/Test/Mftf/ActionGroup/AssertAdminAdvancedReportingPageUrlActionGroup.xml @@ -15,6 +15,6 @@ - + diff --git a/app/code/Magento/Analytics/composer.json b/app/code/Magento/Analytics/composer.json index 9bf08b4b068ca..2a29c61b93ae0 100644 --- a/app/code/Magento/Analytics/composer.json +++ b/app/code/Magento/Analytics/composer.json @@ -1,19 +1,20 @@ { "name": "magento/module-analytics", "description": "N/A", - "require": { - "php": "~7.4.0||~8.1.0", - "magento/module-backend": "*", - "magento/module-config": "*", - "magento/module-integration": "*", - "magento/module-store": "*", - "magento/framework": "*" - }, "type": "magento2-module", "license": [ "OSL-3.0", "AFL-3.0" ], + "version": "100.4.4-p4", + "require": { + "php": "~7.4.0||~8.1.0", + "magento/module-backend": "102.0.*", + "magento/module-config": "101.2.*", + "magento/module-integration": "100.4.*", + "magento/module-store": "101.1.*", + "magento/framework": "103.0.*" + }, "autoload": { "files": [ "registration.php" @@ -23,3 +24,4 @@ } } } + diff --git a/app/code/Magento/AsynchronousOperations/composer.json b/app/code/Magento/AsynchronousOperations/composer.json index b09ca94052e87..ba34d2f770ee9 100644 --- a/app/code/Magento/AsynchronousOperations/composer.json +++ b/app/code/Magento/AsynchronousOperations/composer.json @@ -1,27 +1,28 @@ { "name": "magento/module-asynchronous-operations", "description": "N/A", + "type": "magento2-module", + "license": [ + "OSL-3.0", + "AFL-3.0" + ], "config": { "sort-packages": true }, + "version": "100.4.4", "require": { - "magento/framework": "*", - "magento/framework-message-queue": "*", - "magento/framework-bulk": "*", - "magento/module-authorization": "*", - "magento/module-backend": "*", - "magento/module-ui": "*", + "magento/framework": "103.0.*", + "magento/framework-message-queue": "100.4.*", + "magento/framework-bulk": "101.0.*", + "magento/module-authorization": "100.4.*", + "magento/module-backend": "102.0.*", + "magento/module-ui": "101.2.*", "php": "~7.4.0||~8.1.0" }, "suggest": { - "magento/module-admin-notification": "*", + "magento/module-admin-notification": "100.4.*", "magento/module-logging": "*" }, - "type": "magento2-module", - "license": [ - "OSL-3.0", - "AFL-3.0" - ], "autoload": { "files": [ "registration.php" @@ -31,3 +32,4 @@ } } } + diff --git a/app/code/Magento/Authorization/composer.json b/app/code/Magento/Authorization/composer.json index d122e8b29b46e..b4b0fa6ab43ab 100644 --- a/app/code/Magento/Authorization/composer.json +++ b/app/code/Magento/Authorization/composer.json @@ -1,19 +1,20 @@ { "name": "magento/module-authorization", "description": "Authorization module provides access to Magento ACL functionality.", + "type": "magento2-module", + "license": [ + "OSL-3.0", + "AFL-3.0" + ], "config": { "sort-packages": true }, + "version": "100.4.4", "require": { "php": "~7.4.0||~8.1.0", - "magento/framework": "*", - "magento/module-backend": "*" + "magento/framework": "103.0.*", + "magento/module-backend": "102.0.*" }, - "type": "magento2-module", - "license": [ - "OSL-3.0", - "AFL-3.0" - ], "autoload": { "files": [ "registration.php" @@ -23,3 +24,4 @@ } } } + diff --git a/app/code/Magento/AwsS3/composer.json b/app/code/Magento/AwsS3/composer.json index 470580df35f59..874341cce831d 100644 --- a/app/code/Magento/AwsS3/composer.json +++ b/app/code/Magento/AwsS3/composer.json @@ -1,20 +1,21 @@ { "name": "magento/module-aws-s3", "description": "N/A", + "type": "magento2-module", + "license": [ + "proprietary" + ], "config": { "sort-packages": true }, + "version": "100.4.2", "require": { "php": "~7.4.0||~8.1.0", - "magento/framework": "*", - "magento/module-remote-storage": "*", + "magento/framework": "103.0.*", + "magento/module-remote-storage": "100.4.*", "league/flysystem": "^2.0", "league/flysystem-aws-s3-v3": "^2.0" }, - "type": "magento2-module", - "license": [ - "proprietary" - ], "autoload": { "files": [ "registration.php" @@ -24,3 +25,4 @@ } } } + diff --git a/app/code/Magento/Backend/Block/System/Store/Grid/Render/Group.php b/app/code/Magento/Backend/Block/System/Store/Grid/Render/Group.php index 3d7154eb20f92..34d588c2dc080 100644 --- a/app/code/Magento/Backend/Block/System/Store/Grid/Render/Group.php +++ b/app/code/Magento/Backend/Block/System/Store/Grid/Render/Group.php @@ -3,8 +3,12 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Backend\Block\System\Store\Grid\Render; +use Magento\Framework\DataObject; + /** * Store render group * @@ -13,9 +17,9 @@ class Group extends \Magento\Backend\Block\Widget\Grid\Column\Renderer\AbstractRenderer { /** - * {@inheritdoc} + * @inheritDoc */ - public function render(\Magento\Framework\DataObject $row) + public function render(DataObject $row) { if (!$row->getData($this->getColumn()->getIndex())) { return null; @@ -28,6 +32,6 @@ public function render(\Magento\Framework\DataObject $row) '">' . $this->escapeHtml($row->getData($this->getColumn()->getIndex())) . '
' - . '(' . __('Code') . ': ' . $row->getGroupCode() . ')'; + . '(' . __('Code') . ': ' . $this->escapeHtml($row->getGroupCode()) . ')'; } } diff --git a/app/code/Magento/Backend/Controller/Adminhtml/System/Design.php b/app/code/Magento/Backend/Controller/Adminhtml/System/Design.php index d61a76055b3e5..6e04c6e882786 100644 --- a/app/code/Magento/Backend/Controller/Adminhtml/System/Design.php +++ b/app/code/Magento/Backend/Controller/Adminhtml/System/Design.php @@ -14,10 +14,10 @@ abstract class Design extends Action * * @see _isAllowed() */ - const ADMIN_RESOURCE = 'Magento_Backend::design'; + public const ADMIN_RESOURCE = 'Magento_Backend::schedule'; /** - * Core registry + * Core registry instance * * @var \Magento\Framework\Registry */ diff --git a/app/code/Magento/Backend/composer.json b/app/code/Magento/Backend/composer.json index 65aa05fe71e56..709664b0b3190 100644 --- a/app/code/Magento/Backend/composer.json +++ b/app/code/Magento/Backend/composer.json @@ -1,38 +1,39 @@ { "name": "magento/module-backend", "description": "N/A", + "type": "magento2-module", + "license": [ + "OSL-3.0", + "AFL-3.0" + ], "config": { "sort-packages": true }, + "version": "102.0.4-p11", "require": { "php": "~7.4.0||~8.1.0", - "magento/framework": "*", - "magento/module-backup": "*", - "magento/module-catalog": "*", - "magento/module-config": "*", - "magento/module-cms": "*", - "magento/module-customer": "*", - "magento/module-developer": "*", - "magento/module-directory": "*", - "magento/module-eav": "*", - "magento/module-quote": "*", - "magento/module-reports": "*", - "magento/module-require-js": "*", - "magento/module-sales": "*", - "magento/module-security": "*", - "magento/module-store": "*", - "magento/module-translation": "*", - "magento/module-ui": "*", - "magento/module-user": "*" + "magento/framework": "103.0.*", + "magento/module-backup": "100.4.*", + "magento/module-catalog": "104.0.*", + "magento/module-config": "101.2.*", + "magento/module-cms": "104.0.*", + "magento/module-customer": "103.0.*", + "magento/module-developer": "100.4.*", + "magento/module-directory": "100.4.*", + "magento/module-eav": "102.1.*", + "magento/module-quote": "101.2.*", + "magento/module-reports": "100.4.*", + "magento/module-require-js": "100.4.*", + "magento/module-sales": "103.0.*", + "magento/module-security": "100.4.*", + "magento/module-store": "101.1.*", + "magento/module-translation": "100.4.*", + "magento/module-ui": "101.2.*", + "magento/module-user": "101.2.*" }, "suggest": { - "magento/module-theme": "*" + "magento/module-theme": "101.1.*" }, - "type": "magento2-module", - "license": [ - "OSL-3.0", - "AFL-3.0" - ], "autoload": { "files": [ "registration.php", @@ -43,3 +44,4 @@ } } } + diff --git a/app/code/Magento/Backend/etc/adminhtml/system.xml b/app/code/Magento/Backend/etc/adminhtml/system.xml index 57cc36da95cfe..04a52530e52c8 100644 --- a/app/code/Magento/Backend/etc/adminhtml/system.xml +++ b/app/code/Magento/Backend/etc/adminhtml/system.xml @@ -10,6 +10,9 @@ + + + diff --git a/app/code/Magento/Backend/view/adminhtml/templates/widget/form/element.phtml b/app/code/Magento/Backend/view/adminhtml/templates/widget/form/element.phtml index 299f53dc9e3ef..561f4adfd16d3 100644 --- a/app/code/Magento/Backend/view/adminhtml/templates/widget/form/element.phtml +++ b/app/code/Magento/Backend/view/adminhtml/templates/widget/form/element.phtml @@ -94,7 +94,7 @@ $htmlId = $element->getHtmlId(); theme : "advanced", elements : "{$block->escapeJs($element->getName())}", plugins : "inlinepopups,style,layer,table,save,advhr,advimage,advlink,emotions,iespell," - + "insertdatetime,preview,zoom,media,searchreplace,print,contextmenu,paste,directionality," + + "insertdatetime,preview,zoom,media,searchreplace,print,paste,directionality," + "fullscreen,noneditable,visualchars,nonbreaking,xhtmlxtras", theme_advanced_buttons1 : "newdocument,|,bold,italic,underline,strikethrough,|" + ",justifyleft,justifycenter,justifyright,justifyfull,|" diff --git a/app/code/Magento/Backup/composer.json b/app/code/Magento/Backup/composer.json index e7437a3077aa7..137a008f10209 100644 --- a/app/code/Magento/Backup/composer.json +++ b/app/code/Magento/Backup/composer.json @@ -1,21 +1,22 @@ { "name": "magento/module-backup", "description": "N/A", + "type": "magento2-module", + "license": [ + "OSL-3.0", + "AFL-3.0" + ], "config": { "sort-packages": true }, + "version": "100.4.4", "require": { "php": "~7.4.0||~8.1.0", - "magento/framework": "*", - "magento/module-backend": "*", - "magento/module-cron": "*", - "magento/module-store": "*" + "magento/framework": "103.0.*", + "magento/module-backend": "102.0.*", + "magento/module-cron": "100.4.*", + "magento/module-store": "101.1.*" }, - "type": "magento2-module", - "license": [ - "OSL-3.0", - "AFL-3.0" - ], "autoload": { "files": [ "registration.php" @@ -25,3 +26,4 @@ } } } + diff --git a/app/code/Magento/Bundle/Test/Mftf/ActionGroup/StoreFrontAddProductToCartFromBundleActionGroup.xml b/app/code/Magento/Bundle/Test/Mftf/ActionGroup/StoreFrontAddProductToCartFromBundleActionGroup.xml index 7038ad90b81b9..45e26cf327dc9 100644 --- a/app/code/Magento/Bundle/Test/Mftf/ActionGroup/StoreFrontAddProductToCartFromBundleActionGroup.xml +++ b/app/code/Magento/Bundle/Test/Mftf/ActionGroup/StoreFrontAddProductToCartFromBundleActionGroup.xml @@ -17,11 +17,15 @@ + + + + diff --git a/app/code/Magento/Bundle/composer.json b/app/code/Magento/Bundle/composer.json index 47be75a42c254..782f460495f34 100644 --- a/app/code/Magento/Bundle/composer.json +++ b/app/code/Magento/Bundle/composer.json @@ -1,39 +1,40 @@ { "name": "magento/module-bundle", "description": "N/A", + "type": "magento2-module", + "license": [ + "OSL-3.0", + "AFL-3.0" + ], "config": { "sort-packages": true }, + "version": "101.0.4-p7", "require": { "php": "~7.4.0||~8.1.0", - "magento/framework": "*", - "magento/module-backend": "*", - "magento/module-catalog": "*", - "magento/module-catalog-inventory": "*", - "magento/module-catalog-rule": "*", - "magento/module-checkout": "*", - "magento/module-config": "*", - "magento/module-customer": "*", - "magento/module-eav": "*", - "magento/module-gift-message": "*", - "magento/module-media-storage": "*", - "magento/module-quote": "*", - "magento/module-sales": "*", - "magento/module-store": "*", - "magento/module-tax": "*", - "magento/module-ui": "*", - "magento/module-directory": "*" + "magento/framework": "103.0.*", + "magento/module-backend": "102.0.*", + "magento/module-catalog": "104.0.*", + "magento/module-catalog-inventory": "100.4.*", + "magento/module-catalog-rule": "101.2.*", + "magento/module-checkout": "100.4.*", + "magento/module-config": "101.2.*", + "magento/module-customer": "103.0.*", + "magento/module-eav": "102.1.*", + "magento/module-gift-message": "100.4.*", + "magento/module-media-storage": "100.4.*", + "magento/module-quote": "101.2.*", + "magento/module-sales": "103.0.*", + "magento/module-store": "101.1.*", + "magento/module-tax": "100.4.*", + "magento/module-ui": "101.2.*", + "magento/module-directory": "100.4.*" }, "suggest": { - "magento/module-webapi": "*", - "magento/module-bundle-sample-data": "*", - "magento/module-sales-rule": "*" + "magento/module-webapi": "100.4.*", + "magento/module-bundle-sample-data": "Sample Data version: 100.4.*", + "magento/module-sales-rule": "101.2.*" }, - "type": "magento2-module", - "license": [ - "OSL-3.0", - "AFL-3.0" - ], "autoload": { "files": [ "registration.php" @@ -43,3 +44,4 @@ } } } + diff --git a/app/code/Magento/Bundle/view/frontend/templates/catalog/product/view/type/bundle/option/radio.phtml b/app/code/Magento/Bundle/view/frontend/templates/catalog/product/view/type/bundle/option/radio.phtml index 706b28049470e..5f3e219866ba6 100644 --- a/app/code/Magento/Bundle/view/frontend/templates/catalog/product/view/type/bundle/option/radio.phtml +++ b/app/code/Magento/Bundle/view/frontend/templates/catalog/product/view/type/bundle/option/radio.phtml @@ -4,6 +4,8 @@ * See COPYING.txt for license details. */ use Magento\Bundle\ViewModel\ValidateQuantity; + +// phpcs:disable Generic.Files.LineLength.TooLong ?> getOption(); ?> @@ -20,42 +22,45 @@ $viewModel = $block->getData('validateQuantityViewModel');
- showSingle()) : ?> + showSingle()): ?> getSelectionTitlePrice($_selections[0]) ?> getTierPriceRenderer()->renderTierPrice($_selections[0]) ?> - - getRequired()) : ?> + + getRequired()): ?>
isSalable())?'':' checked="checked" ' ?> + isSalable())?'':' checked="checked" ' ?> value=""/>
- +
getRequired()) { echo 'data-validate="{\'validate-one-required-by-name\':true}"'; }?> + getRequired()) { + echo 'data-validate="{\'validate-one-required-by-name\':true}"'; + } ?> name="bundle_option[escapeHtmlAttr($_option->getId()) ?>]" data-selector="bundle_option[escapeHtmlAttr($_option->getId()) ?>]" - isSelected($_selection)) { echo ' checked="checked"'; } ?> - isSaleable()) { echo ' disabled="disabled"'; } ?> - value="escapeHtmlAttr($_selection->getSelectionId()) ?>"/> + isSelected($_selection)) { echo ' checked="checked"'; } ?> + isSaleable()) { echo ' disabled="disabled"'; } ?> + value="escapeHtmlAttr($_selection->getSelectionId()) ?>" + data-errors-message-box="#validation-message-box-radio"/>
+
- id="bundle-option-escapeHtmlAttr($_option->getId()) ?>-qty-input" - class="input-text qty" - type="number" - min="0" - data-validate="escapeHtmlAttr($viewModel->getQuantityValidators()) ?>" - name="bundle_option_qty[escapeHtmlAttr($_option->getId()) ?>]" - data-selector="bundle_option_qty[escapeHtmlAttr($_option->getId()) ?>]" - value="escapeHtmlAttr($_defaultQty) ?>"/> + id="bundle-option-escapeHtmlAttr($_option->getId()) ?>-qty-input" + class="input-text qty" + type="number" + min="0" + data-validate="escapeHtmlAttr($viewModel->getQuantityValidators()) ?>" + name="bundle_option_qty[escapeHtmlAttr($_option->getId()) ?>]" + data-selector="bundle_option_qty[escapeHtmlAttr($_option->getId()) ?>]" + value="escapeHtmlAttr($_defaultQty) ?>"/>
diff --git a/app/code/Magento/BundleGraphQl/composer.json b/app/code/Magento/BundleGraphQl/composer.json index 70a619cbf6837..62f1337085934 100644 --- a/app/code/Magento/BundleGraphQl/composer.json +++ b/app/code/Magento/BundleGraphQl/composer.json @@ -2,23 +2,24 @@ "name": "magento/module-bundle-graph-ql", "description": "N/A", "type": "magento2-module", - "require": { - "php": "~7.4.0||~8.1.0", - "magento/module-catalog": "*", - "magento/module-bundle": "*", - "magento/module-graph-ql": "*", - "magento/module-catalog-graph-ql": "*", - "magento/module-quote": "*", - "magento/module-quote-graph-ql": "*", - "magento/module-store": "*", - "magento/module-sales": "*", - "magento/module-sales-graph-ql": "*", - "magento/framework": "*" - }, "license": [ "OSL-3.0", "AFL-3.0" ], + "version": "100.4.4", + "require": { + "php": "~7.4.0||~8.1.0", + "magento/module-catalog": "104.0.*", + "magento/module-bundle": "101.0.*", + "magento/module-graph-ql": "100.4.*", + "magento/module-catalog-graph-ql": "100.4.*", + "magento/module-quote": "101.2.*", + "magento/module-quote-graph-ql": "100.4.*", + "magento/module-store": "101.1.*", + "magento/module-sales": "103.0.*", + "magento/module-sales-graph-ql": "100.4.*", + "magento/framework": "103.0.*" + }, "autoload": { "files": [ "registration.php" @@ -28,3 +29,4 @@ } } } + diff --git a/app/code/Magento/BundleImportExport/composer.json b/app/code/Magento/BundleImportExport/composer.json index ff7d0acc7c48d..1c5cf7e70ab12 100644 --- a/app/code/Magento/BundleImportExport/composer.json +++ b/app/code/Magento/BundleImportExport/composer.json @@ -1,24 +1,25 @@ { "name": "magento/module-bundle-import-export", "description": "N/A", + "type": "magento2-module", + "license": [ + "OSL-3.0", + "AFL-3.0" + ], "config": { "sort-packages": true }, + "version": "100.4.3", "require": { "php": "~7.4.0||~8.1.0", - "magento/framework": "*", - "magento/module-bundle": "*", - "magento/module-store": "*", - "magento/module-catalog": "*", - "magento/module-catalog-import-export": "*", - "magento/module-eav": "*", - "magento/module-import-export": "*" + "magento/framework": "103.0.*", + "magento/module-bundle": "101.0.*", + "magento/module-store": "101.1.*", + "magento/module-catalog": "104.0.*", + "magento/module-catalog-import-export": "101.1.*", + "magento/module-eav": "102.1.*", + "magento/module-import-export": "101.0.*" }, - "type": "magento2-module", - "license": [ - "OSL-3.0", - "AFL-3.0" - ], "autoload": { "files": [ "registration.php" @@ -28,3 +29,4 @@ } } } + diff --git a/app/code/Magento/CacheInvalidate/composer.json b/app/code/Magento/CacheInvalidate/composer.json index c756a5fe602e9..7985a50cb2e28 100644 --- a/app/code/Magento/CacheInvalidate/composer.json +++ b/app/code/Magento/CacheInvalidate/composer.json @@ -1,19 +1,20 @@ { "name": "magento/module-cache-invalidate", "description": "N/A", + "type": "magento2-module", + "license": [ + "OSL-3.0", + "AFL-3.0" + ], "config": { "sort-packages": true }, + "version": "100.4.2", "require": { "php": "~7.4.0||~8.1.0", - "magento/framework": "*", - "magento/module-page-cache": "*" + "magento/framework": "103.0.*", + "magento/module-page-cache": "100.4.*" }, - "type": "magento2-module", - "license": [ - "OSL-3.0", - "AFL-3.0" - ], "autoload": { "files": [ "registration.php" @@ -23,3 +24,4 @@ } } } + diff --git a/app/code/Magento/Captcha/Test/Mftf/Test/StorefrontCaptchaOnOnepageCheckoutPyamentTest.xml b/app/code/Magento/Captcha/Test/Mftf/Test/StorefrontCaptchaOnOnepageCheckoutPyamentTest.xml index 912e637dc534e..4ab4ec7f055f9 100644 --- a/app/code/Magento/Captcha/Test/Mftf/Test/StorefrontCaptchaOnOnepageCheckoutPyamentTest.xml +++ b/app/code/Magento/Captcha/Test/Mftf/Test/StorefrontCaptchaOnOnepageCheckoutPyamentTest.xml @@ -21,6 +21,7 @@ + 20 @@ -62,6 +63,7 @@ + diff --git a/app/code/Magento/Captcha/composer.json b/app/code/Magento/Captcha/composer.json index b276555326eb8..a90ec4cd5568f 100644 --- a/app/code/Magento/Captcha/composer.json +++ b/app/code/Magento/Captcha/composer.json @@ -1,27 +1,28 @@ { "name": "magento/module-captcha", "description": "N/A", + "type": "magento2-module", + "license": [ + "OSL-3.0", + "AFL-3.0" + ], "config": { "sort-packages": true }, + "version": "100.4.4-p4", "require": { "php": "~7.4.0||~8.1.0", - "magento/framework": "*", - "magento/module-backend": "*", - "magento/module-checkout": "*", - "magento/module-customer": "*", - "magento/module-sales": "*", - "magento/module-store": "*", - "magento/module-authorization": "*", + "magento/framework": "103.0.*", + "magento/module-backend": "102.0.*", + "magento/module-checkout": "100.4.*", + "magento/module-customer": "103.0.*", + "magento/module-sales": "103.0.*", + "magento/module-store": "101.1.*", + "magento/module-authorization": "100.4.*", "laminas/laminas-captcha": "^2.11.0", "laminas/laminas-db": "^2.13.4", "laminas/laminas-session": "^2.12.0" }, - "type": "magento2-module", - "license": [ - "OSL-3.0", - "AFL-3.0" - ], "autoload": { "files": [ "registration.php" @@ -31,3 +32,4 @@ } } } + diff --git a/app/code/Magento/CardinalCommerce/composer.json b/app/code/Magento/CardinalCommerce/composer.json index 4c49c92cec1ea..097783f77e433 100644 --- a/app/code/Magento/CardinalCommerce/composer.json +++ b/app/code/Magento/CardinalCommerce/composer.json @@ -1,21 +1,22 @@ { "name": "magento/module-cardinal-commerce", "description": "Provides a possibility to enable 3-D Secure 2.0 support for payment methods.", + "type": "magento2-module", + "license": [ + "OSL-3.0", + "AFL-3.0" + ], "config": { "sort-packages": true }, + "version": "100.4.2", "require": { "php": "~7.4.0||~8.1.0", - "magento/framework": "*", - "magento/module-checkout": "*", - "magento/module-payment": "*", - "magento/module-store": "*" + "magento/framework": "103.0.*", + "magento/module-checkout": "100.4.*", + "magento/module-payment": "100.4.*", + "magento/module-store": "101.1.*" }, - "type": "magento2-module", - "license": [ - "OSL-3.0", - "AFL-3.0" - ], "autoload": { "files": [ "registration.php" @@ -25,3 +26,4 @@ } } } + diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/NewAction.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/NewAction.php index 0b1ef98c386c4..ea14dbc1ce627 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/NewAction.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/NewAction.php @@ -4,18 +4,21 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Catalog\Controller\Adminhtml\Product; -use Magento\Framework\App\Action\HttpGetActionInterface as HttpGetActionInterface; -use Magento\Backend\App\Action; use Magento\Catalog\Controller\Adminhtml\Product; +use Magento\Framework\App\Action\HttpGetActionInterface as HttpGetActionInterface; use Magento\Framework\App\ObjectManager; +use Magento\Framework\RegexValidator; class NewAction extends \Magento\Catalog\Controller\Adminhtml\Product implements HttpGetActionInterface { /** * @var Initialization\StockDataFilter * @deprecated 101.0.0 + * @see Initialization\StockDataFilter */ protected $stockFilter; @@ -30,23 +33,32 @@ class NewAction extends \Magento\Catalog\Controller\Adminhtml\Product implements protected $resultForwardFactory; /** - * @param Action\Context $context + * @var RegexValidator + */ + private RegexValidator $regexValidator; + + /** + * @param Context $context * @param Builder $productBuilder * @param Initialization\StockDataFilter $stockFilter * @param \Magento\Framework\View\Result\PageFactory $resultPageFactory * @param \Magento\Backend\Model\View\Result\ForwardFactory $resultForwardFactory + * @param RegexValidator|null $regexValidator */ public function __construct( \Magento\Backend\App\Action\Context $context, Product\Builder $productBuilder, Initialization\StockDataFilter $stockFilter, \Magento\Framework\View\Result\PageFactory $resultPageFactory, - \Magento\Backend\Model\View\Result\ForwardFactory $resultForwardFactory + \Magento\Backend\Model\View\Result\ForwardFactory $resultForwardFactory, + RegexValidator $regexValidator = null ) { $this->stockFilter = $stockFilter; parent::__construct($context, $productBuilder); $this->resultPageFactory = $resultPageFactory; $this->resultForwardFactory = $resultForwardFactory; + $this->regexValidator = $regexValidator + ?: ObjectManager::getInstance()->get(RegexValidator::class); } /** @@ -56,6 +68,11 @@ public function __construct( */ public function execute() { + $typeId = $this->getRequest()->getParam('type'); + if (!$this->regexValidator->validateParamRegex($typeId)) { + return $this->resultForwardFactory->create()->forward('noroute'); + } + if (!$this->getRequest()->getParam('set')) { return $this->resultForwardFactory->create()->forward('noroute'); } diff --git a/app/code/Magento/Catalog/Model/Product/Option/Type/File/ValidatorInfo.php b/app/code/Magento/Catalog/Model/Product/Option/Type/File/ValidatorInfo.php index 4c4daccb90174..8afbdf7425ca2 100644 --- a/app/code/Magento/Catalog/Model/Product/Option/Type/File/ValidatorInfo.php +++ b/app/code/Magento/Catalog/Model/Product/Option/Type/File/ValidatorInfo.php @@ -49,6 +49,7 @@ class ValidatorInfo extends Validator * @var IoFile */ private $ioFile; + /** * @var NotProtectedExtension */ @@ -147,12 +148,14 @@ private function validatePath(array $optionValuePath): bool { foreach ([$optionValuePath['quote_path'], $optionValuePath['order_path']] as $path) { $pathInfo = $this->ioFile->getPathInfo($path); - if (isset($pathInfo['extension'])) { - if (!$this->fileValidator->isValid($pathInfo['extension'])) { - return false; - } + + if (isset($pathInfo['extension']) + && (empty($pathInfo['extension']) || !$this->fileValidator->isValid($pathInfo['extension'])) + ) { + return false; } } + return true; } diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryMainSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryMainSection.xml index f3a0919f6c728..cf419c8f27c00 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryMainSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryMainSection.xml @@ -40,5 +40,6 @@ + diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminApplyTierPriceToProductTest/AdminApplyTierPriceToProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminApplyTierPriceToProductTest/AdminApplyTierPriceToProductTest.xml index 8add42ec7493f..b2d36589dd251 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminApplyTierPriceToProductTest/AdminApplyTierPriceToProductTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminApplyTierPriceToProductTest/AdminApplyTierPriceToProductTest.xml @@ -230,6 +230,7 @@ + diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassProductAttributeUpdateAddedToQueueTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassProductAttributeUpdateAddedToQueueTest.xml index c887ed5b42816..1d75f49ab6740 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassProductAttributeUpdateAddedToQueueTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassProductAttributeUpdateAddedToQueueTest.xml @@ -46,8 +46,10 @@ + + diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminRestrictedUserAddCategoryFromProductPageTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminRestrictedUserAddCategoryFromProductPageTest.xml index 795c2ac77acdd..fb4a4a2bfac8d 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminRestrictedUserAddCategoryFromProductPageTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminRestrictedUserAddCategoryFromProductPageTest.xml @@ -97,6 +97,7 @@ + diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockEnabledFlatCatalogTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockEnabledFlatCatalogTest.xml index 3af6de07e561d..71c82f654310a 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockEnabledFlatCatalogTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockEnabledFlatCatalogTest.xml @@ -6,7 +6,7 @@ */ --> - @@ -26,6 +26,9 @@ + + + @@ -33,6 +36,7 @@ + diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CAdminTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CAdminTest.xml index 3cb36ae2c3735..33e34018b5f4e 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CAdminTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CAdminTest.xml @@ -21,8 +21,12 @@ + + + + diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductWithEmptyAttributeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductWithEmptyAttributeTest.xml index 203fa753d10fb..5376441021d6f 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductWithEmptyAttributeTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductWithEmptyAttributeTest.xml @@ -26,22 +26,24 @@ - + - + + + - + - + @@ -49,6 +51,6 @@ - + diff --git a/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/NewActionTest.php b/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/NewActionTest.php old mode 100644 new mode 100755 index 974c85b2b5c98..b1b649bf551c6 --- a/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/NewActionTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/NewActionTest.php @@ -16,6 +16,9 @@ use Magento\Catalog\Controller\Adminhtml\Product\NewAction; use Magento\Catalog\Model\Product; use Magento\Catalog\Test\Unit\Controller\Adminhtml\ProductTest; +use Magento\Framework\RegexValidator; +use Magento\Framework\Validator\Regex; +use Magento\Framework\Validator\RegexFactory; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; use Magento\Framework\View\Result\PageFactory; use PHPUnit\Framework\MockObject\MockObject; @@ -42,6 +45,26 @@ class NewActionTest extends ProductTest */ protected $initializationHelper; + /** + * @var RegexValidator|MockObject + */ + private $regexValidator; + + /** + * @var RegexFactory + */ + private $regexValidatorFactoryMock; + + /** + * @var Regex|MockObject + */ + private $regexValidatorMock; + + /** + * @var ForwardFactory&MockObject|MockObject + */ + private $resultForwardFactory; + protected function setUp(): void { $this->productBuilder = $this->createPartialMock( @@ -63,37 +86,69 @@ protected function setUp(): void ->disableOriginalConstructor() ->setMethods(['create']) ->getMock(); - $resultPageFactory->expects($this->atLeastOnce()) - ->method('create') - ->willReturn($this->resultPage); $this->resultForward = $this->getMockBuilder(Forward::class) ->disableOriginalConstructor() ->getMock(); - $resultForwardFactory = $this->getMockBuilder(ForwardFactory::class) + $this->resultForwardFactory = $this->getMockBuilder(ForwardFactory::class) + ->disableOriginalConstructor() + ->onlyMethods(['create']) + ->getMock(); + + $this->regexValidatorFactoryMock = $this->getMockBuilder(RegexFactory::class) ->disableOriginalConstructor() ->setMethods(['create']) ->getMock(); - $resultForwardFactory->expects($this->any()) - ->method('create') - ->willReturn($this->resultForward); + $this->regexValidatorMock = $this->createMock(Regex::class); + $this->regexValidatorFactoryMock->method('create') + ->willReturn($this->regexValidatorMock); + $this->regexValidator = new regexValidator($this->regexValidatorFactoryMock); $this->action = (new ObjectManager($this))->getObject( NewAction::class, [ 'context' => $this->initContext(), 'productBuilder' => $this->productBuilder, 'resultPageFactory' => $resultPageFactory, - 'resultForwardFactory' => $resultForwardFactory, + 'resultForwardFactory' => $this->resultForwardFactory, + 'regexValidator' => $this->regexValidator, ] ); } - public function testExecute() + /** + * @return void + */ + public function testExecute(): void { - $this->action->getRequest()->expects($this->any())->method('getParam')->willReturn(true); - $this->action->getRequest()->expects($this->any())->method('getFullActionName') - ->willReturn('catalog_product_new'); - $this->action->execute(); + $value = 'catalog_product_new'; + + $this->action->getRequest()->expects($this->any())->method('getParam')->willReturn($value); + $this->regexValidatorMock->expects($this->any()) + ->method('isValid') + ->with($value) + ->willReturn(true); + + $this->assertEquals(true, $this->regexValidator->validateParamRegex($value)); + } + + /** + * @return void + */ + public function testExecuteWithError(): void + { + $value = 'simple\' and true()]|*[self%3a%3ahandle%20or%20self%3a%3alayout'; + + $this->action->getRequest()->expects($this->any()) + ->method('getParam') + ->willReturn($value); + $this->resultForwardFactory->expects($this->any()) + ->method('create') + ->willReturn($this->resultForward); + $this->resultForward->expects($this->once()) + ->method('forward') + ->with('noroute') + ->willReturn(true); + $this->assertTrue($this->action->execute()); } } diff --git a/app/code/Magento/Catalog/Test/Unit/Pricing/Render/FinalPriceBoxTest.php b/app/code/Magento/Catalog/Test/Unit/Pricing/Render/FinalPriceBoxTest.php index f3831e50ef3d9..a97c75d32da15 100644 --- a/app/code/Magento/Catalog/Test/Unit/Pricing/Render/FinalPriceBoxTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Pricing/Render/FinalPriceBoxTest.php @@ -15,8 +15,11 @@ use Magento\Catalog\Pricing\Render\FinalPriceBox; use Magento\Framework\App\Cache\StateInterface; use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\App\DeploymentConfig; use Magento\Framework\App\State; +use Magento\Framework\Config\ConfigOptionsListConstants; use Magento\Framework\Event\Test\Unit\ManagerStub; +use Magento\Framework\ObjectManagerInterface; use Magento\Framework\Pricing\Amount\AmountInterface; use Magento\Framework\Pricing\Price\PriceInterface; use Magento\Framework\Pricing\PriceInfoInterface; @@ -96,11 +99,27 @@ class FinalPriceBoxTest extends TestCase */ private $minimalPriceCalculator; + /** + * @var DeploymentConfig|MockObject + */ + private $deploymentConfig; + + /** + * @var ObjectManagerInterface|MockObject + */ + private $objectManagerMock; + /** * @inheritDoc + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ protected function setUp(): void { + $this->objectManagerMock = $this->getMockBuilder(ObjectManagerInterface::class) + ->disableOriginalConstructor() + ->onlyMethods(['get']) + ->getMockForAbstractClass(); + \Magento\Framework\App\ObjectManager::setInstance($this->objectManagerMock); $this->product = $this->getMockBuilder(Product::class) ->addMethods(['getCanShowPrice']) ->onlyMethods(['getPriceInfo', 'isSalable', 'getId']) @@ -183,6 +202,11 @@ protected function setUp(): void ->disableOriginalConstructor() ->getMockForAbstractClass(); + $this->deploymentConfig = $this->createPartialMock( + DeploymentConfig::class, + ['get'] + ); + $this->minimalPriceCalculator = $this->getMockForAbstractClass(MinimalPriceCalculatorInterface::class); $this->object = $objectManager->getObject( FinalPriceBox::class, @@ -455,6 +479,15 @@ public function testHidePrice(): void */ public function testGetCacheKey(): void { + $this->objectManagerMock->expects($this->any()) + ->method('get') + ->with(DeploymentConfig::class) + ->willReturn($this->deploymentConfig); + + $this->deploymentConfig->expects($this->any()) + ->method('get') + ->with(ConfigOptionsListConstants::CONFIG_PATH_CRYPT_KEY) + ->willReturn('448198e08af35844a42d3c93c1ef4e03'); $result = $this->object->getCacheKey(); $this->assertStringEndsWith('list-category-page', $result); } diff --git a/app/code/Magento/Catalog/composer.json b/app/code/Magento/Catalog/composer.json index 6597e88e9d995..bd56668a1a1d7 100644 --- a/app/code/Magento/Catalog/composer.json +++ b/app/code/Magento/Catalog/composer.json @@ -1,48 +1,49 @@ { "name": "magento/module-catalog", "description": "N/A", + "type": "magento2-module", + "license": [ + "OSL-3.0", + "AFL-3.0" + ], "config": { "sort-packages": true }, + "version": "104.0.4-p10", "require": { "php": "~7.4.0||~8.1.0", - "magento/framework": "*", - "magento/module-authorization": "*", - "magento/module-asynchronous-operations": "*", - "magento/module-backend": "*", - "magento/module-catalog-inventory": "*", - "magento/module-catalog-rule": "*", - "magento/module-catalog-url-rewrite": "*", - "magento/module-checkout": "*", - "magento/module-cms": "*", - "magento/module-config": "*", - "magento/module-customer": "*", - "magento/module-directory": "*", - "magento/module-eav": "*", - "magento/module-indexer": "*", - "magento/module-media-storage": "*", - "magento/module-msrp": "*", - "magento/module-page-cache": "*", - "magento/module-product-alert": "*", - "magento/module-quote": "*", - "magento/module-store": "*", - "magento/module-tax": "*", - "magento/module-theme": "*", - "magento/module-ui": "*", - "magento/module-url-rewrite": "*", - "magento/module-widget": "*", - "magento/module-wishlist": "*" + "magento/framework": "103.0.*", + "magento/module-authorization": "100.4.*", + "magento/module-asynchronous-operations": "100.4.*", + "magento/module-backend": "102.0.*", + "magento/module-catalog-inventory": "100.4.*", + "magento/module-catalog-rule": "101.2.*", + "magento/module-catalog-url-rewrite": "100.4.*", + "magento/module-checkout": "100.4.*", + "magento/module-cms": "104.0.*", + "magento/module-config": "101.2.*", + "magento/module-customer": "103.0.*", + "magento/module-directory": "100.4.*", + "magento/module-eav": "102.1.*", + "magento/module-indexer": "100.4.*", + "magento/module-media-storage": "100.4.*", + "magento/module-msrp": "100.4.*", + "magento/module-page-cache": "100.4.*", + "magento/module-product-alert": "100.4.*", + "magento/module-quote": "101.2.*", + "magento/module-store": "101.1.*", + "magento/module-tax": "100.4.*", + "magento/module-theme": "101.1.*", + "magento/module-ui": "101.2.*", + "magento/module-url-rewrite": "102.0.*", + "magento/module-widget": "101.2.*", + "magento/module-wishlist": "101.2.*" }, "suggest": { - "magento/module-cookie": "*", - "magento/module-sales": "*", - "magento/module-catalog-sample-data": "*" + "magento/module-cookie": "100.4.*", + "magento/module-sales": "103.0.*", + "magento/module-catalog-sample-data": "Sample Data version: 100.4.*" }, - "type": "magento2-module", - "license": [ - "OSL-3.0", - "AFL-3.0" - ], "autoload": { "files": [ "registration.php" @@ -52,3 +53,4 @@ } } } + diff --git a/app/code/Magento/Catalog/i18n/en_US.csv b/app/code/Magento/Catalog/i18n/en_US.csv index d5ba3b75493eb..34d31f1c9a6fa 100644 --- a/app/code/Magento/Catalog/i18n/en_US.csv +++ b/app/code/Magento/Catalog/i18n/en_US.csv @@ -816,4 +816,6 @@ Details,Details "Are you sure you want to delete this category?","Are you sure you want to delete this category?" "Attribute Set Information","Attribute Set Information" "Failed to retrieve product links for ""%1""","Failed to retrieve product links for ""%1""" +"The url has invalid characters. Please correct and try again.","The url has invalid characters. Please correct and try again." + diff --git a/app/code/Magento/Catalog/view/adminhtml/web/js/category-tree.js b/app/code/Magento/Catalog/view/adminhtml/web/js/category-tree.js index d292bd126593c..b4d4ed12d20ba 100644 --- a/app/code/Magento/Catalog/view/adminhtml/web/js/category-tree.js +++ b/app/code/Magento/Catalog/view/adminhtml/web/js/category-tree.js @@ -5,10 +5,9 @@ define([ 'jquery', - 'mageUtils', 'jquery/ui', 'jquery/jstree/jquery.jstree' -], function ($, utils) { +], function ($) { 'use strict'; $.widget('mage.categoryTree', { @@ -87,7 +86,7 @@ define([ // jscs:disable requireCamelCaseOrUpperCaseIdentifiers result = { id: node.id, - text: utils.unescape(node.name) + ' (' + node.product_count + ')', + text: node.name + ' (' + node.product_count + ')', li_attr: { class: node.cls + (!!node.disabled ? ' disabled' : '') //eslint-disable-line no-extra-boolean-cast }, diff --git a/app/code/Magento/CatalogAnalytics/composer.json b/app/code/Magento/CatalogAnalytics/composer.json index a41a47fa4764b..2fb9f4fb021cf 100644 --- a/app/code/Magento/CatalogAnalytics/composer.json +++ b/app/code/Magento/CatalogAnalytics/composer.json @@ -1,17 +1,18 @@ { "name": "magento/module-catalog-analytics", "description": "N/A", - "require": { - "php": "~7.4.0||~8.1.0", - "magento/framework": "*", - "magento/module-catalog": "*", - "magento/module-analytics": "*" - }, "type": "magento2-module", "license": [ "OSL-3.0", "AFL-3.0" ], + "version": "100.4.2", + "require": { + "php": "~7.4.0||~8.1.0", + "magento/framework": "103.0.*", + "magento/module-catalog": "104.0.*", + "magento/module-analytics": "100.4.*" + }, "autoload": { "files": [ "registration.php" @@ -21,3 +22,4 @@ } } } + diff --git a/app/code/Magento/CatalogCmsGraphQl/composer.json b/app/code/Magento/CatalogCmsGraphQl/composer.json index cf9e76f3b2ea2..b32b4635745c3 100644 --- a/app/code/Magento/CatalogCmsGraphQl/composer.json +++ b/app/code/Magento/CatalogCmsGraphQl/composer.json @@ -2,21 +2,22 @@ "name": "magento/module-catalog-cms-graph-ql", "description": "N/A", "type": "magento2-module", + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "version": "100.4.1", "require": { "php": "~7.4.0||~8.1.0", - "magento/framework": "*", - "magento/module-catalog": "*", - "magento/module-cms-graph-ql": "*" + "magento/framework": "103.0.*", + "magento/module-catalog": "104.0.*", + "magento/module-cms-graph-ql": "100.4.*" }, "suggest": { - "magento/module-graph-ql": "*", - "magento/module-cms": "*", - "magento/module-catalog-graph-ql": "*" + "magento/module-graph-ql": "100.4.*", + "magento/module-cms": "104.0.*", + "magento/module-catalog-graph-ql": "100.4.*" }, - "license": [ - "OSL-3.0", - "AFL-3.0" - ], "autoload": { "files": [ "registration.php" @@ -26,3 +27,4 @@ } } } + diff --git a/app/code/Magento/CatalogCustomerGraphQl/composer.json b/app/code/Magento/CatalogCustomerGraphQl/composer.json index b1743ae964966..c7062a2d03e6a 100644 --- a/app/code/Magento/CatalogCustomerGraphQl/composer.json +++ b/app/code/Magento/CatalogCustomerGraphQl/composer.json @@ -2,17 +2,18 @@ "name": "magento/module-catalog-customer-graph-ql", "description": "N/A", "type": "magento2-module", - "require": { - "php": "~7.4.0||~8.1.0", - "magento/framework": "*", - "magento/module-catalog": "*", - "magento/module-customer": "*", - "magento/module-catalog-graph-ql": "*" - }, "license": [ "OSL-3.0", "AFL-3.0" ], + "version": "100.4.4", + "require": { + "php": "~7.4.0||~8.1.0", + "magento/framework": "103.0.*", + "magento/module-catalog": "104.0.*", + "magento/module-customer": "103.0.*", + "magento/module-catalog-graph-ql": "100.4.*" + }, "autoload": { "files": [ "registration.php" @@ -22,3 +23,4 @@ } } } + diff --git a/app/code/Magento/CatalogGraphQl/composer.json b/app/code/Magento/CatalogGraphQl/composer.json index c289f84a359ba..bd3626743f4de 100644 --- a/app/code/Magento/CatalogGraphQl/composer.json +++ b/app/code/Magento/CatalogGraphQl/composer.json @@ -2,28 +2,29 @@ "name": "magento/module-catalog-graph-ql", "description": "N/A", "type": "magento2-module", + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "version": "100.4.4", "require": { "php": "~7.4.0||~8.1.0", - "magento/module-eav": "*", - "magento/module-catalog": "*", - "magento/module-catalog-inventory": "*", - "magento/module-directory": "*", - "magento/module-search": "*", - "magento/module-store": "*", - "magento/module-eav-graph-ql": "*", - "magento/module-catalog-search": "*", - "magento/framework": "*", - "magento/module-graph-ql": "*", - "magento/module-advanced-search": "*" + "magento/module-eav": "102.1.*", + "magento/module-catalog": "104.0.*", + "magento/module-catalog-inventory": "100.4.*", + "magento/module-directory": "100.4.*", + "magento/module-search": "101.1.*", + "magento/module-store": "101.1.*", + "magento/module-eav-graph-ql": "100.4.*", + "magento/module-catalog-search": "102.0.*", + "magento/framework": "103.0.*", + "magento/module-graph-ql": "100.4.*", + "magento/module-advanced-search": "100.4.*" }, "suggest": { - "magento/module-graph-ql-cache": "*", - "magento/module-store-graph-ql": "*" + "magento/module-graph-ql-cache": "100.4.*", + "magento/module-store-graph-ql": "100.4.*" }, - "license": [ - "OSL-3.0", - "AFL-3.0" - ], "autoload": { "files": [ "registration.php" @@ -33,3 +34,4 @@ } } } + diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product.php b/app/code/Magento/CatalogImportExport/Model/Import/Product.php index 25dc5360c8df2..3ad96737bf323 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product.php @@ -47,6 +47,7 @@ */ class Product extends AbstractEntity { + private const COL_NAME_FORMAT = '/[\x00-\x1F\x7F]/'; private const DEFAULT_GLOBAL_MULTIPLE_VALUE_SEPARATOR = ','; public const CONFIG_KEY_PRODUCT_TYPES = 'global/importexport/import_product_types'; private const HASH_ALGORITHM = 'sha256'; @@ -1611,6 +1612,11 @@ protected function _saveProducts() $bunch[$rowNum][self::URL_KEY] = $rowData[self::URL_KEY] = $urlKey; } + if (!empty($rowData[self::COL_NAME])) { + // remove null byte character + $rowData[self::COL_NAME] = preg_replace(self::COL_NAME_FORMAT, '', $rowData[self::COL_NAME]); + } + $rowSku = $rowData[self::COL_SKU]; $rowSkuNormalized = mb_strtolower($rowSku); diff --git a/app/code/Magento/CatalogImportExport/composer.json b/app/code/Magento/CatalogImportExport/composer.json index dac8624086df0..817c7e65cb94e 100644 --- a/app/code/Magento/CatalogImportExport/composer.json +++ b/app/code/Magento/CatalogImportExport/composer.json @@ -1,29 +1,30 @@ { "name": "magento/module-catalog-import-export", "description": "N/A", + "type": "magento2-module", + "license": [ + "OSL-3.0", + "AFL-3.0" + ], "config": { "sort-packages": true }, + "version": "101.1.4-p4", "require": { "php": "~7.4.0||~8.1.0", "ext-ctype": "*", - "magento/framework": "*", - "magento/module-catalog": "*", - "magento/module-catalog-inventory": "*", - "magento/module-catalog-url-rewrite": "*", - "magento/module-customer": "*", - "magento/module-eav": "*", - "magento/module-import-export": "*", - "magento/module-media-storage": "*", - "magento/module-store": "*", - "magento/module-tax": "*", - "magento/module-authorization": "*" + "magento/framework": "103.0.*", + "magento/module-catalog": "104.0.*", + "magento/module-catalog-inventory": "100.4.*", + "magento/module-catalog-url-rewrite": "100.4.*", + "magento/module-customer": "103.0.*", + "magento/module-eav": "102.1.*", + "magento/module-import-export": "101.0.*", + "magento/module-media-storage": "100.4.*", + "magento/module-store": "101.1.*", + "magento/module-tax": "100.4.*", + "magento/module-authorization": "100.4.*" }, - "type": "magento2-module", - "license": [ - "OSL-3.0", - "AFL-3.0" - ], "autoload": { "files": [ "registration.php" @@ -33,3 +34,4 @@ } } } + diff --git a/app/code/Magento/CatalogInventory/composer.json b/app/code/Magento/CatalogInventory/composer.json index 4b7fee72c4347..18c51001c091f 100644 --- a/app/code/Magento/CatalogInventory/composer.json +++ b/app/code/Magento/CatalogInventory/composer.json @@ -1,25 +1,26 @@ { "name": "magento/module-catalog-inventory", "description": "N/A", + "type": "magento2-module", + "license": [ + "OSL-3.0", + "AFL-3.0" + ], "config": { "sort-packages": true }, + "version": "100.4.4", "require": { "php": "~7.4.0||~8.1.0", - "magento/framework": "*", - "magento/module-catalog": "*", - "magento/module-config": "*", - "magento/module-customer": "*", - "magento/module-eav": "*", - "magento/module-quote": "*", - "magento/module-store": "*", - "magento/module-ui": "*" + "magento/framework": "103.0.*", + "magento/module-catalog": "104.0.*", + "magento/module-config": "101.2.*", + "magento/module-customer": "103.0.*", + "magento/module-eav": "102.1.*", + "magento/module-quote": "101.2.*", + "magento/module-store": "101.1.*", + "magento/module-ui": "101.2.*" }, - "type": "magento2-module", - "license": [ - "OSL-3.0", - "AFL-3.0" - ], "autoload": { "files": [ "registration.php" @@ -30,3 +31,4 @@ }, "abandoned": "magento/inventory-composer-metapackage" } + diff --git a/app/code/Magento/CatalogInventoryGraphQl/composer.json b/app/code/Magento/CatalogInventoryGraphQl/composer.json index 38685524d5346..92f6224abc5a8 100644 --- a/app/code/Magento/CatalogInventoryGraphQl/composer.json +++ b/app/code/Magento/CatalogInventoryGraphQl/composer.json @@ -2,18 +2,19 @@ "name": "magento/module-catalog-inventory-graph-ql", "description": "N/A", "type": "magento2-module", - "require": { - "php": "~7.4.0||~8.1.0", - "magento/framework": "*", - "magento/module-store": "*", - "magento/module-catalog": "*", - "magento/module-catalog-inventory": "*", - "magento/module-graph-ql": "*" - }, "license": [ "OSL-3.0", "AFL-3.0" ], + "version": "100.4.1", + "require": { + "php": "~7.4.0||~8.1.0", + "magento/framework": "103.0.*", + "magento/module-store": "101.1.*", + "magento/module-catalog": "104.0.*", + "magento/module-catalog-inventory": "100.4.*", + "magento/module-graph-ql": "100.4.*" + }, "autoload": { "files": [ "registration.php" @@ -23,3 +24,4 @@ } } } + diff --git a/app/code/Magento/CatalogRule/composer.json b/app/code/Magento/CatalogRule/composer.json index 531a12ac017ed..43a970bc52a60 100644 --- a/app/code/Magento/CatalogRule/composer.json +++ b/app/code/Magento/CatalogRule/composer.json @@ -1,29 +1,30 @@ { "name": "magento/module-catalog-rule", "description": "N/A", + "type": "magento2-module", + "license": [ + "OSL-3.0", + "AFL-3.0" + ], "config": { "sort-packages": true }, + "version": "101.2.4", "require": { "php": "~7.4.0||~8.1.0", - "magento/framework": "*", - "magento/module-backend": "*", - "magento/module-catalog": "*", - "magento/module-customer": "*", - "magento/module-eav": "*", - "magento/module-rule": "*", - "magento/module-store": "*", - "magento/module-ui": "*" + "magento/framework": "103.0.*", + "magento/module-backend": "102.0.*", + "magento/module-catalog": "104.0.*", + "magento/module-customer": "103.0.*", + "magento/module-eav": "102.1.*", + "magento/module-rule": "100.4.*", + "magento/module-store": "101.1.*", + "magento/module-ui": "101.2.*" }, "suggest": { - "magento/module-import-export": "*", - "magento/module-catalog-rule-sample-data": "*" + "magento/module-import-export": "101.0.*", + "magento/module-catalog-rule-sample-data": "Sample Data version: 100.4.*" }, - "type": "magento2-module", - "license": [ - "OSL-3.0", - "AFL-3.0" - ], "autoload": { "files": [ "registration.php" @@ -33,3 +34,4 @@ } } } + diff --git a/app/code/Magento/CatalogRuleConfigurable/composer.json b/app/code/Magento/CatalogRuleConfigurable/composer.json index 68da972ae94f9..46a0b604835ad 100644 --- a/app/code/Magento/CatalogRuleConfigurable/composer.json +++ b/app/code/Magento/CatalogRuleConfigurable/composer.json @@ -1,25 +1,26 @@ { "name": "magento/module-catalog-rule-configurable", "description": "N/A", + "type": "magento2-module", + "license": [ + "OSL-3.0", + "AFL-3.0" + ], "config": { "sort-packages": true }, + "version": "100.4.3", "require": { "php": "~7.4.0||~8.1.0", - "magento/framework": "*", + "magento/framework": "103.0.*", "magento/magento-composer-installer": "*", - "magento/module-catalog": "*", - "magento/module-catalog-rule": "*", - "magento/module-configurable-product": "*" + "magento/module-catalog": "104.0.*", + "magento/module-catalog-rule": "101.2.*", + "magento/module-configurable-product": "100.4.*" }, "suggest": { - "magento/module-catalog-rule": "*" + "magento/module-catalog-rule": "101.2.*" }, - "type": "magento2-module", - "license": [ - "OSL-3.0", - "AFL-3.0" - ], "autoload": { "files": [ "registration.php" @@ -29,3 +30,4 @@ } } } + diff --git a/app/code/Magento/CatalogRuleGraphQl/composer.json b/app/code/Magento/CatalogRuleGraphQl/composer.json index 2c8c3ef20c96a..6bd7ef191d895 100644 --- a/app/code/Magento/CatalogRuleGraphQl/composer.json +++ b/app/code/Magento/CatalogRuleGraphQl/composer.json @@ -2,17 +2,18 @@ "name": "magento/module-catalog-rule-graph-ql", "description": "N/A", "type": "magento2-module", + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "version": "100.4.2", "require": { "php": "~7.4.0||~8.1.0", - "magento/framework": "*" + "magento/framework": "103.0.*" }, "suggest": { - "magento/module-catalog-rule": "*" + "magento/module-catalog-rule": "101.2.*" }, - "license": [ - "OSL-3.0", - "AFL-3.0" - ], "autoload": { "files": [ "registration.php" @@ -22,3 +23,4 @@ } } } + diff --git a/app/code/Magento/CatalogSearch/composer.json b/app/code/Magento/CatalogSearch/composer.json index 465d7daeebe18..bf04cbc55237e 100644 --- a/app/code/Magento/CatalogSearch/composer.json +++ b/app/code/Magento/CatalogSearch/composer.json @@ -1,32 +1,33 @@ { "name": "magento/module-catalog-search", "description": "Catalog search", + "type": "magento2-module", + "license": [ + "OSL-3.0", + "AFL-3.0" + ], "config": { "sort-packages": true }, + "version": "102.0.4-p11", "require": { "php": "~7.4.0||~8.1.0", - "magento/framework": "*", - "magento/module-backend": "*", - "magento/module-catalog": "*", - "magento/module-indexer": "*", - "magento/module-catalog-inventory": "*", - "magento/module-customer": "*", - "magento/module-directory": "*", - "magento/module-eav": "*", - "magento/module-search": "*", - "magento/module-store": "*", - "magento/module-theme": "*", - "magento/module-ui": "*" + "magento/framework": "103.0.*", + "magento/module-backend": "102.0.*", + "magento/module-catalog": "104.0.*", + "magento/module-indexer": "100.4.*", + "magento/module-catalog-inventory": "100.4.*", + "magento/module-customer": "103.0.*", + "magento/module-directory": "100.4.*", + "magento/module-eav": "102.1.*", + "magento/module-search": "101.1.*", + "magento/module-store": "101.1.*", + "magento/module-theme": "101.1.*", + "magento/module-ui": "101.2.*" }, "suggest": { - "magento/module-config": "*" + "magento/module-config": "101.2.*" }, - "type": "magento2-module", - "license": [ - "OSL-3.0", - "AFL-3.0" - ], "autoload": { "files": [ "registration.php" @@ -36,3 +37,4 @@ } } } + diff --git a/app/code/Magento/CatalogSearch/etc/acl.xml b/app/code/Magento/CatalogSearch/etc/acl.xml index dd82addc2e304..e30b8ec8a4b97 100644 --- a/app/code/Magento/CatalogSearch/etc/acl.xml +++ b/app/code/Magento/CatalogSearch/etc/acl.xml @@ -13,7 +13,7 @@ - + @@ -21,4 +21,4 @@ - \ No newline at end of file + diff --git a/app/code/Magento/CatalogUrlRewrite/composer.json b/app/code/Magento/CatalogUrlRewrite/composer.json index ce409e2186faa..490ad57b35a6f 100644 --- a/app/code/Magento/CatalogUrlRewrite/composer.json +++ b/app/code/Magento/CatalogUrlRewrite/composer.json @@ -1,29 +1,30 @@ { "name": "magento/module-catalog-url-rewrite", "description": "N/A", + "type": "magento2-module", + "license": [ + "OSL-3.0", + "AFL-3.0" + ], "config": { "sort-packages": true }, + "version": "100.4.4", "require": { "php": "~7.4.0||~8.1.0", - "magento/framework": "*", - "magento/module-backend": "*", - "magento/module-catalog": "*", - "magento/module-catalog-import-export": "*", - "magento/module-eav": "*", - "magento/module-import-export": "*", - "magento/module-store": "*", - "magento/module-ui": "*", - "magento/module-url-rewrite": "*" + "magento/framework": "103.0.*", + "magento/module-backend": "102.0.*", + "magento/module-catalog": "104.0.*", + "magento/module-catalog-import-export": "101.1.*", + "magento/module-eav": "102.1.*", + "magento/module-import-export": "101.0.*", + "magento/module-store": "101.1.*", + "magento/module-ui": "101.2.*", + "magento/module-url-rewrite": "102.0.*" }, "suggest": { - "magento/module-webapi": "*" + "magento/module-webapi": "100.4.*" }, - "type": "magento2-module", - "license": [ - "OSL-3.0", - "AFL-3.0" - ], "autoload": { "files": [ "registration.php" @@ -33,3 +34,4 @@ } } } + diff --git a/app/code/Magento/CatalogUrlRewriteGraphQl/composer.json b/app/code/Magento/CatalogUrlRewriteGraphQl/composer.json index 025234af6f865..d79c2ab17f611 100644 --- a/app/code/Magento/CatalogUrlRewriteGraphQl/composer.json +++ b/app/code/Magento/CatalogUrlRewriteGraphQl/composer.json @@ -2,23 +2,24 @@ "name": "magento/module-catalog-url-rewrite-graph-ql", "description": "N/A", "type": "magento2-module", + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "version": "100.4.3", "require": { "php": "~7.4.0||~8.1.0", - "magento/module-store": "*", - "magento/module-catalog": "*", - "magento/module-catalog-graph-ql": "*", - "magento/module-url-rewrite-graph-ql": "*", - "magento/framework": "*" + "magento/module-store": "101.1.*", + "magento/module-catalog": "104.0.*", + "magento/module-catalog-graph-ql": "100.4.*", + "magento/module-url-rewrite-graph-ql": "100.4.*", + "magento/framework": "103.0.*" }, "suggest": { - "magento/module-catalog-url-rewrite": "*", - "magento/module-catalog-graph-ql": "*", - "magento/module-url-rewrite-graph-ql": "*" + "magento/module-catalog-url-rewrite": "100.4.*", + "magento/module-catalog-graph-ql": "100.4.*", + "magento/module-url-rewrite-graph-ql": "100.4.*" }, - "license": [ - "OSL-3.0", - "AFL-3.0" - ], "autoload": { "files": [ "registration.php" @@ -28,3 +29,4 @@ } } } + diff --git a/app/code/Magento/CatalogWidget/Test/Mftf/Test/StorefrontProductGridUIUpdatesOnDesktopTest.xml b/app/code/Magento/CatalogWidget/Test/Mftf/Test/StorefrontProductGridUIUpdatesOnDesktopTest.xml index 5590aa1cdcefa..e833c8e72f47a 100644 --- a/app/code/Magento/CatalogWidget/Test/Mftf/Test/StorefrontProductGridUIUpdatesOnDesktopTest.xml +++ b/app/code/Magento/CatalogWidget/Test/Mftf/Test/StorefrontProductGridUIUpdatesOnDesktopTest.xml @@ -61,6 +61,7 @@ + diff --git a/app/code/Magento/CatalogWidget/composer.json b/app/code/Magento/CatalogWidget/composer.json index 33c5e3b3ba3ee..bb74be273a5e5 100644 --- a/app/code/Magento/CatalogWidget/composer.json +++ b/app/code/Magento/CatalogWidget/composer.json @@ -1,27 +1,28 @@ { "name": "magento/module-catalog-widget", "description": "N/A", + "type": "magento2-module", + "license": [ + "OSL-3.0", + "AFL-3.0" + ], "config": { "sort-packages": true }, + "version": "100.4.4-p2", "require": { "php": "~7.4.0||~8.1.0", - "magento/framework": "*", - "magento/module-backend": "*", - "magento/module-catalog": "*", - "magento/module-customer": "*", - "magento/module-eav": "*", - "magento/module-rule": "*", - "magento/module-store": "*", - "magento/module-widget": "*", - "magento/module-wishlist": "*", - "magento/module-theme": "*" + "magento/framework": "103.0.*", + "magento/module-backend": "102.0.*", + "magento/module-catalog": "104.0.*", + "magento/module-customer": "103.0.*", + "magento/module-eav": "102.1.*", + "magento/module-rule": "100.4.*", + "magento/module-store": "101.1.*", + "magento/module-widget": "101.2.*", + "magento/module-wishlist": "101.2.*", + "magento/module-theme": "101.1.*" }, - "type": "magento2-module", - "license": [ - "OSL-3.0", - "AFL-3.0" - ], "autoload": { "files": [ "registration.php" @@ -31,3 +32,4 @@ } } } + diff --git a/app/code/Magento/Checkout/Controller/Sidebar/UpdateItemQty.php b/app/code/Magento/Checkout/Controller/Sidebar/UpdateItemQty.php index 44e40c57f6615..abaecf51b076b 100644 --- a/app/code/Magento/Checkout/Controller/Sidebar/UpdateItemQty.php +++ b/app/code/Magento/Checkout/Controller/Sidebar/UpdateItemQty.php @@ -3,19 +3,25 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Checkout\Controller\Sidebar; use Magento\Checkout\Model\Cart\RequestQuantityProcessor; use Magento\Checkout\Model\Sidebar; use Magento\Framework\App\Action\Action; use Magento\Framework\App\Action\Context; +use Magento\Framework\App\Action\HttpPostActionInterface; use Magento\Framework\App\ObjectManager; use Magento\Framework\App\Response\Http; use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Json\Helper\Data; use Psr\Log\LoggerInterface; -class UpdateItemQty extends Action +/** + * Class used to update item quantity. + */ +class UpdateItemQty extends Action implements HttpPostActionInterface { /** * @var Sidebar @@ -61,12 +67,18 @@ public function __construct( } /** + * Action for Quantity update + * * @return $this */ public function execute() { $itemId = (int)$this->getRequest()->getParam('item_id'); - $itemQty = $this->getRequest()->getParam('item_qty') * 1; + $itemQty = (int)$this->getRequest()->getParam('item_qty'); + + if ($itemQty <= 0) { + return $this->jsonResponse(__('Invalid Item Quantity Requested.')); + } $itemQty = $this->quantityProcessor->prepareQuantity($itemQty); try { diff --git a/app/code/Magento/Checkout/Model/DefaultConfigProvider.php b/app/code/Magento/Checkout/Model/DefaultConfigProvider.php index 9e31c4d77301e..dd4080bcc39dc 100644 --- a/app/code/Magento/Checkout/Model/DefaultConfigProvider.php +++ b/app/code/Magento/Checkout/Model/DefaultConfigProvider.php @@ -31,6 +31,7 @@ use Magento\Quote\Model\QuoteIdMaskFactory; use Magento\Store\Model\ScopeInterface; use Magento\Ui\Component\Form\Element\Multiline; +use Magento\Framework\Escaper; /** * Default Config Provider for checkout @@ -191,6 +192,11 @@ class DefaultConfigProvider implements ConfigProviderInterface */ private $configPostProcessor; + /** + * @var Escaper + */ + private $escaper; + /** * @param CheckoutHelper $checkoutHelper * @param Session $checkoutSession @@ -222,6 +228,7 @@ class DefaultConfigProvider implements ConfigProviderInterface * @param AddressMetadataInterface $addressMetadata * @param AttributeOptionManagementInterface $attributeOptionManager * @param CustomerAddressDataProvider|null $customerAddressData + * @param Escaper|null $escaper * @codeCoverageIgnore * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ @@ -255,7 +262,8 @@ public function __construct( CaptchaConfigPostProcessorInterface $configPostProcessor, AddressMetadataInterface $addressMetadata = null, AttributeOptionManagementInterface $attributeOptionManager = null, - CustomerAddressDataProvider $customerAddressData = null + CustomerAddressDataProvider $customerAddressData = null, + Escaper $escaper = null ) { $this->checkoutHelper = $checkoutHelper; $this->checkoutSession = $checkoutSession; @@ -289,6 +297,7 @@ public function __construct( $this->customerAddressData = $customerAddressData ?: ObjectManager::getInstance()->get(CustomerAddressDataProvider::class); $this->configPostProcessor = $configPostProcessor; + $this->escaper = $escaper ?? ObjectManager::getInstance()->get(Escaper::class); } /** @@ -343,6 +352,7 @@ public function getConfig() 'shipping/shipping_policy/shipping_policy_content', ScopeInterface::SCOPE_STORE ); + $policyContent = $this->escaper->escapeHtml($policyContent); $output['shippingPolicy'] = [ 'isEnabled' => $this->scopeConfig->isSetFlag( 'shipping/shipping_policy/enable_shipping_policy', diff --git a/app/code/Magento/Checkout/Model/ShippingInformationManagement.php b/app/code/Magento/Checkout/Model/ShippingInformationManagement.php index f397a8ddc9cf1..f08c48c55efa1 100644 --- a/app/code/Magento/Checkout/Model/ShippingInformationManagement.php +++ b/app/code/Magento/Checkout/Model/ShippingInformationManagement.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Checkout\Model; @@ -39,60 +40,62 @@ class ShippingInformationManagement implements ShippingInformationManagementInte /** * @var PaymentMethodManagementInterface */ - protected $paymentMethodManagement; + protected PaymentMethodManagementInterface $paymentMethodManagement; /** * @var PaymentDetailsFactory */ - protected $paymentDetailsFactory; + protected PaymentDetailsFactory $paymentDetailsFactory; /** * @var CartTotalRepositoryInterface */ - protected $cartTotalsRepository; + protected CartTotalRepositoryInterface $cartTotalsRepository; /** * @var CartRepositoryInterface */ - protected $quoteRepository; - + protected CartRepositoryInterface $quoteRepository; /** * @var Logger */ - protected $logger; + protected Logger $logger; /** * @var QuoteAddressValidator */ - protected $addressValidator; + protected QuoteAddressValidator $addressValidator; /** * @var AddressRepositoryInterface * @deprecated 100.2.0 + * @see AddressRepositoryInterface */ - protected $addressRepository; + protected AddressRepositoryInterface $addressRepository; /** * @var ScopeConfigInterface * @deprecated 100.2.0 + * @see ScopeConfigInterface */ - protected $scopeConfig; + protected ScopeConfigInterface $scopeConfig; /** * @var TotalsCollector * @deprecated 100.2.0 + * @see TotalsCollector */ - protected $totalsCollector; + protected TotalsCollector $totalsCollector; /** * @var CartExtensionFactory */ - private $cartExtensionFactory; + private CartExtensionFactory $cartExtensionFactory; /** * @var ShippingAssignmentFactory */ - protected $shippingAssignmentFactory; + protected ShippingAssignmentFactory $shippingAssignmentFactory; /** * @var ShippingFactory @@ -262,8 +265,11 @@ protected function validateQuote(Quote $quote): void * @param string $method * @return CartInterface */ - private function prepareShippingAssignment(CartInterface $quote, AddressInterface $address, $method): CartInterface - { + private function prepareShippingAssignment( + CartInterface $quote, + AddressInterface $address, + string $method + ): CartInterface { $cartExtension = $quote->getExtensionAttributes(); if ($cartExtension === null) { $cartExtension = $this->cartExtensionFactory->create(); diff --git a/app/code/Magento/Checkout/Observer/CspPolicyObserver.php b/app/code/Magento/Checkout/Observer/CspPolicyObserver.php new file mode 100644 index 0000000000000..10c6e7aefac12 --- /dev/null +++ b/app/code/Magento/Checkout/Observer/CspPolicyObserver.php @@ -0,0 +1,69 @@ +inlineTranslate = $inlineTranslate; + $this->dynamicCollector = $dynamicCollector; + } + + /** + * Override CSP policy for checkout page wit inline translation + * + * @param Observer $observer + * @return void + * + * @throws \Exception + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function execute(Observer $observer): void + { + if ($this->inlineTranslate->isAllowed()) { + $policy = new FetchPolicy( + 'script-src', + false, + [], + [], + true, + true, + false, + [], + [] + ); + + $this->dynamicCollector->add($policy); + } + } +} diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertMiniCartEmptyActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertMiniCartEmptyActionGroup.xml index 649421a53040b..2119e5c43f7bb 100644 --- a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertMiniCartEmptyActionGroup.xml +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertMiniCartEmptyActionGroup.xml @@ -13,8 +13,9 @@ Validates that the provided Product Count appears in the Storefront Header next to the Shopping Cart icon. Clicks on the Mini Shopping Cart icon. Validates that the 'No Items' message is present and correct in the Storefront Mini Shopping Cart. + - + diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/CheckoutPlaceOrderActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/CheckoutPlaceOrderActionGroup.xml index 48c515a3dd197..4269e647cf7f8 100644 --- a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/CheckoutPlaceOrderActionGroup.xml +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/CheckoutPlaceOrderActionGroup.xml @@ -17,8 +17,12 @@ + + + + diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutAsCustomerUsingDefaultAddressTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutAsCustomerUsingDefaultAddressTest.xml index 64f392d39edcb..f770e7f42caec 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutAsCustomerUsingDefaultAddressTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutAsCustomerUsingDefaultAddressTest.xml @@ -20,11 +20,11 @@ + 560 - @@ -40,6 +40,7 @@ + diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutAsCustomerUsingNonDefaultAddressTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutAsCustomerUsingNonDefaultAddressTest.xml index b60b60d5d0dc5..a235da133af79 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutAsCustomerUsingNonDefaultAddressTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutAsCustomerUsingNonDefaultAddressTest.xml @@ -20,6 +20,7 @@ + 560 @@ -40,6 +41,7 @@ + diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckoutWithSpecialPriceProductsTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckoutWithSpecialPriceProductsTest.xml index 1a85bb0bee1ee..07d1378d00529 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckoutWithSpecialPriceProductsTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckoutWithSpecialPriceProductsTest.xml @@ -18,6 +18,7 @@ + @@ -105,6 +106,7 @@ + diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerLoginDuringCheckoutTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerLoginDuringCheckoutTest.xml index eb76748a81c97..ec4593cf133b0 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerLoginDuringCheckoutTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerLoginDuringCheckoutTest.xml @@ -19,6 +19,7 @@ + @@ -39,9 +40,9 @@ - + diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontVerifySecureURLRedirectCheckoutTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontVerifySecureURLRedirectCheckoutTest.xml index 901c5c3598dbf..af5af1d2a0963 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontVerifySecureURLRedirectCheckoutTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontVerifySecureURLRedirectCheckoutTest.xml @@ -45,8 +45,10 @@ - + + + diff --git a/app/code/Magento/Checkout/Test/Unit/Controller/Sidebar/UpdateItemQtyTest.php b/app/code/Magento/Checkout/Test/Unit/Controller/Sidebar/UpdateItemQtyTest.php index 400f6ddfe26f4..959cebea17471 100644 --- a/app/code/Magento/Checkout/Test/Unit/Controller/Sidebar/UpdateItemQtyTest.php +++ b/app/code/Magento/Checkout/Test/Unit/Controller/Sidebar/UpdateItemQtyTest.php @@ -19,6 +19,9 @@ use PHPUnit\Framework\TestCase; use Psr\Log\LoggerInterface; +/** + * Class used to execute test cases for update item quantity + */ class UpdateItemQtyTest extends TestCase { /** @@ -244,4 +247,36 @@ public function testExecuteWithException(): void $this->assertEquals('json represented', $this->updateItemQty->execute()); } + + /** + * @return void + */ + public function testExecuteWithInvalidItemQty(): void + { + $error = [ + 'success' => false, + 'error_message' => 'Invalid Item Quantity Requested.' + ]; + $jsonResult = json_encode($error); + $this->requestMock + ->method('getParam') + ->withConsecutive(['item_id', null], ['item_qty', null]) + ->willReturnOnConsecutiveCalls('1', '{{7+2}}'); + + $this->sidebarMock->expects($this->once()) + ->method('getResponseData') + ->with('Invalid Item Quantity Requested.') + ->willReturn($error); + + $this->jsonHelperMock->expects($this->once()) + ->method('jsonEncode') + ->with($error) + ->willReturn($jsonResult); + + $this->responseMock->expects($this->once()) + ->method('representJson') + ->willReturn($jsonResult); + + $this->assertEquals($jsonResult, $this->updateItemQty->execute()); + } } diff --git a/app/code/Magento/Checkout/composer.json b/app/code/Magento/Checkout/composer.json index f277184d8986b..efbcddb7d1655 100644 --- a/app/code/Magento/Checkout/composer.json +++ b/app/code/Magento/Checkout/composer.json @@ -1,41 +1,43 @@ { "name": "magento/module-checkout", "description": "N/A", + "type": "magento2-module", + "license": [ + "OSL-3.0", + "AFL-3.0" + ], "config": { "sort-packages": true }, + "version": "100.4.4-p11", "require": { "php": "~7.4.0||~8.1.0", - "magento/framework": "*", - "magento/module-captcha": "*", - "magento/module-catalog": "*", - "magento/module-catalog-inventory": "*", - "magento/module-config": "*", - "magento/module-customer": "*", - "magento/module-directory": "*", - "magento/module-eav": "*", - "magento/module-msrp": "*", - "magento/module-page-cache": "*", - "magento/module-payment": "*", - "magento/module-quote": "*", - "magento/module-sales": "*", - "magento/module-sales-rule": "*", - "magento/module-security": "*", - "magento/module-shipping": "*", - "magento/module-store": "*", - "magento/module-tax": "*", - "magento/module-theme": "*", - "magento/module-ui": "*", - "magento/module-authorization": "*" + "magento/framework": "103.0.*", + "magento/module-captcha": "100.4.*", + "magento/module-catalog": "104.0.*", + "magento/module-catalog-inventory": "100.4.*", + "magento/module-config": "101.2.*", + "magento/module-customer": "103.0.*", + "magento/module-directory": "100.4.*", + "magento/module-eav": "102.1.*", + "magento/module-msrp": "100.4.*", + "magento/module-page-cache": "100.4.*", + "magento/module-payment": "100.4.*", + "magento/module-quote": "101.2.*", + "magento/module-sales": "103.0.*", + "magento/module-sales-rule": "101.2.*", + "magento/module-security": "100.4.*", + "magento/module-shipping": "100.4.*", + "magento/module-store": "101.1.*", + "magento/module-tax": "100.4.*", + "magento/module-theme": "101.1.*", + "magento/module-ui": "101.2.*", + "magento/module-authorization": "100.4.*", + "magento/module-csp": "100.4.*" }, "suggest": { - "magento/module-cookie": "*" + "magento/module-cookie": "100.4.*" }, - "type": "magento2-module", - "license": [ - "OSL-3.0", - "AFL-3.0" - ], "autoload": { "files": [ "registration.php" @@ -45,3 +47,4 @@ } } } + diff --git a/app/code/Magento/Checkout/etc/adminhtml/system.xml b/app/code/Magento/Checkout/etc/adminhtml/system.xml index b56566a043c3e..944914b34e1ea 100644 --- a/app/code/Magento/Checkout/etc/adminhtml/system.xml +++ b/app/code/Magento/Checkout/etc/adminhtml/system.xml @@ -13,6 +13,11 @@ Magento_Checkout::checkout + + + Magento\Config\Model\Config\Source\Yesno + Enabling this setting will allow unauthenticated users to query if an e-mail address is already associated with a customer account. This can be used to enhance the checkout workflow for guests that do not realize they already have an account but comes at the cost of exposing information to unauthenticated users. + Magento\Config\Model\Config\Source\Yesno @@ -23,7 +28,7 @@ - \Magento\Checkout\Model\Adminhtml\BillingAddressDisplayOptions + Magento\Checkout\Model\Adminhtml\BillingAddressDisplayOptions @@ -101,5 +106,17 @@ +
+ + + + + + If empty, Default Report URI for storefront will be used. + validate-url + + + +
diff --git a/app/code/Magento/Checkout/etc/config.xml b/app/code/Magento/Checkout/etc/config.xml index eac0bd849da35..ef4afdf8d4b27 100644 --- a/app/code/Magento/Checkout/etc/config.xml +++ b/app/code/Magento/Checkout/etc/config.xml @@ -9,6 +9,7 @@ + 0 1 1 0 @@ -55,5 +56,20 @@ + + + + 0 + + + + + + 0 + 1 + + + + diff --git a/app/code/Magento/Checkout/etc/frontend/events.xml b/app/code/Magento/Checkout/etc/frontend/events.xml index 901b652201db1..90e2236ee9d63 100644 --- a/app/code/Magento/Checkout/etc/frontend/events.xml +++ b/app/code/Magento/Checkout/etc/frontend/events.xml @@ -12,4 +12,8 @@ + + + diff --git a/app/code/Magento/Checkout/i18n/en_US.csv b/app/code/Magento/Checkout/i18n/en_US.csv index ca118f21f2441..de0c4717e4170 100644 --- a/app/code/Magento/Checkout/i18n/en_US.csv +++ b/app/code/Magento/Checkout/i18n/en_US.csv @@ -186,3 +186,4 @@ Payment,Payment "Show Cross-sell Items in the Shopping Cart","Show Cross-sell Items in the Shopping Cart" "You added %1 to your shopping cart.","You added %1 to your shopping cart." "The shipping method is missing. Select the shipping method and try again.","The shipping method is missing. Select the shipping method and try again." +"Invalid Item Quantity Requested.","Invalid Item Quantity Requested." diff --git a/app/code/Magento/Checkout/view/frontend/web/js/action/redirect-on-success.js b/app/code/Magento/Checkout/view/frontend/web/js/action/redirect-on-success.js index 3c75e3aebf994..a3e1282c41f26 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/action/redirect-on-success.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/action/redirect-on-success.js @@ -22,6 +22,13 @@ define( */ execute: function () { fullScreenLoader.startLoader(); + this.redirectToSuccessPage(); + }, + + /** + * Redirect to success page. + */ + redirectToSuccessPage: function () { window.location.replace(url.build(this.redirectUrl)); } }; diff --git a/app/code/Magento/Checkout/view/frontend/web/js/model/error-processor.js b/app/code/Magento/Checkout/view/frontend/web/js/model/error-processor.js index bf1697650e762..b9c9d4cc9f06f 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/model/error-processor.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/model/error-processor.js @@ -24,7 +24,7 @@ define([ messageContainer = messageContainer || globalMessageList; if (response.status == 401) { //eslint-disable-line eqeqeq - window.location.replace(url.build('customer/account/login/')); + this.redirectTo(url.build('customer/account/login/')); } else { try { error = JSON.parse(response.responseText); @@ -35,6 +35,13 @@ define([ } messageContainer.addErrorMessage(error); } + }, + + /** + * Method to redirect by requested URL. + */ + redirectTo: function (redirectUrl) { + window.location.replace(redirectUrl); } }; }); diff --git a/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/AdminDeleteAllTermConditionsActionGroup.xml b/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/AdminDeleteAllTermConditionsActionGroup.xml new file mode 100644 index 0000000000000..fec0a686d839e --- /dev/null +++ b/app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/AdminDeleteAllTermConditionsActionGroup.xml @@ -0,0 +1,27 @@ + + + + + + Deletes all rows one by one on the 'Terms and Conditions' page. + + + + + + {{AdminTermGridSection.allTermRows}} + {{AdminMainActionsSection.delete}} + {{AdminConfirmationModalSection.ok}} + You deleted the condition. + {{AdminMessagesSection.success}} + + + + + diff --git a/app/code/Magento/CheckoutAgreements/Test/Mftf/Helper/CheckoutAgreementsHelpers.php b/app/code/Magento/CheckoutAgreements/Test/Mftf/Helper/CheckoutAgreementsHelpers.php new file mode 100644 index 0000000000000..7f0150b274f47 --- /dev/null +++ b/app/code/Magento/CheckoutAgreements/Test/Mftf/Helper/CheckoutAgreementsHelpers.php @@ -0,0 +1,61 @@ +getModule("\\" . MagentoWebDriver::class); + $webDriver = $magentoWebDriver->webDriver; + + $magentoWebDriver->waitForPageLoad(30); + $rows = $webDriver->findElements(WebDriverBy::xpath($rowsToDelete)); + while (!empty($rows)) { + $rows[0]->click(); + $magentoWebDriver->waitForPageLoad(30); + $magentoWebDriver->waitForElementVisible($deleteButton, 10); + $magentoWebDriver->click($deleteButton); + $magentoWebDriver->waitForPageLoad(30); + $magentoWebDriver->waitForElementVisible($modalAcceptButton, 10); + $magentoWebDriver->click($modalAcceptButton); + $magentoWebDriver->waitForPageLoad(60); + $magentoWebDriver->waitForText($successMessage, 10, $successMessageContainer); + $rows = $webDriver->findElements(WebDriverBy::xpath($rowsToDelete)); + } + } catch (Exception $exception) { + $this->fail($exception->getMessage()); + } + } +} diff --git a/app/code/Magento/CheckoutAgreements/Test/Mftf/Section/AdminTermGridSection.xml b/app/code/Magento/CheckoutAgreements/Test/Mftf/Section/AdminTermGridSection.xml index 326f9dcce4320..ab198fbfb4ecc 100644 --- a/app/code/Magento/CheckoutAgreements/Test/Mftf/Section/AdminTermGridSection.xml +++ b/app/code/Magento/CheckoutAgreements/Test/Mftf/Section/AdminTermGridSection.xml @@ -14,5 +14,7 @@ + + diff --git a/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminCreateActiveHtmlTermEntityTest.xml b/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminCreateActiveHtmlTermEntityTest.xml index c597d3d660dc8..4c1a258115646 100644 --- a/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminCreateActiveHtmlTermEntityTest.xml +++ b/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminCreateActiveHtmlTermEntityTest.xml @@ -32,10 +32,9 @@ - - - - + + + diff --git a/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminCreateActiveTextTermEntityTest.xml b/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminCreateActiveTextTermEntityTest.xml index a90c3536ec744..f0c2bac759829 100644 --- a/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminCreateActiveTextTermEntityTest.xml +++ b/app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminCreateActiveTextTermEntityTest.xml @@ -20,9 +20,7 @@ - - - + diff --git a/app/code/Magento/CheckoutAgreements/composer.json b/app/code/Magento/CheckoutAgreements/composer.json index 753bef25e3e64..e5cc74b014ea3 100644 --- a/app/code/Magento/CheckoutAgreements/composer.json +++ b/app/code/Magento/CheckoutAgreements/composer.json @@ -1,22 +1,23 @@ { "name": "magento/module-checkout-agreements", "description": "N/A", + "type": "magento2-module", + "license": [ + "OSL-3.0", + "AFL-3.0" + ], "config": { "sort-packages": true }, + "version": "100.4.3-p7", "require": { "php": "~7.4.0||~8.1.0", - "magento/framework": "*", - "magento/module-backend": "*", - "magento/module-checkout": "*", - "magento/module-quote": "*", - "magento/module-store": "*" + "magento/framework": "103.0.*", + "magento/module-backend": "102.0.*", + "magento/module-checkout": "100.4.*", + "magento/module-quote": "101.2.*", + "magento/module-store": "101.1.*" }, - "type": "magento2-module", - "license": [ - "OSL-3.0", - "AFL-3.0" - ], "autoload": { "files": [ "registration.php" @@ -26,3 +27,4 @@ } } } + diff --git a/app/code/Magento/CheckoutAgreementsGraphQl/composer.json b/app/code/Magento/CheckoutAgreementsGraphQl/composer.json index de6bc855e7847..f75215e0e3e1f 100644 --- a/app/code/Magento/CheckoutAgreementsGraphQl/composer.json +++ b/app/code/Magento/CheckoutAgreementsGraphQl/composer.json @@ -2,19 +2,20 @@ "name": "magento/module-checkout-agreements-graph-ql", "description": "N/A", "type": "magento2-module", + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "version": "100.4.1", "require": { "php": "~7.4.0||~8.1.0", - "magento/framework": "*", - "magento/module-store": "*", - "magento/module-checkout-agreements": "*" + "magento/framework": "103.0.*", + "magento/module-store": "101.1.*", + "magento/module-checkout-agreements": "100.4.*" }, "suggest": { - "magento/module-graph-ql": "*" + "magento/module-graph-ql": "100.4.*" }, - "license": [ - "OSL-3.0", - "AFL-3.0" - ], "autoload": { "files": [ "registration.php" @@ -24,3 +25,4 @@ } } } + diff --git a/app/code/Magento/Cms/Block/Adminhtml/Block/Widget/Chooser.php b/app/code/Magento/Cms/Block/Adminhtml/Block/Widget/Chooser.php index 86976f6c912e8..d56aeeb15d723 100644 --- a/app/code/Magento/Cms/Block/Adminhtml/Block/Widget/Chooser.php +++ b/app/code/Magento/Cms/Block/Adminhtml/Block/Widget/Chooser.php @@ -96,7 +96,7 @@ public function prepareElementHtml(\Magento\Framework\Data\Form\Element\Abstract */ public function getRowClickCallback() { - $chooserJsObject = $this->getId(); + $chooserJsObject = $this->_escaper->escapeJs($this->getId()); $js = ' function (grid, event) { var trElement = Event.findElement(event, "tr"); diff --git a/app/code/Magento/Cms/Block/Adminhtml/Page/Widget/Chooser.php b/app/code/Magento/Cms/Block/Adminhtml/Page/Widget/Chooser.php index 32d36a1869958..e6647985bd94e 100644 --- a/app/code/Magento/Cms/Block/Adminhtml/Page/Widget/Chooser.php +++ b/app/code/Magento/Cms/Block/Adminhtml/Page/Widget/Chooser.php @@ -65,7 +65,6 @@ public function __construct( protected function _construct() { parent::_construct(); - //$this->setDefaultSort('name'); $this->setUseAjax(true); $this->setDefaultFilter(['chooser_is_active' => '1']); } @@ -113,7 +112,7 @@ public function prepareElementHtml(\Magento\Framework\Data\Form\Element\Abstract */ public function getRowClickCallback() { - $chooserJsObject = $this->getId(); + $chooserJsObject = $this->_escaper->escapeJs($this->getId()); $js = ' function (grid, event) { var trElement = Event.findElement(event, "tr"); diff --git a/app/code/Magento/Cms/Controller/Noroute/Index.php b/app/code/Magento/Cms/Controller/Noroute/Index.php index b30beae73dce1..5475e7960dd26 100644 --- a/app/code/Magento/Cms/Controller/Noroute/Index.php +++ b/app/code/Magento/Cms/Controller/Noroute/Index.php @@ -6,15 +6,17 @@ */ namespace Magento\Cms\Controller\Noroute; +use Magento\Framework\Controller\Result\ForwardFactory; + /** * @SuppressWarnings(PHPMD.AllPurposeAction) */ class Index extends \Magento\Framework\App\Action\Action { /** - * @var \Magento\Framework\Controller\Result\ForwardFactory + * @var ForwardFactory */ - protected $resultForwardFactory; + protected ForwardFactory $resultForwardFactory; /** * @param \Magento\Framework\App\Action\Context $context @@ -48,6 +50,7 @@ public function execute() if ($resultPage) { $resultPage->setStatusHeader(404, '1.1', 'Not Found'); $resultPage->setHeader('Status', '404 File not found'); + $resultPage->setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0', true); return $resultPage; } else { /** @var \Magento\Framework\Controller\Result\Forward $resultForward */ diff --git a/app/code/Magento/Cms/Model/Wysiwyg/DefaultConfigProvider.php b/app/code/Magento/Cms/Model/Wysiwyg/DefaultConfigProvider.php index 326f8b7d26b9c..09820927e6036 100644 --- a/app/code/Magento/Cms/Model/Wysiwyg/DefaultConfigProvider.php +++ b/app/code/Magento/Cms/Model/Wysiwyg/DefaultConfigProvider.php @@ -27,13 +27,13 @@ public function __construct(\Magento\Framework\View\Asset\Repository $assetRepo) } /** - * {@inheritdoc} + * @inheritdoc */ public function getConfig(\Magento\Framework\DataObject $config) : \Magento\Framework\DataObject { $config->addData([ 'tinymce' => [ - 'toolbar' => 'formatselect | bold italic underline | alignleft aligncenter alignright | ' + 'toolbar' => ' blocks | formatselect | bold italic underline | alignleft aligncenter alignright | ' . 'bullist numlist | link table charmap', 'plugins' => implode( ' ', @@ -44,10 +44,7 @@ public function getConfig(\Magento\Framework\DataObject $config) : \Magento\Fram 'link', 'charmap', 'media', - 'noneditable', 'table', - 'contextmenu', - 'paste', 'code', 'help', 'table' diff --git a/app/code/Magento/Cms/Test/Mftf/Data/WysiwygConfigData.xml b/app/code/Magento/Cms/Test/Mftf/Data/WysiwygConfigData.xml index e8c02dfd56589..5fae3d9842e3b 100644 --- a/app/code/Magento/Cms/Test/Mftf/Data/WysiwygConfigData.xml +++ b/app/code/Magento/Cms/Test/Mftf/Data/WysiwygConfigData.xml @@ -22,6 +22,6 @@ cms/wysiwyg/editor 0 Yes - mage/adminhtml/wysiwyg/tiny_mce/tinymce5Adapter + mage/adminhtml/wysiwyg/tiny_mce/tinymceAdapter diff --git a/app/code/Magento/Cms/Test/Mftf/Section/TinyMCESection/MediaGallerySection.xml b/app/code/Magento/Cms/Test/Mftf/Section/TinyMCESection/MediaGallerySection.xml index a6281be044d84..e97209c79f5ff 100644 --- a/app/code/Magento/Cms/Test/Mftf/Section/TinyMCESection/MediaGallerySection.xml +++ b/app/code/Magento/Cms/Test/Mftf/Section/TinyMCESection/MediaGallerySection.xml @@ -20,7 +20,7 @@ - + diff --git a/app/code/Magento/Cms/Test/Mftf/Section/TinyMCESection/TinyMCESection.xml b/app/code/Magento/Cms/Test/Mftf/Section/TinyMCESection/TinyMCESection.xml index abc4d99e16597..04c7dce180cc1 100644 --- a/app/code/Magento/Cms/Test/Mftf/Section/TinyMCESection/TinyMCESection.xml +++ b/app/code/Magento/Cms/Test/Mftf/Section/TinyMCESection/TinyMCESection.xml @@ -16,19 +16,19 @@ - - - - - - - - - - - - - + + + + + + + + + + + + + diff --git a/app/code/Magento/Cms/Test/Unit/Controller/Noroute/IndexTest.php b/app/code/Magento/Cms/Test/Unit/Controller/Noroute/IndexTest.php index 665b79fdf48be..6d4dcfc0711df 100644 --- a/app/code/Magento/Cms/Test/Unit/Controller/Noroute/IndexTest.php +++ b/app/code/Magento/Cms/Test/Unit/Controller/Noroute/IndexTest.php @@ -29,17 +29,17 @@ class IndexTest extends TestCase /** * @var Index */ - protected $_controller; + protected Index $_controller; /** * @var MockObject */ - protected $_cmsHelperMock; + protected MockObject $_cmsHelperMock; /** * @var MockObject */ - protected $_requestMock; + protected MockObject $_requestMock; /** * @var ForwardFactory|MockObject @@ -121,8 +121,10 @@ public function testExecuteResultPage(): void ->willReturn($this->resultPageMock); $this->resultPageMock ->method('setHeader') - ->with('Status', '404 File not found') - ->willReturn($this->resultPageMock); + ->withConsecutive( + ['Status', '404 File not found'], + ['Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0'] + )->willReturn($this->resultPageMock); $this->_cmsHelperMock->expects( $this->once() )->method( diff --git a/app/code/Magento/Cms/composer.json b/app/code/Magento/Cms/composer.json index b3b2ba31db37b..78eaa8a1854fe 100644 --- a/app/code/Magento/Cms/composer.json +++ b/app/code/Magento/Cms/composer.json @@ -1,30 +1,31 @@ { "name": "magento/module-cms", "description": "N/A", + "type": "magento2-module", + "license": [ + "OSL-3.0", + "AFL-3.0" + ], "config": { "sort-packages": true }, + "version": "104.0.4-p11", "require": { "php": "~7.4.0||~8.1.0", - "magento/framework": "*", - "magento/module-backend": "*", - "magento/module-catalog": "*", - "magento/module-email": "*", - "magento/module-media-storage": "*", - "magento/module-store": "*", - "magento/module-theme": "*", - "magento/module-ui": "*", - "magento/module-variable": "*", - "magento/module-widget": "*" + "magento/framework": "103.0.*", + "magento/module-backend": "102.0.*", + "magento/module-catalog": "104.0.*", + "magento/module-email": "101.1.*", + "magento/module-media-storage": "100.4.*", + "magento/module-store": "101.1.*", + "magento/module-theme": "101.1.*", + "magento/module-ui": "101.2.*", + "magento/module-variable": "100.4.*", + "magento/module-widget": "101.2.*" }, "suggest": { - "magento/module-cms-sample-data": "*" + "magento/module-cms-sample-data": "Sample Data version: 100.4.*" }, - "type": "magento2-module", - "license": [ - "OSL-3.0", - "AFL-3.0" - ], "autoload": { "files": [ "registration.php" @@ -34,3 +35,4 @@ } } } + diff --git a/app/code/Magento/Cms/etc/adminhtml/di.xml b/app/code/Magento/Cms/etc/adminhtml/di.xml index e2ef86b7f650b..c2bd2e28e3bec 100644 --- a/app/code/Magento/Cms/etc/adminhtml/di.xml +++ b/app/code/Magento/Cms/etc/adminhtml/di.xml @@ -37,8 +37,8 @@ - mage/adminhtml/wysiwyg/tiny_mce/tinymce5Adapter - TinyMCE 5 + mage/adminhtml/wysiwyg/tiny_mce/tinymceAdapter + TinyMCE 7 diff --git a/app/code/Magento/Cms/etc/config.xml b/app/code/Magento/Cms/etc/config.xml index fa5b28a93639d..d21f497758960 100644 --- a/app/code/Magento/Cms/etc/config.xml +++ b/app/code/Magento/Cms/etc/config.xml @@ -23,7 +23,7 @@ enabled - mage/adminhtml/wysiwyg/tiny_mce/tinymce5Adapter + mage/adminhtml/wysiwyg/tiny_mce/tinymceAdapter 0 diff --git a/app/code/Magento/CmsGraphQl/composer.json b/app/code/Magento/CmsGraphQl/composer.json index b2550344299fa..31cdf1b9242e7 100644 --- a/app/code/Magento/CmsGraphQl/composer.json +++ b/app/code/Magento/CmsGraphQl/composer.json @@ -2,22 +2,23 @@ "name": "magento/module-cms-graph-ql", "description": "N/A", "type": "magento2-module", + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "version": "100.4.2", "require": { "php": "~7.4.0||~8.1.0", - "magento/framework": "*", - "magento/module-cms": "*", - "magento/module-widget": "*", - "magento/module-store": "*" + "magento/framework": "103.0.*", + "magento/module-cms": "104.0.*", + "magento/module-widget": "101.2.*", + "magento/module-store": "101.1.*" }, "suggest": { - "magento/module-graph-ql": "*", - "magento/module-graph-ql-cache": "*", - "magento/module-store-graph-ql": "*" + "magento/module-graph-ql": "100.4.*", + "magento/module-graph-ql-cache": "100.4.*", + "magento/module-store-graph-ql": "100.4.*" }, - "license": [ - "OSL-3.0", - "AFL-3.0" - ], "autoload": { "files": [ "registration.php" @@ -27,3 +28,4 @@ } } } + diff --git a/app/code/Magento/CmsUrlRewrite/composer.json b/app/code/Magento/CmsUrlRewrite/composer.json index 8fb9bbfff22e2..d9e13faaab104 100644 --- a/app/code/Magento/CmsUrlRewrite/composer.json +++ b/app/code/Magento/CmsUrlRewrite/composer.json @@ -1,21 +1,22 @@ { "name": "magento/module-cms-url-rewrite", "description": "N/A", + "type": "magento2-module", + "license": [ + "OSL-3.0", + "AFL-3.0" + ], "config": { "sort-packages": true }, + "version": "100.4.3", "require": { "php": "~7.4.0||~8.1.0", - "magento/framework": "*", - "magento/module-cms": "*", - "magento/module-store": "*", - "magento/module-url-rewrite": "*" + "magento/framework": "103.0.*", + "magento/module-cms": "104.0.*", + "magento/module-store": "101.1.*", + "magento/module-url-rewrite": "102.0.*" }, - "type": "magento2-module", - "license": [ - "OSL-3.0", - "AFL-3.0" - ], "autoload": { "files": [ "registration.php" @@ -25,3 +26,4 @@ } } } + diff --git a/app/code/Magento/CmsUrlRewriteGraphQl/composer.json b/app/code/Magento/CmsUrlRewriteGraphQl/composer.json index 70a598d26d574..05fb964b8e51e 100644 --- a/app/code/Magento/CmsUrlRewriteGraphQl/composer.json +++ b/app/code/Magento/CmsUrlRewriteGraphQl/composer.json @@ -2,22 +2,23 @@ "name": "magento/module-cms-url-rewrite-graph-ql", "description": "N/A", "type": "magento2-module", + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "version": "100.4.2", "require": { "php": "~7.4.0||~8.1.0", - "magento/framework": "*", - "magento/module-cms": "*", - "magento/module-store": "*", - "magento/module-url-rewrite-graph-ql": "*", - "magento/module-cms-graph-ql": "*" + "magento/framework": "103.0.*", + "magento/module-cms": "104.0.*", + "magento/module-store": "101.1.*", + "magento/module-url-rewrite-graph-ql": "100.4.*", + "magento/module-cms-graph-ql": "100.4.*" }, "suggest": { - "magento/module-cms-url-rewrite": "*", - "magento/module-catalog-graph-ql": "*" + "magento/module-cms-url-rewrite": "100.4.*", + "magento/module-catalog-graph-ql": "100.4.*" }, - "license": [ - "OSL-3.0", - "AFL-3.0" - ], "autoload": { "files": [ "registration.php" @@ -27,3 +28,4 @@ } } } + diff --git a/app/code/Magento/CompareListGraphQl/composer.json b/app/code/Magento/CompareListGraphQl/composer.json index e8fb5d588852e..bae7c7a6e3769 100644 --- a/app/code/Magento/CompareListGraphQl/composer.json +++ b/app/code/Magento/CompareListGraphQl/composer.json @@ -2,16 +2,17 @@ "name": "magento/module-compare-list-graph-ql", "description": "N/A", "type": "magento2-module", - "require": { - "php": "~7.4.0||~8.1.0", - "magento/framework": "*", - "magento/module-catalog": "*", - "magento/module-customer": "*" - }, "license": [ "OSL-3.0", "AFL-3.0" ], + "version": "100.4.1", + "require": { + "php": "~7.4.0||~8.1.0", + "magento/framework": "103.0.*", + "magento/module-catalog": "104.0.*", + "magento/module-customer": "103.0.*" + }, "autoload": { "files": [ "registration.php" @@ -21,3 +22,4 @@ } } } + diff --git a/app/code/Magento/Config/Block/System/Config/Form/Field/File.php b/app/code/Magento/Config/Block/System/Config/Form/Field/File.php index 6ebd85af7a5da..edc3d3e977072 100644 --- a/app/code/Magento/Config/Block/System/Config/Form/Field/File.php +++ b/app/code/Magento/Config/Block/System/Config/Form/Field/File.php @@ -9,6 +9,8 @@ * * @author Magento Core Team */ +declare(strict_types=1); + namespace Magento\Config\Block\System\Config\Form\Field; class File extends \Magento\Framework\Data\Form\Element\File @@ -35,7 +37,7 @@ protected function _getDeleteCheckbox() $html = ''; if ((string)$this->getValue()) { $label = __('Delete File'); - $html .= '
' . $this->getValue() . ' '; + $html .= '
' . $this->_escaper->escapeHtml($this->getValue()) . ' '; $html .= 'assertStringContainsString($this->testData['before_element_html'], $html); diff --git a/app/code/Magento/Config/composer.json b/app/code/Magento/Config/composer.json index 61100e6336c27..46c7dfb2a1457 100644 --- a/app/code/Magento/Config/composer.json +++ b/app/code/Magento/Config/composer.json @@ -1,25 +1,26 @@ { "name": "magento/module-config", "description": "N/A", + "type": "magento2-module", + "license": [ + "OSL-3.0", + "AFL-3.0" + ], "config": { "sort-packages": true }, + "version": "101.2.4-p10", "require": { "php": "~7.4.0||~8.1.0", - "magento/framework": "*", - "magento/module-backend": "*", - "magento/module-cron": "*", - "magento/module-deploy": "*", - "magento/module-directory": "*", - "magento/module-email": "*", - "magento/module-media-storage": "*", - "magento/module-store": "*" + "magento/framework": "103.0.*", + "magento/module-backend": "102.0.*", + "magento/module-cron": "100.4.*", + "magento/module-deploy": "100.4.*", + "magento/module-directory": "100.4.*", + "magento/module-email": "101.1.*", + "magento/module-media-storage": "100.4.*", + "magento/module-store": "101.1.*" }, - "type": "magento2-module", - "license": [ - "OSL-3.0", - "AFL-3.0" - ], "autoload": { "files": [ "registration.php" @@ -29,3 +30,4 @@ } } } + diff --git a/app/code/Magento/Config/i18n/en_US.csv b/app/code/Magento/Config/i18n/en_US.csv index ceb1efdc8b77d..b7e1e4c7611bc 100644 --- a/app/code/Magento/Config/i18n/en_US.csv +++ b/app/code/Magento/Config/i18n/en_US.csv @@ -119,3 +119,4 @@ Dashboard,Dashboard "Store Email Addresses Section","Store Email Addresses Section" "Email to a Friend","Email to a Friend" "Taiwan","Taiwan, Province of China" +"Invalid file name", "Invalid file name" diff --git a/app/code/Magento/ConfigurableImportExport/composer.json b/app/code/Magento/ConfigurableImportExport/composer.json index 98205def6a799..6280f47609dbd 100644 --- a/app/code/Magento/ConfigurableImportExport/composer.json +++ b/app/code/Magento/ConfigurableImportExport/composer.json @@ -1,24 +1,25 @@ { "name": "magento/module-configurable-import-export", "description": "N/A", + "type": "magento2-module", + "license": [ + "OSL-3.0", + "AFL-3.0" + ], "config": { "sort-packages": true }, + "version": "100.4.2", "require": { "php": "~7.4.0||~8.1.0", - "magento/framework": "*", - "magento/module-catalog": "*", - "magento/module-catalog-import-export": "*", - "magento/module-configurable-product": "*", - "magento/module-eav": "*", - "magento/module-import-export": "*", - "magento/module-store": "*" + "magento/framework": "103.0.*", + "magento/module-catalog": "104.0.*", + "magento/module-catalog-import-export": "101.1.*", + "magento/module-configurable-product": "100.4.*", + "magento/module-eav": "102.1.*", + "magento/module-import-export": "101.0.*", + "magento/module-store": "101.1.*" }, - "type": "magento2-module", - "license": [ - "OSL-3.0", - "AFL-3.0" - ], "autoload": { "files": [ "registration.php" @@ -28,3 +29,4 @@ } } } + diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/CreateNewAttributeActionGroup.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/CreateNewAttributeActionGroup.xml index 9925aba09fb82..0f0ea705971ec 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/CreateNewAttributeActionGroup.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/CreateNewAttributeActionGroup.xml @@ -51,6 +51,7 @@ + diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Data/ConfigurableProductAttributeNameDesignData.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Data/ConfigurableProductAttributeNameDesignData.xml index 0018f5996c9bc..ec8fc99b46bb3 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Data/ConfigurableProductAttributeNameDesignData.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Data/ConfigurableProductAttributeNameDesignData.xml @@ -12,7 +12,7 @@ Shoes 60 100 - design + design123 red red123 blue diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/ConfigurableProductAttributeNameDesignSection/CatalogProductsSection.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/ConfigurableProductAttributeNameDesignSection/CatalogProductsSection.xml index fb71d6cbda2a8..df3fffb6f716c 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/ConfigurableProductAttributeNameDesignSection/CatalogProductsSection.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/ConfigurableProductAttributeNameDesignSection/CatalogProductsSection.xml @@ -14,7 +14,7 @@ - + diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/ConfigurableProductAttributeNameDesignSection/ConfigurableProductSection.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/ConfigurableProductAttributeNameDesignSection/ConfigurableProductSection.xml index 8099f30941f7d..437411d7c041a 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/ConfigurableProductAttributeNameDesignSection/ConfigurableProductSection.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/ConfigurableProductAttributeNameDesignSection/ConfigurableProductSection.xml @@ -12,7 +12,7 @@ - + diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontGalleryConfigurableProductWithVisualSwatchAttributePrependMediaTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontGalleryConfigurableProductWithVisualSwatchAttributePrependMediaTest.xml index c76d49a76d947..61d4eb230b9ec 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontGalleryConfigurableProductWithVisualSwatchAttributePrependMediaTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontGalleryConfigurableProductWithVisualSwatchAttributePrependMediaTest.xml @@ -106,7 +106,9 @@ - + + + @@ -115,14 +117,16 @@ - + - + + + @@ -130,7 +134,10 @@ - + + + + |{{Magento2.filename}}.*.jpg| @@ -157,7 +164,13 @@ - + + + + + + + |{{MagentoLogo.filename}}.*.png| @@ -196,7 +209,10 @@ - + + + + |{{Magento2.filename}}.*.jpg| @@ -223,7 +239,13 @@ - + + + + + + + |{{Magento3.filename}}.*.jpg| @@ -262,7 +284,10 @@ - + + + + |{{Magento2.filename}}.*.jpg| diff --git a/app/code/Magento/ConfigurableProduct/composer.json b/app/code/Magento/ConfigurableProduct/composer.json index 67b1ad2b2ed33..940a1d8581646 100644 --- a/app/code/Magento/ConfigurableProduct/composer.json +++ b/app/code/Magento/ConfigurableProduct/composer.json @@ -1,38 +1,39 @@ { "name": "magento/module-configurable-product", "description": "N/A", + "type": "magento2-module", + "license": [ + "OSL-3.0", + "AFL-3.0" + ], "config": { "sort-packages": true }, + "version": "100.4.4-p7", "require": { "php": "~7.4.0||~8.1.0", - "magento/framework": "*", - "magento/module-backend": "*", - "magento/module-catalog": "*", - "magento/module-catalog-inventory": "*", - "magento/module-checkout": "*", - "magento/module-customer": "*", - "magento/module-eav": "*", - "magento/module-media-storage": "*", - "magento/module-quote": "*", - "magento/module-store": "*", - "magento/module-ui": "*" + "magento/framework": "103.0.*", + "magento/module-backend": "102.0.*", + "magento/module-catalog": "104.0.*", + "magento/module-catalog-inventory": "100.4.*", + "magento/module-checkout": "100.4.*", + "magento/module-customer": "103.0.*", + "magento/module-eav": "102.1.*", + "magento/module-media-storage": "100.4.*", + "magento/module-quote": "101.2.*", + "magento/module-store": "101.1.*", + "magento/module-ui": "101.2.*" }, "suggest": { - "magento/module-msrp": "*", - "magento/module-webapi": "*", - "magento/module-sales": "*", - "magento/module-sales-rule": "*", - "magento/module-product-video": "*", - "magento/module-configurable-sample-data": "*", - "magento/module-product-links-sample-data": "*", - "magento/module-tax": "*" + "magento/module-msrp": "100.4.*", + "magento/module-webapi": "100.4.*", + "magento/module-sales": "103.0.*", + "magento/module-sales-rule": "101.2.*", + "magento/module-product-video": "100.4.*", + "magento/module-configurable-sample-data": "Sample Data version: 100.4.*", + "magento/module-product-links-sample-data": "Sample Data version: 100.4.*", + "magento/module-tax": "100.4.*" }, - "type": "magento2-module", - "license": [ - "OSL-3.0", - "AFL-3.0" - ], "autoload": { "files": [ "registration.php" @@ -42,3 +43,4 @@ } } } + diff --git a/app/code/Magento/ConfigurableProductGraphQl/composer.json b/app/code/Magento/ConfigurableProductGraphQl/composer.json index b839227511d88..c6afeab187bdb 100644 --- a/app/code/Magento/ConfigurableProductGraphQl/composer.json +++ b/app/code/Magento/ConfigurableProductGraphQl/composer.json @@ -2,21 +2,22 @@ "name": "magento/module-configurable-product-graph-ql", "description": "N/A", "type": "magento2-module", - "require": { - "php": "~7.4.0||~8.1.0", - "magento/module-catalog": "*", - "magento/module-configurable-product": "*", - "magento/module-graph-ql": "*", - "magento/module-catalog-graph-ql": "*", - "magento/module-quote": "*", - "magento/module-quote-graph-ql": "*", - "magento/module-catalog-inventory": "*", - "magento/framework": "*" - }, "license": [ "OSL-3.0", "AFL-3.0" ], + "version": "100.4.4", + "require": { + "php": "~7.4.0||~8.1.0", + "magento/module-catalog": "104.0.*", + "magento/module-configurable-product": "100.4.*", + "magento/module-graph-ql": "100.4.*", + "magento/module-catalog-graph-ql": "100.4.*", + "magento/module-quote": "101.2.*", + "magento/module-quote-graph-ql": "100.4.*", + "magento/module-catalog-inventory": "100.4.*", + "magento/framework": "103.0.*" + }, "autoload": { "files": [ "registration.php" @@ -26,3 +27,4 @@ } } } + diff --git a/app/code/Magento/ConfigurableProductSales/composer.json b/app/code/Magento/ConfigurableProductSales/composer.json index 55b2e78bd24d2..4256442b87ac1 100644 --- a/app/code/Magento/ConfigurableProductSales/composer.json +++ b/app/code/Magento/ConfigurableProductSales/composer.json @@ -1,22 +1,23 @@ { "name": "magento/module-configurable-product-sales", "description": "N/A", + "type": "magento2-module", + "license": [ + "OSL-3.0", + "AFL-3.0" + ], "config": { "sort-packages": true }, + "version": "100.4.2", "require": { "php": "~7.4.0||~8.1.0", - "magento/framework": "*", - "magento/module-catalog": "*", - "magento/module-sales": "*", - "magento/module-store": "*", - "magento/module-configurable-product": "*" + "magento/framework": "103.0.*", + "magento/module-catalog": "104.0.*", + "magento/module-sales": "103.0.*", + "magento/module-store": "101.1.*", + "magento/module-configurable-product": "100.4.*" }, - "type": "magento2-module", - "license": [ - "OSL-3.0", - "AFL-3.0" - ], "autoload": { "files": [ "registration.php" @@ -26,3 +27,4 @@ } } } + diff --git a/app/code/Magento/Contact/composer.json b/app/code/Magento/Contact/composer.json index 00ea8f865928d..55e8dae73c64d 100644 --- a/app/code/Magento/Contact/composer.json +++ b/app/code/Magento/Contact/composer.json @@ -1,22 +1,23 @@ { "name": "magento/module-contact", "description": "N/A", + "type": "magento2-module", + "license": [ + "OSL-3.0", + "AFL-3.0" + ], "config": { "sort-packages": true }, + "version": "100.4.4", "require": { "php": "~7.4.0||~8.1.0", - "magento/framework": "*", - "magento/module-cms": "*", - "magento/module-config": "*", - "magento/module-customer": "*", - "magento/module-store": "*" + "magento/framework": "103.0.*", + "magento/module-cms": "104.0.*", + "magento/module-config": "101.2.*", + "magento/module-customer": "103.0.*", + "magento/module-store": "101.1.*" }, - "type": "magento2-module", - "license": [ - "OSL-3.0", - "AFL-3.0" - ], "autoload": { "files": [ "registration.php" @@ -26,3 +27,4 @@ } } } + diff --git a/app/code/Magento/Cookie/composer.json b/app/code/Magento/Cookie/composer.json index 6a5752792f7fb..597019ad62110 100644 --- a/app/code/Magento/Cookie/composer.json +++ b/app/code/Magento/Cookie/composer.json @@ -1,22 +1,23 @@ { "name": "magento/module-cookie", "description": "N/A", + "type": "magento2-module", + "license": [ + "OSL-3.0", + "AFL-3.0" + ], "config": { "sort-packages": true }, + "version": "100.4.4", "require": { "php": "~7.4.0||~8.1.0", - "magento/framework": "*", - "magento/module-store": "*" + "magento/framework": "103.0.*", + "magento/module-store": "101.1.*" }, "suggest": { - "magento/module-backend": "*" + "magento/module-backend": "102.0.*" }, - "type": "magento2-module", - "license": [ - "OSL-3.0", - "AFL-3.0" - ], "autoload": { "files": [ "registration.php" @@ -26,3 +27,4 @@ } } } + diff --git a/app/code/Magento/Cron/composer.json b/app/code/Magento/Cron/composer.json index 0468a95b457c0..6cc88e7583af0 100644 --- a/app/code/Magento/Cron/composer.json +++ b/app/code/Magento/Cron/composer.json @@ -1,22 +1,23 @@ { "name": "magento/module-cron", "description": "N/A", + "type": "magento2-module", + "license": [ + "OSL-3.0", + "AFL-3.0" + ], "config": { "sort-packages": true }, + "version": "100.4.4", "require": { "php": "~7.4.0||~8.1.0", - "magento/framework": "*", - "magento/module-store": "*" + "magento/framework": "103.0.*", + "magento/module-store": "101.1.*" }, "suggest": { - "magento/module-config": "*" + "magento/module-config": "101.2.*" }, - "type": "magento2-module", - "license": [ - "OSL-3.0", - "AFL-3.0" - ], "autoload": { "files": [ "registration.php" @@ -26,3 +27,4 @@ } } } + diff --git a/app/code/Magento/Csp/Block/Sri/Hashes.php b/app/code/Magento/Csp/Block/Sri/Hashes.php new file mode 100644 index 0000000000000..7b67ec4ca534f --- /dev/null +++ b/app/code/Magento/Csp/Block/Sri/Hashes.php @@ -0,0 +1,94 @@ +integrityRepositoryPool = $integrityRepositoryPool ?: ObjectManager::getInstance() + ->get(SubresourceIntegrityRepositoryPool::class); + + $this->serializer = $serializer ?: ObjectManager::getInstance() + ->get(SerializerInterface::class); + } + + /** + * Retrieves integrity hashes in serialized format. + * + * @throws LocalizedException + * + * @return string + */ + public function getSerialized(): string + { + $result = []; + + $baseUrl = $this->_urlBuilder->getBaseUrl( + ["_type" => UrlInterface::URL_TYPE_STATIC] + ); + + $integrityRepository = $this->integrityRepositoryPool->get( + Package::BASE_AREA + ); + + foreach ($integrityRepository->getAll() as $integrity) { + $url = $baseUrl . $integrity->getPath(); + + $result[$url] = $integrity->getHash(); + } + + $integrityRepository = $this->integrityRepositoryPool->get( + $this->_appState->getAreaCode() + ); + + foreach ($integrityRepository->getAll() as $integrity) { + $url = $baseUrl . $integrity->getPath(); + + $result[$url] = $integrity->getHash(); + } + + return $this->serializer->serialize($result); + } +} diff --git a/app/code/Magento/Csp/Helper/CspNonceProvider.php b/app/code/Magento/Csp/Helper/CspNonceProvider.php new file mode 100644 index 0000000000000..14cfc859b321b --- /dev/null +++ b/app/code/Magento/Csp/Helper/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/Helper/InlineUtil.php b/app/code/Magento/Csp/Helper/InlineUtil.php index f2a70fe465de3..be6ac32485fad 100644 --- a/app/code/Magento/Csp/Helper/InlineUtil.php +++ b/app/code/Magento/Csp/Helper/InlineUtil.php @@ -45,6 +45,14 @@ class InlineUtil implements InlineUtilInterface, SecurityProcessorInterface */ private $configCollector; + /** + * @var CspNonceProvider + */ + private CspNonceProvider $nonceProvider; + + /** + * @var array[] + */ private static $tagMeta = [ 'script' => ['id' => 'script-src', 'remote' => ['src'], 'hash' => true], 'style' => ['id' => 'style-src', 'remote' => [], 'hash' => true], @@ -67,17 +75,20 @@ class InlineUtil implements InlineUtilInterface, SecurityProcessorInterface * @param bool $useUnsafeHashes Use 'unsafe-hashes' policy (not supported by CSP v2). * @param HtmlRenderer|null $htmlRenderer * @param ConfigCollector|null $configCollector + * @param CspNonceProvider|null $nonceProvider */ public function __construct( DynamicCollector $dynamicCollector, bool $useUnsafeHashes = false, ?HtmlRenderer $htmlRenderer = null, - ?ConfigCollector $configCollector = null + ?ConfigCollector $configCollector = null, + ?CspNonceProvider $nonceProvider = null ) { $this->dynamicCollector = $dynamicCollector; $this->useUnsafeHashes = $useUnsafeHashes; $this->htmlRenderer = $htmlRenderer ?? ObjectManager::getInstance()->get(HtmlRenderer::class); $this->configCollector = $configCollector ?? ObjectManager::getInstance()->get(ConfigCollector::class); + $this->nonceProvider = $nonceProvider ?? ObjectManager::getInstance()->get(CspNonceProvider::class); } /** @@ -200,19 +211,34 @@ public function processTag(TagData $tagData): TagData && !empty(self::$tagMeta[$tagData->getTag()]['hash']) && $this->isInlineDisabled(self::$tagMeta[$tagData->getTag()]['id']) ) { - $this->dynamicCollector->add( - new FetchPolicy( - $policyId, - false, - [], - [], - false, - false, - false, - [], - $this->generateHashValue($tagData->getContent()) - ) - ); + /** create new tagData with a nonce */ + if ($tagData->getTag() === 'script') { + $nonce = $this->nonceProvider->generateNonce(); + $tagAttributes = $tagData->getAttributes(); + $tagAttributes['nonce'] = $nonce; + $newTagData = new TagData( + $tagData->getTag(), + $tagAttributes, + $tagData->getContent(), + $tagData->isTextContent() + ); + + $tagData = $newTagData; + } else { + $this->dynamicCollector->add( + new FetchPolicy( + $policyId, + false, + [], + [], + false, + false, + false, + [], + $this->generateHashValue($tagData->getContent()) + ) + ); + } } } diff --git a/app/code/Magento/Csp/Model/Collector/ConfigCollector.php b/app/code/Magento/Csp/Model/Collector/ConfigCollector.php index 34711fe5d8a22..1ae4badbc9348 100644 --- a/app/code/Magento/Csp/Model/Collector/ConfigCollector.php +++ b/app/code/Magento/Csp/Model/Collector/ConfigCollector.php @@ -11,6 +11,8 @@ use Magento\Csp\Model\Collector\Config\PolicyReaderPool; use Magento\Framework\App\Area; use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\App\ObjectManager; +use Magento\Framework\App\Request\Http; use Magento\Framework\App\State; use Magento\Store\Model\StoreManagerInterface; use Magento\Store\Model\ScopeInterface; @@ -40,22 +42,32 @@ class ConfigCollector implements PolicyCollectorInterface */ private $storeManager; + /** + * @var Http + */ + private Http $request; + /** * @param ScopeConfigInterface $config * @param PolicyReaderPool $readersPool * @param State $state * @param StoreManagerInterface $storeManager + * @param Http|null $request */ public function __construct( ScopeConfigInterface $config, PolicyReaderPool $readersPool, State $state, - StoreManagerInterface $storeManager + StoreManagerInterface $storeManager, + ?Http $request = null ) { $this->config = $config; $this->readersPool = $readersPool; $this->state = $state; $this->storeManager = $storeManager; + + $this->request = $request + ?? ObjectManager::getInstance()->get(Http::class); } /** @@ -74,11 +86,26 @@ public function collect(array $defaultPolicies = []): array } if ($configArea) { - $policiesConfig = $this->config->getValue( + $policiesConfigGlobal = $this->config->getValue( 'csp/policies/' . $configArea, ScopeInterface::SCOPE_STORE, $this->storeManager->getStore() ); + + $policiesConfigLocal = $this->config->getValue( + sprintf( + 'csp/policies/%s_%s', + $configArea, + $this->request->getFullActionName() + ), + ScopeInterface::SCOPE_STORE, + $this->storeManager->getStore() + ); + + $policiesConfig = is_array($policiesConfigLocal) ? + array_replace_recursive($policiesConfigGlobal, $policiesConfigLocal) : + $policiesConfigGlobal; + if (is_array($policiesConfig) && $policiesConfig) { foreach ($policiesConfig as $policyConfig) { $collected[] = $this->readersPool->getReader($policyConfig['policy_id']) diff --git a/app/code/Magento/Csp/Model/Deploy/Package/Processor/PostProcessor/Integrity.php b/app/code/Magento/Csp/Model/Deploy/Package/Processor/PostProcessor/Integrity.php new file mode 100644 index 0000000000000..41e81752632fb --- /dev/null +++ b/app/code/Magento/Csp/Model/Deploy/Package/Processor/PostProcessor/Integrity.php @@ -0,0 +1,89 @@ +filesystem = $filesystem; + $this->hashGenerator = $hashGenerator; + $this->integrityFactory = $integrityFactory; + $this->integrityCollector = $integrityCollector; + } + + /** + * @inheritdoc + */ + public function process(Package $package, array $options): bool + { + $staticDir = $this->filesystem->getDirectoryRead( + DirectoryList::ROOT + ); + + foreach ($package->getFiles() as $file) { + if ($file->getExtension() == "js") { + $integrity = $this->integrityFactory->create( + [ + "data" => [ + 'hash' => $this->hashGenerator->generate( + $staticDir->readFile($file->getSourcePath()) + ), + 'path' => $file->getDeployedFilePath() + ] + ] + ); + + $this->integrityCollector->collect($integrity); + } + } + + return true; + } +} diff --git a/app/code/Magento/Csp/Model/Deploy/Package/Processor/PostProcessor/Map.php b/app/code/Magento/Csp/Model/Deploy/Package/Processor/PostProcessor/Map.php new file mode 100644 index 0000000000000..4e97517a165c5 --- /dev/null +++ b/app/code/Magento/Csp/Model/Deploy/Package/Processor/PostProcessor/Map.php @@ -0,0 +1,139 @@ +minification = $minification; + $this->integrityFactory = $integrityFactory; + $this->hashGenerator = $hashGenerator; + $this->driver = $driver; + $this->integrityCollector = $integrityCollector; + $this->filesystem = $filesystem; + parent::__construct($deployStaticFile, $formatter, $packageFileFactory, $minification); + } + + /** + * @inheritdoc + * + * @throws FileSystemException + */ + public function process(Package $package, array $options): bool + { + parent::process($package, $options); + $fileName = $this->minification->addMinifiedSign(RepositoryMap::REQUIRE_JS_MAP_NAME); + $path = $package->getPath(); + $relativePath = $path . DIRECTORY_SEPARATOR . $fileName; + + if ($this->fileExists($relativePath)) { + $dir = $this->filesystem->getDirectoryWrite(DirectoryList::STATIC_VIEW); + $absolutePath = $dir->getAbsolutePath($relativePath); + $fileContent = $this->driver->fileGetContents($absolutePath); + + if ($fileContent) { + $integrity = $this->integrityFactory->create( + [ + "data" => [ + 'hash' => $this->hashGenerator->generate($fileContent), + 'path' => $relativePath + ] + ] + ); + $this->integrityCollector->collect($integrity); + } + } + return true; + } + + /** + * Check if file exist + * + * @param string $path + * @return bool + * @throws FileSystemException + */ + private function fileExists(string $path): bool + { + $dir = $this->filesystem->getDirectoryWrite(DirectoryList::STATIC_VIEW); + return $dir->isExist($path); + } +} diff --git a/app/code/Magento/Csp/Model/Mode/ConfigManager.php b/app/code/Magento/Csp/Model/Mode/ConfigManager.php index 9f10154604d5f..bc31a41982549 100644 --- a/app/code/Magento/Csp/Model/Mode/ConfigManager.php +++ b/app/code/Magento/Csp/Model/Mode/ConfigManager.php @@ -7,6 +7,8 @@ namespace Magento\Csp\Model\Mode; +use Magento\Framework\App\Request\Http; +use Magento\Framework\App\ObjectManager; use Magento\Csp\Api\Data\ModeConfiguredInterface; use Magento\Csp\Api\ModeConfigManagerInterface; use Magento\Csp\Model\Mode\Data\ModeConfigured; @@ -36,16 +38,29 @@ class ConfigManager implements ModeConfigManagerInterface */ private $state; + /** + * @var Http + */ + private Http $request; + /** * @param ScopeConfigInterface $config * @param Store $store * @param State $state + * @param Http|null $request */ - public function __construct(ScopeConfigInterface $config, Store $store, State $state) - { + public function __construct( + ScopeConfigInterface $config, + Store $store, + State $state, + ?Http $request = null + ) { $this->config = $config; $this->storeModel = $store; $this->state = $state; + + $this->request = $request + ?? ObjectManager::getInstance()->get(Http::class); } /** @@ -54,25 +69,58 @@ public function __construct(ScopeConfigInterface $config, Store $store, State $s public function getConfigured(): ModeConfiguredInterface { $area = $this->state->getAreaCode(); + if ($area === Area::AREA_ADMINHTML) { $configArea = 'admin'; } elseif ($area === Area::AREA_FRONTEND) { $configArea = 'storefront'; } else { - throw new \RuntimeException('CSP can only be configured for storefront or admin area'); + throw new \RuntimeException( + 'CSP can only be configured for storefront or admin area' + ); } - $reportOnly = $this->config->isSetFlag( - 'csp/mode/' . $configArea .'/report_only', + $reportOnly = $this->config->getValue( + sprintf( + 'csp/mode/%s_%s/report_only', + $configArea, + $this->request->getFullActionName() + ), ScopeInterface::SCOPE_STORE, $this->storeModel->getStore() ); + + if ($reportOnly === null) { + // Fallback to default configuration. + $reportOnly = $this->config->getValue( + 'csp/mode/' . $configArea .'/report_only', + ScopeInterface::SCOPE_STORE, + $this->storeModel->getStore() + ); + } + $reportUri = $this->config->getValue( - 'csp/mode/' . $configArea .'/report_uri', + sprintf( + 'csp/mode/%s_%s/report_uri', + $configArea, + $this->request->getFullActionName() + ), ScopeInterface::SCOPE_STORE, $this->storeModel->getStore() ); - return new ModeConfigured($reportOnly, !empty($reportUri) ? $reportUri : null); + if (empty($reportUri)) { + // Fallback to default configuration. + $reportUri = $this->config->getValue( + 'csp/mode/' . $configArea .'/report_uri', + ScopeInterface::SCOPE_STORE, + $this->storeModel->getStore() + ); + } + + return new ModeConfigured( + (bool) $reportOnly, + !empty($reportUri) ? $reportUri : null + ); } } diff --git a/app/code/Magento/Csp/Model/SubresourceIntegrity.php b/app/code/Magento/Csp/Model/SubresourceIntegrity.php new file mode 100644 index 0000000000000..0a2824c93027e --- /dev/null +++ b/app/code/Magento/Csp/Model/SubresourceIntegrity.php @@ -0,0 +1,34 @@ +getData("path"); + } + + /** + * Gets an integrity hash. + * + * @return string|null + */ + public function getHash(): ?string + { + return $this->getData("hash"); + } +} diff --git a/app/code/Magento/Csp/Model/SubresourceIntegrity/HashGenerator.php b/app/code/Magento/Csp/Model/SubresourceIntegrity/HashGenerator.php new file mode 100644 index 0000000000000..be78a1a0c66b8 --- /dev/null +++ b/app/code/Magento/Csp/Model/SubresourceIntegrity/HashGenerator.php @@ -0,0 +1,37 @@ +data = $data; + } + + /** + * Collects given Integrity object. + * + * @param SubresourceIntegrity $integrity + * + * @return void + */ + public function collect(SubresourceIntegrity $integrity): void + { + $this->data[] = $integrity; + } + + /** + * Provides all collected Integrity objects. + * + * @return SubresourceIntegrity[] + */ + public function release(): array + { + return $this->data; + } +} diff --git a/app/code/Magento/Csp/Model/SubresourceIntegrityRepository.php b/app/code/Magento/Csp/Model/SubresourceIntegrityRepository.php new file mode 100644 index 0000000000000..7cc83466ab078 --- /dev/null +++ b/app/code/Magento/Csp/Model/SubresourceIntegrityRepository.php @@ -0,0 +1,211 @@ +cache = $cache; + $this->serializer = $serializer; + $this->integrityFactory = $integrityFactory; + $this->context = $context; + } + + /** + * Gets an Integrity object by path. + * + * @param string $path + * + * @return SubresourceIntegrity|null + */ + public function getByPath(string $path): ?SubresourceIntegrity + { + $data = $this->getData(); + + if (isset($data[$path])) { + return $this->integrityFactory->create( + [ + "data" => [ + "path" => $path, + "hash" => $data[$path] + ] + ] + ); + } + + return null; + } + + /** + * Gets all available Integrity objects. + * + * @return SubresourceIntegrity[] + */ + public function getAll(): array + { + $result = []; + + foreach ($this->getData() as $path => $hash) { + $result[] = $this->integrityFactory->create( + [ + "data" => [ + "path" => $path, + "hash" => $hash + ] + ] + ); + } + + return $result; + } + + /** + * Saves Integrity object. + * + * @param SubresourceIntegrity $integrity + * + * @return bool + */ + public function save(SubresourceIntegrity $integrity): bool + { + $data = $this->getData(); + + $data[$integrity->getPath()] = $integrity->getHash(); + + $this->data = $data; + + return $this->cache->save( + $this->serializer->serialize($this->data), + $this->getCacheKey(), + [self::CACHE_PREFIX] + ); + } + + /** + * Saves a bunch of Integrity objects. + * + * @param SubresourceIntegrity[] $bunch + * + * @return bool + */ + public function saveBunch(array $bunch): bool + { + $data = $this->getData(); + + foreach ($bunch as $integrity) { + $data[$integrity->getPath()] = $integrity->getHash(); + } + + $this->data = $data; + + return $this->cache->save( + $this->serializer->serialize($this->data), + $this->getCacheKey(), + [self::CACHE_PREFIX] + ); + } + + /** + * Deletes all Integrity objects. + * + * @return bool + */ + public function deleteAll(): bool + { + $this->data = null; + + return $this->cache->remove( + $this->getCacheKey() + ); + } + + /** + * Loads integrity data from a storage. + * + * @return array + */ + private function getData(): array + { + if ($this->data === null) { + $cache = $this->cache->load($this->getCacheKey()); + + $this->data = $cache ? $this->serializer->unserialize($cache) : []; + } + + return $this->data; + } + + /** + * Gets a cache key based on current context. + * + * @return string + */ + private function getCacheKey(): string + { + $cacheKey = self::CACHE_PREFIX; + + if ($this->context) { + $cacheKey .= "_" . $this->context; + } + + return $cacheKey; + } +} diff --git a/app/code/Magento/Csp/Model/SubresourceIntegrityRepositoryPool.php b/app/code/Magento/Csp/Model/SubresourceIntegrityRepositoryPool.php new file mode 100644 index 0000000000000..5ad9c6c676de8 --- /dev/null +++ b/app/code/Magento/Csp/Model/SubresourceIntegrityRepositoryPool.php @@ -0,0 +1,53 @@ +integrityRepositoryFactory = $integrityRepositoryFactory; + } + + /** + * Gets subresource integrity repository by given context. + * + * @param string $context + * + * @return SubresourceIntegrityRepository + */ + public function get(string $context): SubresourceIntegrityRepository + { + if (!isset($this->repositories[$context])) { + $this->repositories[$context] = $this->integrityRepositoryFactory->create( + [ + "context" => $context + ] + ); + } + + return $this->repositories[$context]; + } +} diff --git a/app/code/Magento/Csp/Plugin/AddDefaultPropertiesToGroupPlugin.php b/app/code/Magento/Csp/Plugin/AddDefaultPropertiesToGroupPlugin.php new file mode 100644 index 0000000000000..159e2180427a5 --- /dev/null +++ b/app/code/Magento/Csp/Plugin/AddDefaultPropertiesToGroupPlugin.php @@ -0,0 +1,81 @@ +state = $state; + $this->integrityRepositoryPool = $integrityRepositoryPool; + } + + /** + * Before Plugin to add Properties to JS assets + * + * @param GroupedCollection $subject + * @param AssetInterface $asset + * @param array $properties + * @return array + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function beforeGetFilteredProperties( + GroupedCollection $subject, + AssetInterface $asset, + array $properties = [] + ): array { + if ($asset instanceof LocalInterface) { + $integrityRepository = $this->integrityRepositoryPool->get( + Package::BASE_AREA + ); + + $integrity = $integrityRepository->getByPath($asset->getPath()); + + if (!$integrity) { + $integrityRepository = $this->integrityRepositoryPool->get( + $this->state->getAreaCode() + ); + + $integrity = $integrityRepository->getByPath($asset->getPath()); + } + + if ($integrity && $integrity->getHash()) { + $properties['attributes']['integrity'] = $integrity->getHash(); + $properties['attributes']['crossorigin'] = 'anonymous'; + } + } + + return [$asset, $properties]; + } +} diff --git a/app/code/Magento/Csp/Plugin/GenerateAssetIntegrity.php b/app/code/Magento/Csp/Plugin/GenerateAssetIntegrity.php new file mode 100644 index 0000000000000..2b461588fa5ad --- /dev/null +++ b/app/code/Magento/Csp/Plugin/GenerateAssetIntegrity.php @@ -0,0 +1,91 @@ +hashGenerator = $hashGenerator; + $this->integrityFactory = $integrityFactory; + $this->integrityCollector = $integrityCollector; + } + + /** + * Generates integrity for RequireJs config. + * + * @param FileManager $subject + * @param File $result + * + * @return File + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function afterCreateRequireJsConfigAsset( + FileManager $subject, + File $result + ): File { + if (PHP_SAPI == 'cli') { + if (in_array($result->getContentType(), self::CONTENT_TYPES)) { + $integrity = $this->integrityFactory->create( + [ + "data" => [ + 'hash' => $this->hashGenerator->generate( + $result->getContent() + ), + 'path' => $result->getPath() + ] + ] + ); + + $this->integrityCollector->collect($integrity); + } + } + + return $result; + } +} diff --git a/app/code/Magento/Csp/Plugin/RemoveAllAssetIntegrityHashes.php b/app/code/Magento/Csp/Plugin/RemoveAllAssetIntegrityHashes.php new file mode 100644 index 0000000000000..85b97e9091c54 --- /dev/null +++ b/app/code/Magento/Csp/Plugin/RemoveAllAssetIntegrityHashes.php @@ -0,0 +1,69 @@ +integrityRepositoryPool = $integrityRepositoryPool; + } + + /** + * Removes existing integrity hashes before static content deploy + * + * @param DeployStaticContent $subject + * @param array $options + * + * @return void + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function beforeDeploy( + DeployStaticContent $subject, + array $options + ): void { + if (PHP_SAPI == 'cli' && !$this->isRefreshContentVersionOnly($options)) { + foreach ([Package::BASE_AREA, Area::AREA_FRONTEND, Area::AREA_ADMINHTML] as $area) { + $this->integrityRepositoryPool->get($area) + ->deleteAll(); + } + } + } + + /** + * Checks if only version refresh is requested. + * + * @param array $options + * + * @return bool + */ + private function isRefreshContentVersionOnly(array $options): bool + { + return isset($options[DeployStaticOptions::REFRESH_CONTENT_VERSION_ONLY]) + && $options[DeployStaticOptions::REFRESH_CONTENT_VERSION_ONLY]; + } +} diff --git a/app/code/Magento/Csp/Plugin/StoreAssetIntegrityHashes.php b/app/code/Magento/Csp/Plugin/StoreAssetIntegrityHashes.php new file mode 100644 index 0000000000000..e5674dd3a78ef --- /dev/null +++ b/app/code/Magento/Csp/Plugin/StoreAssetIntegrityHashes.php @@ -0,0 +1,70 @@ +integrityCollector = $integrityCollector; + $this->integrityRepositoryPool = $integrityRepositoryPool; + } + + /** + * Stores generated integrity hashes after static content deploy + * + * @param DeployStaticContent $subject + * @param mixed $result + * @param array $options + * + * @return void + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function afterDeploy( + DeployStaticContent $subject, + $result, + array $options + ): void { + $bunches = []; + + foreach ($this->integrityCollector->release() as $integrity) { + $area = explode("/", $integrity->getPath())[0]; + + $bunches[$area][] = $integrity; + } + + foreach ($bunches as $area => $bunch) { + $this->integrityRepositoryPool->get($area) + ->saveBunch($bunch); + } + } +} diff --git a/app/code/Magento/Csp/Test/Unit/Model/Mode/ConfigManagerTest.php b/app/code/Magento/Csp/Test/Unit/Model/Mode/ConfigManagerTest.php index 3660e063387ae..9fff6efa407ec 100644 --- a/app/code/Magento/Csp/Test/Unit/Model/Mode/ConfigManagerTest.php +++ b/app/code/Magento/Csp/Test/Unit/Model/Mode/ConfigManagerTest.php @@ -12,8 +12,10 @@ use Magento\Csp\Model\Mode\Data\ModeConfigured; use Magento\Framework\App\Area; use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\App\Request\Http; use Magento\Framework\App\State; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Store\Model\ScopeInterface; use Magento\Store\Model\Store; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; @@ -44,6 +46,11 @@ class ConfigManagerTest extends TestCase */ private $stateMock; + /** + * @var Http|MockObject + */ + private $requestMock; + /** * Set Up */ @@ -54,13 +61,15 @@ protected function setUp(): void $this->scopeConfigMock = $this->getMockForAbstractClass(ScopeConfigInterface::class); $this->storeMock = $this->createMock(Store::class); $this->stateMock = $this->createMock(State::class); + $this->requestMock = $this->createMock(Http::class); $this->model = $objectManager->getObject( ConfigManager::class, [ 'config' => $this->scopeConfigMock, 'storeModel' => $this->storeMock, - 'state' => $this->stateMock + 'state' => $this->stateMock, + 'request' => $this->requestMock, ] ); } @@ -102,4 +111,73 @@ public function testConfiguredCSPForAdminArea() $this->assertInstanceOf(ModeConfigured::class, $result); } + + /** + * Test storefront checkout page CSP config. + * + * @return void + * + */ + public function testCheckoutPageReportOnly(): void + { + $this->requestMock->expects($this->exactly(2)) + ->method('getFullActionName') + ->willReturn('checkout_index_index'); + + $this->stateMock->expects($this->once()) + ->method('getAreaCode') + ->willReturn(Area::AREA_FRONTEND); + + $matcher = $this->exactly(2); + $this->scopeConfigMock->expects($matcher) + ->method('getValue') + ->willReturnCallback(function () use ($matcher) { + return match ($matcher->getInvocationCount()) { + 1 => ['csp/mode/checkout_index_index/report_only', ScopeInterface::SCOPE_STORE, null], + 2 => ['csp/mode/checkout_index_index/report_uri', ScopeInterface::SCOPE_STORE, null], + }; + }) + ->willReturnOnConsecutiveCalls(true, 'testReportUri'); + + $result = $this->model->getConfigured(); + + $this->assertInstanceOf(ModeConfigured::class, $result); + $this->assertTrue($result->isReportOnly()); + $this->assertEquals($result->getReportUri(), 'testReportUri'); + } + + /** + * Test non checkout page CSP config. + * + * @return void + */ + public function testNonCheckoutPageReportOnly(): void + { + $this->requestMock->expects($this->exactly(2)) + ->method('getFullActionName') + ->willReturn('dashboard_index_index'); + + $this->stateMock->expects($this->once()) + ->method('getAreaCode') + ->willReturn(Area::AREA_ADMINHTML); + + $matcher = $this->exactly(4); + $this->scopeConfigMock->expects($matcher) + ->method('getValue') + ->willReturnCallback(function () use ($matcher) { + return match ($matcher->getInvocationCount()) { + 1 => ['csp/mode/dashboard_index_index/report_only', ScopeInterface::SCOPE_STORE, null], + 2 => ['csp/mode/admin/report_only', ScopeInterface::SCOPE_STORE, null], + 3 => ['csp/mode/dashboard_index_index/report_uri', ScopeInterface::SCOPE_STORE, null], + 4 => ['csp/mode/admin/report_uri', ScopeInterface::SCOPE_STORE, null], + }; + }) + ->willReturnOnConsecutiveCalls(null, true, null, 'testPageReportUri'); + + $result = $this->model->getConfigured(); + + $this->assertInstanceOf(ModeConfigured::class, $result); + $this->assertTrue($result->isReportOnly()); + $this->assertEquals($result->getReportUri(), 'testPageReportUri'); + } } diff --git a/app/code/Magento/Csp/Test/Unit/Model/SubresourceIntegrityRepositoryTest.php b/app/code/Magento/Csp/Test/Unit/Model/SubresourceIntegrityRepositoryTest.php new file mode 100644 index 0000000000000..bc75d35f8fd9c --- /dev/null +++ b/app/code/Magento/Csp/Test/Unit/Model/SubresourceIntegrityRepositoryTest.php @@ -0,0 +1,130 @@ +cacheMock = $this->getMockBuilder(CacheInterface::class) + ->disableOriginalConstructor() + ->onlyMethods(['save', 'load']) + ->getMockForAbstractClass(); + $this->serializerMock = $this->getMockBuilder(SerializerInterface::class) + ->disableOriginalConstructor() + ->onlyMethods(['serialize', 'unserialize']) + ->getMockForAbstractClass(); + $this->integrityFactoryMock = $this->getMockBuilder(SubresourceIntegrityFactory::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->subresourceIntegrityRepository = new SubresourceIntegrityRepository( + $this->cacheMock, + $this->serializerMock, + $this->integrityFactoryMock + ); + } + + /** Test save repository + * + * + * @return void + */ + public function testSave(): void + { + $data = new SubresourceIntegrity( + [ + 'hash' => 'testhash', + 'path' => 'js/jquery.js' + ] + ); + + $expected[$data->getPath()] = $data->getHash(); + $serialized = json_encode($expected); + $this->cacheMock->expects($this->once())->method('load')->willReturn(false); + $this->serializerMock->expects($this->once())->method('serialize')->with($expected)->willReturn($serialized); + $this->cacheMock->expects($this->once())->method('save')->willReturn(true); + $this->assertTrue($this->subresourceIntegrityRepository->save($data)); + } + + /** Test that cache saves in bunch + * + * + * @return void + */ + public function testSaveBunch(): void + { + $bunch1 = new SubresourceIntegrity( + [ + 'hash' => 'testhash', + 'path' => 'js/jquery.js' + ] + ); + + $bunch2 = new SubresourceIntegrity( + [ + 'hash' => 'testhash2', + 'path' => 'js/test.js' + ] + ); + + $bunches = [$bunch1, $bunch2]; + + $expected = []; + + foreach ($bunches as $bunch) { + $expected[$bunch->getPath()] = $bunch->getHash(); + } + $serializedBunch = json_encode($expected); + $this->cacheMock->expects($this->once())->method('load')->willReturn(false); + $this->serializerMock->expects($this->once())->method('serialize') + ->with($expected)->willReturn($serializedBunch); + $this->cacheMock->expects($this->once())->method('save')->willReturn(true); + $this->assertTrue($this->subresourceIntegrityRepository->saveBunch($bunches)); + } +} diff --git a/app/code/Magento/Csp/Test/Unit/Plugin/AddDefaultPropertiesToGroupPluginTest.php b/app/code/Magento/Csp/Test/Unit/Plugin/AddDefaultPropertiesToGroupPluginTest.php new file mode 100644 index 0000000000000..3e6b08f17bb7b --- /dev/null +++ b/app/code/Magento/Csp/Test/Unit/Plugin/AddDefaultPropertiesToGroupPluginTest.php @@ -0,0 +1,113 @@ +integrityRepositoryPoolMock = $this->getMockBuilder(SubresourceIntegrityRepositoryPool::class) + ->disableOriginalConstructor() + ->onlyMethods(['get']) + ->getMock(); + $this->assetInterfaceMock = $this->getMockBuilder(File::class) + ->disableOriginalConstructor() + ->onlyMethods(['getPath']) + ->getMockForAbstractClass(); + $this->stateMock = $this->getMockBuilder(State::class) + ->disableOriginalConstructor() + ->onlyMethods(['getAreaCode']) + ->getMock(); + $this->plugin = new AddDefaultPropertiesToGroupPlugin( + $this->stateMock, + $this->integrityRepositoryPoolMock + ); + } + + /** + * Test for plugin with Js assets + * + * @return void + */ + public function testBeforeGetFilteredProperties(): void + { + $integrityRepositoryMock = $this->getMockBuilder(SubresourceIntegrityRepository::class) + ->disableOriginalConstructor() + ->onlyMethods(['getByPath']) + ->getMock(); + $groupedCollectionMock = $this->getMockBuilder(GroupedCollection::class) + ->disableOriginalConstructor() + ->getMock(); + $path = 'jquery.js'; + $area = 'base'; + + $data = new SubresourceIntegrity( + [ + 'hash' => 'testhash', + 'path' => $path + ] + ); + $properties['attributes']['integrity'] = $data->getHash(); + $properties['attributes']['crossorigin'] = 'anonymous'; + $expected = [$this->assetInterfaceMock, $properties]; + $this->integrityRepositoryPoolMock->expects($this->once())->method('get')->with($area) + ->willReturn( + $integrityRepositoryMock + ); + $this->assetInterfaceMock->expects($this->once())->method('getPath')->willReturn($path); + $integrityRepositoryMock->expects($this->once())->method('getByPath')->with($path)->willReturn($data); + $this->assertEquals( + $expected, + $this->plugin->beforeGetFilteredProperties( + $groupedCollectionMock, + $this->assetInterfaceMock + ) + ); + } +} diff --git a/app/code/Magento/Csp/Test/Unit/Plugin/StoreAssetIntegrityHashesTest.php b/app/code/Magento/Csp/Test/Unit/Plugin/StoreAssetIntegrityHashesTest.php new file mode 100644 index 0000000000000..7ac1d462015c7 --- /dev/null +++ b/app/code/Magento/Csp/Test/Unit/Plugin/StoreAssetIntegrityHashesTest.php @@ -0,0 +1,96 @@ +integrityRepositoryPoolMock = $this->getMockBuilder(SubresourceIntegrityRepositoryPool::class) + ->disableOriginalConstructor() + ->onlyMethods(['get']) + ->getMock(); + $this->integrityCollectorMock = $this->getMockBuilder(SubresourceIntegrityCollector::class) + ->disableOriginalConstructor() + ->onlyMethods(['release']) + ->getMock(); + $this->plugin = new StoreAssetIntegrityHashes( + $this->integrityCollectorMock, + $this->integrityRepositoryPoolMock, + ); + } + + /** + * Test After Deploy method of plugin + * + * @return void + * @doesNotPerformAssertions + */ + public function testAfterDeploy(): void + { + $bunch1 = new SubresourceIntegrity( + [ + 'hash' => 'testhash', + 'path' => 'adminhtml/js/jquery.js' + ] + ); + + $bunch2 = new SubresourceIntegrity( + [ + 'hash' => 'testhash2', + 'path' => 'frontend/js/test.js' + ] + ); + + $bunches = [$bunch1, $bunch2]; + $deployStaticContentMock = $this->getMockBuilder(DeployStaticContent::class) + ->disableOriginalConstructor() + ->getMock(); + $subResourceIntegrityMock = $this->getMockBuilder(SubresourceIntegrityRepository::class) + ->disableOriginalConstructor() + ->onlyMethods(['saveBunch']) + ->getMock(); + $this->integrityCollectorMock->expects($this->once())->method('release')->willReturn($bunches); + $this->integrityRepositoryPoolMock->expects($this->any())->method('get')->willReturn($subResourceIntegrityMock); + $subResourceIntegrityMock->expects($this->any())->method('saveBunch')->willReturn(true); + $this->plugin->afterDeploy($deployStaticContentMock, null, []); + } +} diff --git a/app/code/Magento/Csp/composer.json b/app/code/Magento/Csp/composer.json index 2079a30d92068..2b4d209abeb2e 100644 --- a/app/code/Magento/Csp/composer.json +++ b/app/code/Magento/Csp/composer.json @@ -1,19 +1,22 @@ { "name": "magento/module-csp", "description": "CSP module enables Content Security Policies for Magento", + "type": "magento2-module", + "license": [ + "OSL-3.0", + "AFL-3.0" + ], "config": { "sort-packages": true }, + "version": "100.4.3-p9", "require": { "php": "~7.4.0||~8.1.0", - "magento/framework": "*", - "magento/module-store": "*" + "magento/framework": "103.0.*", + "magento/module-store": "101.1.*", + "magento/module-require-js": "100.4.*", + "magento/module-deploy": "100.4.*" }, - "type": "magento2-module", - "license": [ - "OSL-3.0", - "AFL-3.0" - ], "autoload": { "files": [ "registration.php" @@ -23,3 +26,4 @@ } } } + diff --git a/app/code/Magento/Csp/etc/acl.xml b/app/code/Magento/Csp/etc/acl.xml new file mode 100644 index 0000000000000..da62f053ab4a1 --- /dev/null +++ b/app/code/Magento/Csp/etc/acl.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + diff --git a/app/code/Magento/Csp/etc/adminhtml/system.xml b/app/code/Magento/Csp/etc/adminhtml/system.xml new file mode 100644 index 0000000000000..38b2a6834bdb3 --- /dev/null +++ b/app/code/Magento/Csp/etc/adminhtml/system.xml @@ -0,0 +1,35 @@ + + + + +
+ + security + Magento_Csp::config + + + + + + + URI to report CSP violations on storefront. Used for all storefront pages that don't have own URI configured above. + validate-url + + + + + + + URI to report CSP violations in admin area. Used for all admin pages that don't have own URI configured above. + validate-url + + + +
+
+
diff --git a/app/code/Magento/Csp/etc/di.xml b/app/code/Magento/Csp/etc/di.xml index 5e90c4b0c866c..79c688f8cf0b0 100644 --- a/app/code/Magento/Csp/etc/di.xml +++ b/app/code/Magento/Csp/etc/di.xml @@ -110,4 +110,27 @@ Magento\Csp\Model\BlockCache + + + + Magento\Csp\Model\Deploy\Package\Processor\PostProcessor\Integrity + + + + + + + + + + + + + + + + + Magento\Framework\Filesystem\Driver\File + + diff --git a/app/code/Magento/Csp/view/adminhtml/layout/sales_order_create_index.xml b/app/code/Magento/Csp/view/adminhtml/layout/sales_order_create_index.xml new file mode 100644 index 0000000000000..73fe99913466c --- /dev/null +++ b/app/code/Magento/Csp/view/adminhtml/layout/sales_order_create_index.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + diff --git a/app/code/Magento/Csp/view/base/templates/sri/hashes.phtml b/app/code/Magento/Csp/view/base/templates/sri/hashes.phtml new file mode 100644 index 0000000000000..e9d666a50550d --- /dev/null +++ b/app/code/Magento/Csp/view/base/templates/sri/hashes.phtml @@ -0,0 +1,18 @@ + + +getSerialized(); +$scriptString = <<', $content ); - $this->assertStringContainsString("", $content); + $this->assertStringContainsString("\n let myVar = 1;\n", $content); $header = $this->getResponse()->getHeader('Content-Security-Policy'); $this->assertNotEmpty($header); $this->assertStringContainsString('http://my.magento.com', $header->getFieldValue()); - $this->assertStringContainsString('\'sha256-H4RRnauTM2X2Xg/z9zkno1crqhsaY3uKKu97uwmnXXE=\'', $header->getFieldValue()); } } diff --git a/dev/tests/integration/testsuite/Magento/Csp/Helper/CspNonceProviderMock.php b/dev/tests/integration/testsuite/Magento/Csp/Helper/CspNonceProviderMock.php new file mode 100644 index 0000000000000..75ec9abbe1770 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Csp/Helper/CspNonceProviderMock.php @@ -0,0 +1,68 @@ +random = $random; + $this->dynamicCollector = $dynamicCollector; + } + + /** + * Generate nonce and add it to the CSP header + * + * @return string + */ + public function generateNonce(): string + { + $cspNonce = 'nonce-1234567890abcdef'; + + $policy = new FetchPolicy( + 'script-src', + false, + [], + [], + false, + false, + false, + [$cspNonce], + [] + ); + + $this->dynamicCollector->add($policy); + + return $cspNonce; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Csp/Helper/InlineUtilTest.php b/dev/tests/integration/testsuite/Magento/Csp/Helper/InlineUtilTest.php index ab1b702ab1a00..89ac8ab50e68f 100644 --- a/dev/tests/integration/testsuite/Magento/Csp/Helper/InlineUtilTest.php +++ b/dev/tests/integration/testsuite/Magento/Csp/Helper/InlineUtilTest.php @@ -44,7 +44,8 @@ public function setUp(): void { Bootstrap::getObjectManager()->configure([ 'preferences' => [ - DynamicCollector::class => DynamicCollectorMock::class + DynamicCollector::class => DynamicCollectorMock::class, + CspNonceProvider::class => CspNonceProviderMock::class ] ]); $this->util = Bootstrap::getObjectManager()->get(InlineUtil::class); @@ -116,7 +117,7 @@ public function getTags(): array 'script', ['type' => 'text/javascript'], "\n let someVar = 25;\n document.getElementById('test').innerText = someVar;\n", - "", [ new FetchPolicy( @@ -127,8 +128,8 @@ public function getTags(): array false, false, false, - [], - ['U+SKpEef030N2YgyKKdIBIvPy8Fmd42N/JcTZgQV+DA=' => 'sha256'] + ['nonce-1234567890abcdef'], + [] ) ] ], diff --git a/dev/tests/integration/testsuite/Magento/Csp/Model/Collector/ConfigCollectorTest.php b/dev/tests/integration/testsuite/Magento/Csp/Model/Collector/ConfigCollectorTest.php index cf6287ed5b4e1..e8e1365489abb 100644 --- a/dev/tests/integration/testsuite/Magento/Csp/Model/Collector/ConfigCollectorTest.php +++ b/dev/tests/integration/testsuite/Magento/Csp/Model/Collector/ConfigCollectorTest.php @@ -12,6 +12,7 @@ use Magento\Csp\Model\Policy\FlagPolicy; use Magento\Csp\Model\Policy\PluginTypesPolicy; use Magento\Csp\Model\Policy\SandboxPolicy; +use Magento\Framework\App\RequestInterface; use PHPUnit\Framework\TestCase; use Magento\TestFramework\Helper\Bootstrap; @@ -25,12 +26,18 @@ class ConfigCollectorTest extends TestCase */ private $collector; + /** + * @var RequestInterface + */ + private $request; + /** * @inheritDoc */ protected function setUp(): void { $this->collector = Bootstrap::getObjectManager()->get(ConfigCollector::class); + $this->request = Bootstrap::getObjectManager()->get(RequestInterface::class); } /** @@ -68,7 +75,7 @@ private function getExpectedPolicies(): array 'manifest-src' => new FetchPolicy('manifest-src', false, [], [], true), 'media-src' => new FetchPolicy('media-src', false, [], [], true), 'object-src' => new FetchPolicy('object-src', false, [], [], true), - 'script-src' => new FetchPolicy('script-src', false, [], [], true, false, false, [], [], false, true), + 'script-src' => new FetchPolicy('script-src', false, [], [], true, true, true, [], [], false, true), 'style-src' => new FetchPolicy('style-src', false, [], [], true), 'base-uri' => new FetchPolicy('base-uri', false, [], [], true), 'plugin-types' => new PluginTypesPolicy( @@ -172,11 +179,18 @@ private function getExpectedPolicies(): array * @magentoConfigFixture default_store csp/policies/storefront/mixed_content/policy_id block-all-mixed-content * @magentoConfigFixture default_store csp/policies/storefront/base/policy_id base-uri * @magentoConfigFixture default_store csp/policies/storefront/base/inline 0 + * @magentoConfigFixture default_store csp/policies/storefront_checkout_index_index/scripts/policy_id script-src + * @magentoConfigFixture default_store csp/policies/storefront_checkout_index_index/scripts/self 1 + * @magentoConfigFixture default_store csp/policies/storefront_checkout_index_index/scripts/inline 1 + * @magentoConfigFixture default_store csp/policies/storefront_checkout_index_index/scripts/eval 1 * @magentoConfigFixture default_store csp/policies/storefront/upgrade/policy_id upgrade-insecure-requests * @return void */ public function testCollecting(): void { + $this->request->setRouteName('checkout'); + $this->request->setControllerName('index'); + $this->request->setActionName('index'); $policies = $this->collector->collect([new FlagPolicy('upgrade-insecure-requests')]); $expectedPolicies = $this->getExpectedPolicies(); $this->assertNotEmpty($policies); diff --git a/dev/tests/integration/testsuite/Magento/Customer/Model/AccountManagement/CreateAccountTest.php b/dev/tests/integration/testsuite/Magento/Customer/Model/AccountManagement/CreateAccountTest.php index bd2c26e449d72..8b1f393f00ee4 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Model/AccountManagement/CreateAccountTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Model/AccountManagement/CreateAccountTest.php @@ -421,6 +421,8 @@ public function testCreateNewCustomerWithPasswordHashWithNotAllowedCountry(): vo $customerData = $this->customerRepository->getById($customerId); $customerData->getAddresses()[1]->setRegion(null)->setCountryId($allowedCountryIdForSecondWebsite) ->setRegionId(null); + $customerData->getAddresses()[1]->setIsDefaultBilling(true); + $customerData->getAddresses()[1]->setIsDefaultShipping(true); $customerData->setStoreId($store->getId())->setWebsiteId($store->getWebsiteId())->setId(null); $password = $this->random->getRandomString(8); $passwordHash = $this->encryptor->getHash($password, true); diff --git a/dev/tests/integration/testsuite/Magento/Customer/Model/AccountManagementTest.php b/dev/tests/integration/testsuite/Magento/Customer/Model/AccountManagementTest.php index de4a592f46c9e..b7c2dea1b5e02 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Model/AccountManagementTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Model/AccountManagementTest.php @@ -9,6 +9,7 @@ use Magento\Customer\Api\AccountManagementInterface; use Magento\Customer\Api\AddressRepositoryInterface; use Magento\Customer\Api\Data\AddressInterface; +use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\Exception\InputException; use Magento\Framework\Exception\NoSuchEntityException; use Magento\Framework\Exception\State\ExpiredException; @@ -16,6 +17,7 @@ use Magento\Framework\Session\SessionManagerInterface; use Magento\Framework\Stdlib\DateTime; use Magento\Framework\Url as UrlBuilder; +use Magento\Store\Model\ScopeInterface; use Magento\Store\Model\StoreManagerInterface; use Magento\TestFramework\Helper\Bootstrap; @@ -331,7 +333,7 @@ public function testValidateResetPasswordLinkTokenExpired() public function testValidateResetPasswordLinkTokenInvalid() { $resetToken = 'lsdj579slkj5987slkj595lkj'; - $invalidToken = 0; + $invalidToken = '0'; $this->setResetPasswordData($resetToken, 'Y-m-d H:i:s'); try { $this->accountManagement->validateResetPasswordLinkToken(1, $invalidToken); @@ -481,7 +483,7 @@ public function testResetPasswordTokenExpired() public function testResetPasswordTokenInvalid() { $resetToken = 'lsdj579slkj5987slkj595lkj'; - $invalidToken = 0; + $invalidToken = '0'; $password = 'new_Password123'; $this->setResetPasswordData($resetToken, 'Y-m-d H:i:s'); @@ -604,7 +606,18 @@ public function testResendConfirmationNotNeeded() */ public function testIsEmailAvailable() { - $this->assertFalse($this->accountManagement->isEmailAvailable('customer@example.com', 1)); + $scopeConfig = $this->objectManager->get(ScopeConfigInterface::class); + $guestLoginConfig = $scopeConfig->getValue( + AccountManagement::GUEST_CHECKOUT_LOGIN_OPTION_SYS_CONFIG, + ScopeInterface::SCOPE_WEBSITE, + 1 + ); + + if (!$guestLoginConfig) { + $this->assertTrue($this->accountManagement->isEmailAvailable('customer@example.com', 1)); + } else { + $this->assertFalse($this->accountManagement->isEmailAvailable('customer@example.com', 1)); + } } /** @@ -612,7 +625,18 @@ public function testIsEmailAvailable() */ public function testIsEmailAvailableNoWebsiteSpecified() { - $this->assertFalse($this->accountManagement->isEmailAvailable('customer@example.com')); + $scopeConfig = $this->objectManager->get(ScopeConfigInterface::class); + $guestLoginConfig = $scopeConfig->getValue( + AccountManagement::GUEST_CHECKOUT_LOGIN_OPTION_SYS_CONFIG, + ScopeInterface::SCOPE_WEBSITE, + 1 + ); + + if (!$guestLoginConfig) { + $this->assertTrue($this->accountManagement->isEmailAvailable('customer@example.com')); + } else { + $this->assertFalse($this->accountManagement->isEmailAvailable('customer@example.com')); + } } /** diff --git a/dev/tests/integration/testsuite/Magento/Framework/View/Element/AbstractBlockTest.php b/dev/tests/integration/testsuite/Magento/Framework/View/Element/AbstractBlockTest.php index f584b8f7cfcd3..d886a9189502b 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/View/Element/AbstractBlockTest.php +++ b/dev/tests/integration/testsuite/Magento/Framework/View/Element/AbstractBlockTest.php @@ -26,6 +26,9 @@ class AbstractBlockTest extends \PHPUnit\Framework\TestCase */ protected $_layout = null; + /** + * @var array + */ protected static $_mocks = []; /** @@ -573,7 +576,7 @@ public function testGetCacheKey() $this->assertNotEquals($name, $key); $block->setCacheKey('key'); - $this->assertEquals(AbstractBlock::CACHE_KEY_PREFIX . 'key', $block->getCacheKey()); + $this->assertEquals(AbstractBlock::CUSTOM_CACHE_KEY_PREFIX . 'key', $block->getCacheKey()); } /** diff --git a/dev/tests/integration/testsuite/Magento/Newsletter/Controller/Subscriber/NewActionTest.php b/dev/tests/integration/testsuite/Magento/Newsletter/Controller/Subscriber/NewActionTest.php index fc1aad57e57e7..00445aa6978b4 100644 --- a/dev/tests/integration/testsuite/Magento/Newsletter/Controller/Subscriber/NewActionTest.php +++ b/dev/tests/integration/testsuite/Magento/Newsletter/Controller/Subscriber/NewActionTest.php @@ -8,11 +8,14 @@ namespace Magento\Newsletter\Controller\Subscriber; use Magento\Customer\Api\CustomerRepositoryInterface; +use Magento\Customer\Model\AccountManagement; use Magento\Customer\Model\Session; use Magento\Customer\Model\Url; +use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\App\Request\Http as HttpRequest; use Magento\Newsletter\Model\ResourceModel\Subscriber as SubscriberResource; use Magento\Newsletter\Model\ResourceModel\Subscriber\CollectionFactory; +use Magento\Store\Model\ScopeInterface; use Magento\TestFramework\TestCase\AbstractController; use Laminas\Stdlib\Parameters; @@ -205,8 +208,18 @@ public function testWithEmailAssignedToAnotherCustomer(): void $this->session->loginById(1); $this->prepareRequest('customer2@search.example.com'); $this->dispatch('newsletter/subscriber/new'); + $scopeConfig = $this->_objectManager->get(ScopeConfigInterface::class); + $guestLoginConfig = $scopeConfig->getValue( + AccountManagement::GUEST_CHECKOUT_LOGIN_OPTION_SYS_CONFIG, + ScopeInterface::SCOPE_WEBSITE, + 1 + ); - $this->performAsserts('This email address is already assigned to another user.'); + if ($guestLoginConfig) { + $this->performAsserts('This email address is already assigned to another user.'); + } else { + $this->performAsserts('This email address is already subscribed.'); + } } /** diff --git a/dev/tests/integration/testsuite/Magento/Quote/Model/Quote/AddressTest.php b/dev/tests/integration/testsuite/Magento/Quote/Model/Quote/AddressTest.php index 41300fbd81757..1ea4e7a2ddf70 100644 --- a/dev/tests/integration/testsuite/Magento/Quote/Model/Quote/AddressTest.php +++ b/dev/tests/integration/testsuite/Magento/Quote/Model/Quote/AddressTest.php @@ -172,7 +172,7 @@ public function testSameAsBillingWhenCustomerHasNoDefaultShippingAddress($unsetI /** @var AddressRepositoryInterface $addressRepository */ $addressRepository = Bootstrap::getObjectManager() ->create(AddressRepositoryInterface::class); - $this->_customer->setDefaultShipping(-1) + $this->_customer->setDefaultShipping(1) ->setAddresses( [ $addressRepository->getById($this->_address->getId()), @@ -211,7 +211,7 @@ public function testSameAsBillingWhenCustomerHasDefaultShippingAddress() /** @var AddressRepositoryInterface $addressRepository */ $addressRepository = Bootstrap::getObjectManager() ->create(AddressRepositoryInterface::class); - $this->_customer->setDefaultShipping(2) + $this->_customer->setDefaultShipping(1) ->setAddresses([$addressRepository->getById($this->_address->getId())]); $this->_customer = $this->customerRepository->save($this->_customer); // we should save the customer data in order to be able to use it diff --git a/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteManagementTest.php b/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteManagementTest.php index 9ebf3e98da69f..a6b7a42bc5a61 100644 --- a/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteManagementTest.php +++ b/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteManagementTest.php @@ -34,6 +34,7 @@ * * @see \Magento\Quote\Model\QuoteManagement * @magentoDbIsolation enabled + * @magentoAppIsolation enabled * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class QuoteManagementTest extends TestCase @@ -196,7 +197,6 @@ public function testSubmitWithDeletedItem(): void $this->cartManagement->placeOrder($quote->getId()); } - /** * Tries to create order with product that has child items and one of them * was deleted when item data check is disabled on quote load. @@ -237,7 +237,11 @@ public function testSubmitWithItemOutOfStockWithDisabledInventoryCheck(): void { $this->makeProductOutOfStock('simple'); $quote = $this->getQuoteByReservedOrderId->execute('test01'); - $this->expectExceptionObject(new LocalizedException(__('The shipping method is missing. Select the shipping method and try again.'))); + $this->expectExceptionObject( + new LocalizedException( + __('The shipping method is missing. Select the shipping method and try again.') + ) + ); $this->cartManagement->placeOrder($quote->getId()); } diff --git a/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteTest.php b/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteTest.php index 7bda40c440628..00a8307c126a7 100644 --- a/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteTest.php +++ b/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteTest.php @@ -32,7 +32,7 @@ * Tests for quote model. * * @see \Magento\Quote\Model\Quote - * + * @magentoAppIsolation enabled * @magentoDbIsolation enabled * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ diff --git a/dev/tests/integration/testsuite/Magento/Quote/Model/ShippingMethodManagementTest.php b/dev/tests/integration/testsuite/Magento/Quote/Model/ShippingMethodManagementTest.php index e938945f2c411..3479201179d7d 100644 --- a/dev/tests/integration/testsuite/Magento/Quote/Model/ShippingMethodManagementTest.php +++ b/dev/tests/integration/testsuite/Magento/Quote/Model/ShippingMethodManagementTest.php @@ -32,6 +32,7 @@ use Magento\TestFramework\Helper\Bootstrap; use Magento\TestFramework\Quote\Model\GetQuoteByReservedOrderId; use PHPUnit\Framework\TestCase; +use Magento\Customer\Model\Session; /** * Test for shipping methods management @@ -319,10 +320,16 @@ public function testEstimateByAddressWithInclExclTaxAndVATGroup() $this->changeCustomerAddress($customer->getDefaultShipping()); $quote = $this->objectManager->get(GetQuoteByReservedOrderId::class)->execute('test01'); + $addressRepository = $this->objectManager->get(AddressRepositoryInterface::class); + $address = $addressRepository->getById(1); + $address->setIsDefaultShipping(true); + $customer->setAddresses([$address]); + $customerSession = $this->objectManager->get(Session::class); + $customerSession->loginById($customer->getId()); /** @var ShippingMethodManagementInterface $shippingEstimation */ $shippingEstimation = $this->objectManager->get(ShippingMethodManagementInterface::class); - $result = $shippingEstimation->estimateByAddressId($quote->getId(), $customer->getDefaultShipping()); + $result = $shippingEstimation->estimateByAddressId($quote->getId(), (int)$customer->getDefaultShipping()); $this->assertEquals(6.05, $result[0]->getPriceInclTax()); $this->assertEquals(5.0, $result[0]->getPriceExclTax()); diff --git a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Creditmemo/SaveTest.php b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Creditmemo/SaveTest.php index bbd6d27d688df..d0b4038521398 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Creditmemo/SaveTest.php +++ b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Creditmemo/SaveTest.php @@ -21,6 +21,11 @@ */ class SaveTest extends AbstractCreditmemoControllerTest { + /** + * @var string + */ + protected $resource = 'Magento_Sales::creditmemo'; + /** * @var string */ diff --git a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Invoice/SaveTest.php b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Invoice/SaveTest.php index c308305771e98..6e1268429083a 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Invoice/SaveTest.php +++ b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Invoice/SaveTest.php @@ -33,6 +33,11 @@ class SaveTest extends AbstractInvoiceControllerTest /** @var Item */ private $orderItemResource; + /** + * @var string + */ + protected $resource = "Magento_Sales::invoice"; + /** * @inheritdoc */ diff --git a/dev/tests/integration/testsuite/Magento/Sales/Helper/AdminTest.php b/dev/tests/integration/testsuite/Magento/Sales/Helper/AdminTest.php index 7b41f9890d74a..17f402faf2c5a 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/Helper/AdminTest.php +++ b/dev/tests/integration/testsuite/Magento/Sales/Helper/AdminTest.php @@ -76,7 +76,7 @@ public function escapeHtmlWithLinksDataProvider(): array ], [ 'Foo', - 'Foo', + 'Foo', 'allowedTags' => ['a'], ], [ @@ -86,7 +86,7 @@ public function escapeHtmlWithLinksDataProvider(): array ], [ "Foo", - 'Foo', + 'Foo', 'allowedTags' => ['a'], ], [ diff --git a/dev/tests/integration/testsuite/Magento/SalesRule/Model/Rule/Action/Discount/CartFixedTest.php b/dev/tests/integration/testsuite/Magento/SalesRule/Model/Rule/Action/Discount/CartFixedTest.php index 2dbba66604869..30f7c60d5f497 100644 --- a/dev/tests/integration/testsuite/Magento/SalesRule/Model/Rule/Action/Discount/CartFixedTest.php +++ b/dev/tests/integration/testsuite/Magento/SalesRule/Model/Rule/Action/Discount/CartFixedTest.php @@ -47,6 +47,11 @@ */ class CartFixedTest extends TestCase { + /** + * @var float + */ + private const EPSILON = 0.0000000001; + /** * @var GuestCartManagementInterface */ @@ -521,11 +526,11 @@ public function testDiscountsWhenByPercentRuleAppliedFirstAndCartFixedRuleSecond $item = array_shift($items); $this->assertEquals('simple1', $item->getSku()); $this->assertEquals(5.99, $item->getPrice()); - $this->assertEquals($expectedDiscounts[$item->getSku()], $item->getDiscountAmount()); + $this->assertEqualsWithDelta($expectedDiscounts[$item->getSku()], $item->getDiscountAmount(), self::EPSILON); $item = array_shift($items); $this->assertEquals('simple2', $item->getSku()); $this->assertEquals(15.99, $item->getPrice()); - $this->assertEquals($expectedDiscounts[$item->getSku()], $item->getDiscountAmount()); + $this->assertEqualsWithDelta($expectedDiscounts[$item->getSku()], $item->getDiscountAmount(), self::EPSILON); } public function discountByPercentDataProvider() diff --git a/dev/tests/integration/testsuite/Magento/Shipping/Controller/Adminhtml/Order/Shipment/SaveTest.php b/dev/tests/integration/testsuite/Magento/Shipping/Controller/Adminhtml/Order/Shipment/SaveTest.php index e3374064aa3cb..043ba86dce3ec 100644 --- a/dev/tests/integration/testsuite/Magento/Shipping/Controller/Adminhtml/Order/Shipment/SaveTest.php +++ b/dev/tests/integration/testsuite/Magento/Shipping/Controller/Adminhtml/Order/Shipment/SaveTest.php @@ -18,6 +18,11 @@ */ class SaveTest extends AbstractShipmentControllerTest { + /** + * @var string + */ + protected $resource = 'Magento_Sales::ship'; + /** * @var string */ @@ -105,8 +110,7 @@ private function prepareRequest(array $params = []) ] ); - $data = $params ?? []; - $this->getRequest()->setPostValue($data); + $this->getRequest()->setPostValue($params); return $order; } diff --git a/dev/tests/integration/testsuite/Magento/Tax/Model/Sales/Total/Quote/TaxTest.php b/dev/tests/integration/testsuite/Magento/Tax/Model/Sales/Total/Quote/TaxTest.php index a119b6259b5f6..f3b43d9f2cca2 100644 --- a/dev/tests/integration/testsuite/Magento/Tax/Model/Sales/Total/Quote/TaxTest.php +++ b/dev/tests/integration/testsuite/Magento/Tax/Model/Sales/Total/Quote/TaxTest.php @@ -23,6 +23,11 @@ */ class TaxTest extends \Magento\TestFramework\Indexer\TestCase { + /** + * @var float + */ + private const EPSILON = 0.0000000001; + /** * Utility object for setting up tax rates, tax classes and tax rules * @@ -176,7 +181,7 @@ public function testFullDiscountWithDeltaRoundingFix() protected function verifyItem($item, $expectedItemData) { foreach ($expectedItemData as $key => $value) { - $this->assertEquals($value, $item->getData($key), 'item ' . $key . ' is incorrect'); + $this->assertEqualsWithDelta($value, $item->getData($key), self::EPSILON, 'item ' . $key . ' is incorrect'); } return $this; @@ -247,7 +252,12 @@ protected function verifyQuoteAddress($quoteAddress, $expectedAddressData) if ($key == 'applied_taxes') { $this->verifyAppliedTaxes($quoteAddress->getAppliedTaxes(), $value); } else { - $this->assertEquals($value, $quoteAddress->getData($key), 'Quote address ' . $key . ' is incorrect'); + $this->assertEqualsWithDelta( + $value, + $quoteAddress->getData($key), + self::EPSILON, + 'Quote address ' . $key . ' is incorrect' + ); } } diff --git a/dev/tests/integration/testsuite/Magento/Tax/Model/TaxCalculationTest.php b/dev/tests/integration/testsuite/Magento/Tax/Model/TaxCalculationTest.php index 604a444883a26..fde67cc7c2094 100644 --- a/dev/tests/integration/testsuite/Magento/Tax/Model/TaxCalculationTest.php +++ b/dev/tests/integration/testsuite/Magento/Tax/Model/TaxCalculationTest.php @@ -16,15 +16,16 @@ class TaxCalculationTest extends \PHPUnit\Framework\TestCase { /** - * Object Manager - * + * @var float + */ + private const EPSILON = 0.0000000001; + + /** * @var \Magento\Framework\ObjectManagerInterface */ private $objectManager; /** - * Tax calculation service - * * @var \Magento\Tax\Api\TaxCalculationInterface */ private $taxCalculationService; @@ -108,7 +109,7 @@ public function testCalculateTaxUnitBased($quoteDetailsData, $expected) ); $taxDetails = $this->taxCalculationService->calculateTax($quoteDetails, 1); - $this->assertEquals($expected, $this->convertObjectToArray($taxDetails)); + $this->assertEqualsWithDelta($expected, $this->convertObjectToArray($taxDetails), self::EPSILON); } /** @@ -1286,7 +1287,7 @@ public function testCalculateTaxRowBased($quoteDetailsData, $expectedTaxDetails) $taxDetails = $this->taxCalculationService->calculateTax($quoteDetails); - $this->assertEquals($expectedTaxDetails, $this->convertObjectToArray($taxDetails)); + $this->assertEqualsWithDelta($expectedTaxDetails, $this->convertObjectToArray($taxDetails), self::EPSILON); } /** @@ -2387,7 +2388,11 @@ public function testMultiRulesRowBased($quoteDetailsData, $expectedTaxDetails) $taxDetails = $this->taxCalculationService->calculateTax($quoteDetails); - $this->assertEquals($expectedTaxDetails, $this->convertObjectToArray($taxDetails)); + $this->assertEqualsWithDelta( + $expectedTaxDetails, + $this->convertObjectToArray($taxDetails), + self::EPSILON + ); } /** @@ -2424,7 +2429,7 @@ public function testMultiRulesTotalBased($quoteDetailsData, $expectedTaxDetails) $taxDetails = $this->taxCalculationService->calculateTax($quoteDetails); - $this->assertEquals($expectedTaxDetails, $this->convertObjectToArray($taxDetails)); + $this->assertEqualsWithDelta($expectedTaxDetails, $this->convertObjectToArray($taxDetails), self::EPSILON); } /** @@ -2471,7 +2476,7 @@ public function testMultiRulesUnitBased($quoteDetailsData, $expectedTaxDetails) $taxDetails = $this->taxCalculationService->calculateTax($quoteDetails); - $this->assertEquals($expectedTaxDetails, $this->convertObjectToArray($taxDetails)); + $this->assertEqualsWithDelta($expectedTaxDetails, $this->convertObjectToArray($taxDetails), self::EPSILON); } /** diff --git a/dev/tests/integration/testsuite/Magento/Test/Integrity/Magento/Payment/MethodsTest.php b/dev/tests/integration/testsuite/Magento/Test/Integrity/Magento/Payment/MethodsTest.php index eb5c66cc9c777..773b90b8ba2e5 100644 --- a/dev/tests/integration/testsuite/Magento/Test/Integrity/Magento/Payment/MethodsTest.php +++ b/dev/tests/integration/testsuite/Magento/Test/Integrity/Magento/Payment/MethodsTest.php @@ -26,7 +26,16 @@ class MethodsTest extends \PHPUnit\Framework\TestCase */ public function testPaymentMethod($code, $methodClass) { - if (in_array($code, ['free', 'substitution', 'vault', 'payflowpro_cc_vault', 'fake_vault'])) { + $skipBlockTemplates = [ + 'free', + 'substitution', + 'vault', + 'payflowpro_cc_vault', + 'fake_vault', + 'braintree_cc_vault', + 'braintree_paypal_vault' + ]; + if (in_array($code, $skipBlockTemplates)) { return; } Bootstrap::getObjectManager()->configure($this->getTestConfiguration()); diff --git a/dev/tests/integration/testsuite/Magento/Ups/Model/CarrierTest.php b/dev/tests/integration/testsuite/Magento/Ups/Model/CarrierTest.php index 0cedcca254f80..5758d523876b7 100644 --- a/dev/tests/integration/testsuite/Magento/Ups/Model/CarrierTest.php +++ b/dev/tests/integration/testsuite/Magento/Ups/Model/CarrierTest.php @@ -14,13 +14,13 @@ use Magento\Framework\HTTP\AsyncClient\Response; use Magento\Framework\HTTP\AsyncClientInterface; use Magento\Quote\Model\Quote\Address\RateRequest; +use Magento\Quote\Model\Quote\Address\RateRequestFactory; use Magento\Quote\Model\Quote\Address\RateResult\Error; +use Magento\Shipping\Model\Shipment\Request; use Magento\TestFramework\Helper\Bootstrap; -use Magento\Quote\Model\Quote\Address\RateRequestFactory; use Magento\TestFramework\HTTP\AsyncClientInterfaceMock; -use PHPUnit\Framework\TestCase; use PHPUnit\Framework\MockObject\MockObject; -use Magento\Shipping\Model\Shipment\Request; +use PHPUnit\Framework\TestCase; use Psr\Log\LoggerInterface; /** @@ -113,29 +113,54 @@ public function testGetShipConfirmUrlLive() } /** - * Collect free rates. + * Collect rates for UPS Ground method. * * @magentoConfigFixture current_store carriers/ups/active 1 - * @magentoConfigFixture current_store carriers/ups/type UPS - * @magentoConfigFixture current_store carriers/ups/allowed_methods 1DA,GND - * @magentoConfigFixture current_store carriers/ups/free_method GND + * @magentoConfigFixture current_store carriers/ups/type UPS_XML + * @magentoConfigFixture current_store carriers/ups/allowed_methods 03 + * @magentoConfigFixture current_store carriers/ups/free_method 03 + * @magentoConfigFixture default_store carriers/ups/shipper_number 12345 + * @magentoConfigFixture default_store carriers/ups/origin_shipment Shipments Originating in the United States + * @magentoConfigFixture default_store carriers/ups/username user + * @magentoConfigFixture default_store carriers/ups/password pass + * @magentoConfigFixture default_store carriers/ups/access_license_number acn + * @magentoConfigFixture default_store carriers/ups/debug 1 + * @magentoConfigFixture default_store currency/options/allow USD,EUR + * @magentoConfigFixture default_store currency/options/base USD */ public function testCollectFreeRates() { - $rateRequest = Bootstrap::getObjectManager()->get(RateRequestFactory::class)->create(); - $rateRequest->setDestCountryId('US'); - $rateRequest->setDestRegionId('CA'); - $rateRequest->setDestPostcode('90001'); - $rateRequest->setPackageQty(1); - $rateRequest->setPackageWeight(1); - $rateRequest->setFreeMethodWeight(0); - $rateRequest->setLimitCarrier($this->carrier::CODE); - $rateRequest->setFreeShipping(true); - $rateResult = $this->carrier->collectRates($rateRequest); - $result = $rateResult->asArray(); - $methods = $result[$this->carrier::CODE]['methods']; - $this->assertEquals(0, $methods['GND']['price']); - $this->assertNotEquals(0, $methods['1DA']['price']); + $request = Bootstrap::getObjectManager()->create( + RateRequest::class, + [ + 'data' => [ + 'dest_country' => 'US', + 'dest_postal' => '90001', + 'package_weight' => '1', + 'package_qty' => '1', + 'free_method_weight' => '5', + 'product' => '11', + 'action' => 'Rate', + 'unit_measure' => 'KGS', + 'free_shipping' => '1', + 'base_currency' => new DataObject(['code' => 'USD']) + ] + ] + ); + //phpcs:disable Magento2.Functions.DiscouragedFunction + $this->httpClient->nextResponses( + [ + new Response( + 200, + [], + file_get_contents(__DIR__ . "/../_files/ups_rates_response_option9.xml") + ) + ] + ); + + $rates = $this->carrier->collectRates($request)->getAllRates(); + $this->assertEquals('19.19', $rates[0]->getPrice()); + $this->assertEquals('03', $rates[0]->getMethod()); } /** @@ -181,7 +206,7 @@ public function testCollectRates(int $negotiable, int $tax, int $responseId, str new Response( 200, [], - file_get_contents(__DIR__ ."/../_files/ups_rates_response_option$responseId.xml") + file_get_contents(__DIR__ . "/../_files/ups_rates_response_option$responseId.xml") ) ] ); @@ -245,9 +270,9 @@ public function collectRatesDataProvider() public function testRequestToShipment(): void { //phpcs:disable Magento2.Functions.DiscouragedFunction - $expectedShipmentRequest = file_get_contents(__DIR__ .'/../_files/ShipmentConfirmRequest.xml'); - $shipmentResponse = file_get_contents(__DIR__ .'/../_files/ShipmentConfirmResponse.xml'); - $acceptResponse = file_get_contents(__DIR__ .'/../_files/ShipmentAcceptResponse.xml'); + $expectedShipmentRequest = file_get_contents(__DIR__ . '/../_files/ShipmentConfirmRequest.xml'); + $shipmentResponse = file_get_contents(__DIR__ . '/../_files/ShipmentConfirmResponse.xml'); + $acceptResponse = file_get_contents(__DIR__ . '/../_files/ShipmentAcceptResponse.xml'); //phpcs:enable Magento2.Functions.DiscouragedFunction $this->httpClient->nextResponses( [ diff --git a/dev/tests/integration/testsuite/Magento/Ups/_files/ups_rates_response_option9.xml b/dev/tests/integration/testsuite/Magento/Ups/_files/ups_rates_response_option9.xml new file mode 100644 index 0000000000000..2e5c4bc0ddbfd --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Ups/_files/ups_rates_response_option9.xml @@ -0,0 +1,299 @@ + + + + + + Rating and Service + 1.0 + + 1 + Success + + + + 12 + + Your invoice may vary from the displayed reference rates + + + LBS + + 20.0 + + + USD + 43.19 + + + USD + 0.00 + + + USD + 43.19 + + 3 + + + + USD + 43.19 + + + USD + 0.00 + + + USD + 43.19 + + 20.0 + + + LBS + + 20.0 + + + + + + 14 + + Your invoice may vary from the displayed reference rates + + + LBS + + 20.0 + + + USD + 122.34 + + + USD + 0.00 + + + USD + 122.34 + + 1 + 8:00 A.M. + + + USD + 122.34 + + + USD + 0.00 + + + USD + 122.34 + + 20.0 + + + LBS + + 20.0 + + + + + + 03 + + Your invoice may vary from the displayed reference rates + + + LBS + + 20.0 + + + USD + 19.19 + + + USD + 0.00 + + + USD + 19.19 + + + + + + USD + 19.19 + + + USD + 0.00 + + + USD + 19.19 + + 20.0 + + + LBS + + 20.0 + + + + + + 13 + + Your invoice may vary from the displayed reference rates + + + LBS + + 20.0 + + + USD + 82.24 + + + USD + 0.00 + + + USD + 82.24 + + 1 + + + + USD + 82.24 + + + USD + 0.00 + + + USD + 82.24 + + 20.0 + + + LBS + + 20.0 + + + + + + 01 + + Your invoice may vary from the displayed reference rates + + + LBS + + 20.0 + + + USD + 88.14 + + + USD + 0.00 + + + USD + 88.14 + + 1 + 10:30 A.M. + + + USD + 88.14 + + + USD + 0.00 + + + USD + 88.14 + + 20.0 + + + LBS + + 20.0 + + + + + + 02 + + Your invoice may vary from the displayed reference rates + + + LBS + + 20.0 + + + USD + 57.11 + + + USD + 0.00 + + + USD + 57.11 + + 2 + + + + USD + 57.11 + + + USD + 0.00 + + + USD + 57.11 + + 20.0 + + + LBS + + 20.0 + + + + diff --git a/dev/tests/js/jasmine/spec_runner/jasmine-config.js b/dev/tests/js/jasmine/spec_runner/jasmine-config.js new file mode 100644 index 0000000000000..47a95ba673e7b --- /dev/null +++ b/dev/tests/js/jasmine/spec_runner/jasmine-config.js @@ -0,0 +1,13 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +(function () { + 'use strict'; + + var env = jasmine.getEnv(), + config = {random: false}; + + env.configure(config); +})(); diff --git a/dev/tests/js/jasmine/spec_runner/tasks/jasmine.js b/dev/tests/js/jasmine/spec_runner/tasks/jasmine.js index 1f5b132478fa8..086943dc61729 100644 --- a/dev/tests/js/jasmine/spec_runner/tasks/jasmine.js +++ b/dev/tests/js/jasmine/spec_runner/tasks/jasmine.js @@ -3,6 +3,10 @@ * See COPYING.txt for license details. */ +/* eslint-disable strict */ +/* eslint-disable quotes */ +/* eslint-disable no-use-before-define */ + 'use strict'; var tasks = {}, @@ -67,7 +71,11 @@ function init(config) { /** * @todo rename "helpers" to "specs" (implies overriding grunt-contrib-jasmine code) */ - helpers: specs + helpers: specs, + sandboxArgs: { + args: ['--no-sandbox', '--disable-setuid-sandbox'], + defaultViewport: {width: 400, height: 400, hasTouch: true} + } } }; }); diff --git a/dev/tests/js/jasmine/spec_runner/template.html b/dev/tests/js/jasmine/spec_runner/template.html index 23e36c527592e..368f430f2add4 100644 --- a/dev/tests/js/jasmine/spec_runner/template.html +++ b/dev/tests/js/jasmine/spec_runner/template.html @@ -20,7 +20,7 @@ <% }); %> <% with (scripts) { %> - <% [].concat(polyfills, jasmine, boot, vendor, src, reporters).forEach(function (script) { %> + <% [].concat(polyfills, jasmine, '.grunt/grunt-contrib-jasmine/boot0.js', 'dev/tests/js/jasmine/spec_runner/jasmine-config.js', '.grunt/grunt-contrib-jasmine/boot1.js', vendor, src, reporters).forEach(function (script) { %> <% }); %> <% } %> diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/action/redirect-on-success.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/action/redirect-on-success.test.js index 4b053b4322dea..46181c0431c0b 100644 --- a/dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/action/redirect-on-success.test.js +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/action/redirect-on-success.test.js @@ -35,8 +35,7 @@ define([ }); it('Checks if loader is called before redirect to success page.', function () { - spyOn(window.location, 'replace').and.returnValue(false); - + spyOn(RedirectOnSuccess, 'redirectToSuccessPage').and.callFake(function () {}); RedirectOnSuccess.execute(); expect(FullScreenLoader.startLoader).toHaveBeenCalled(); diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/model/error-processor.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/model/error-processor.test.js index 93731646aad1f..7443102cada91 100644 --- a/dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/model/error-processor.test.js +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/model/error-processor.test.js @@ -62,7 +62,7 @@ define([ it('check on failed status', function () { var messageContainer = jasmine.createSpyObj('globalMessageList', ['addErrorMessage']); - spyOn(window.location, 'replace').and.callFake(function () {}); + spyOn(model, 'redirectTo').and.callFake(function () {}); model.process({ status: 401, responseText: '' diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/model/new-customer-address.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/model/new-customer-address.test.js index 84c1b97063322..749c801c41a40 100644 --- a/dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/model/new-customer-address.test.js +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/model/new-customer-address.test.js @@ -25,13 +25,15 @@ define([ }); it('Check on empty object.', function () { - var expected = { - countryId: 'US', - regionCode: null, - region: null - }; + var result = newCustomerAddress({}), + expected = { + countryId: 'US', + regionCode: null, + region: null + }; - expect(JSON.stringify(newCustomerAddress({}))).toEqual(JSON.stringify(expected)); + result.postcode = undefined; + expect(JSON.stringify(result)).toEqual(JSON.stringify(expected)); }); it('Check on function call with empty address data.', function () { @@ -57,6 +59,7 @@ define([ regionId: 1 }; + result.postcode = undefined; expect(JSON.stringify(result)).toEqual(JSON.stringify(expected)); }); it('Check on regionId with countryId in address data.', function () { @@ -69,6 +72,7 @@ define([ region: null }; + result.postcode = undefined; expect(JSON.stringify(result)).toEqual(JSON.stringify(expected)); }); it('Check that extensionAttributes property exists if defined', function () { @@ -86,6 +90,7 @@ define([ } }; + result.postcode = undefined; expect(JSON.stringify(result)).toEqual(JSON.stringify(expected)); }); }); diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/components/qty-configurable.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/components/qty-configurable.test.js index 1c81c9fabf85e..30960ca9e6a7c 100644 --- a/dev/tests/js/jasmine/tests/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/components/qty-configurable.test.js +++ b/dev/tests/js/jasmine/tests/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/components/qty-configurable.test.js @@ -42,26 +42,26 @@ define(['squire'], function (Squire) { it('Product is not configurable by default', function () { var component = new Component(params); - expect(component.disabled()).toBeFalsy(); + expect(component.disabled()).toBeFalse(); expect(component.value()).toEqual(1000); }); it('State of component does not changed', function () { var component = new Component(params); - expect(component.disabled()).toBeFalsy(); + expect(component.disabled()).toBeFalse(); component.value(99); component.handleQtyValue(false); - expect(component.disabled()).toBeFalsy(); + expect(component.disabled()).toBeFalse(); expect(component.value()).toEqual(99); }); it('Product changed to configurable', function () { var component = new Component(params); - expect(component.disabled()).toBeFalsy(); + expect(component.disabled()).toBeFalse(); expect(component.value()).toEqual(1000); component.handleQtyValue(true); @@ -100,7 +100,7 @@ define(['squire'], function (Squire) { component.value(100); component.handleQtyValue(false); - expect(component.disabled()).toBeFalsy(); + expect(component.disabled()).toBeFalse(); expect(component.value()).toEqual(100); }); }); diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Customer/adminhtml/js/view/form/components/insert-listing.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Customer/adminhtml/js/view/form/components/insert-listing.test.js index e07b1fbc69453..dcb0f7678e5bc 100644 --- a/dev/tests/js/jasmine/tests/app/code/Magento/Customer/adminhtml/js/view/form/components/insert-listing.test.js +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Customer/adminhtml/js/view/form/components/insert-listing.test.js @@ -23,12 +23,7 @@ define(['Magento_Customer/js/form/components/insert-listing'], function (Constr) }; beforeEach(function () { - obj = new Constr({ - name: 'content_name', - selections: function () { - return selectionsProvider; - } - }); + obj = new Constr({ name: 'content_name' }); }); describe('Check delete massaction process', function () { @@ -45,6 +40,10 @@ define(['Magento_Customer/js/form/components/insert-listing'], function (Constr) it('Check ids are retrieved from selections provider if they are NOT in data', function () { obj._delete = jasmine.createSpy(); + obj.selections = jasmine.createSpy().and.callFake(function () { + return selectionsProvider; + }); + obj.onMassAction({ action: 'delete', data: {} @@ -60,6 +59,10 @@ define(['Magento_Customer/js/form/components/insert-listing'], function (Constr) get: jasmine.createSpy().and.returnValues(2, 3), set: jasmine.createSpy() }; + obj.selections = jasmine.createSpy().and.callFake(function () { + return selectionsProvider; + }); + obj.onMassAction(data); expect(selectionsProvider.selected).not.toHaveBeenCalled(); diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Customer/frontend/js/customer-data.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Customer/frontend/js/customer-data.test.js index e516c3f19d64a..ab6777f6f01fd 100644 --- a/dev/tests/js/jasmine/tests/app/code/Magento/Customer/frontend/js/customer-data.test.js +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Customer/frontend/js/customer-data.test.js @@ -201,6 +201,7 @@ define([ }); it('Check it requests sections from the server if there are expired sections', function () { + clearLocalStorage(); setupLocalStorage({ 'customer': { 'data_id': Math.floor(Date.now() / 1000) + 60 // invalidated, @@ -279,6 +280,7 @@ define([ }); it('Check that result contains invalidated section names', function () { + clearLocalStorage(); setupLocalStorage({ 'cart': { // without storage content 'data_id': Math.floor(Date.now() / 1000) + 60 // in 1 minute diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/MediaGalleryUi/adminhtml/js/grid/messages.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/MediaGalleryUi/adminhtml/js/grid/messages.test.js index 39444f8859465..1c3940d585328 100644 --- a/dev/tests/js/jasmine/tests/app/code/Magento/MediaGalleryUi/adminhtml/js/grid/messages.test.js +++ b/dev/tests/js/jasmine/tests/app/code/Magento/MediaGalleryUi/adminhtml/js/grid/messages.test.js @@ -68,7 +68,7 @@ define([ var escapedMessage = 'escaped message'; // eslint-disable-next-line max-nested-callbacks - spyOn(escaperInstance, 'escapeHtml').and.callFake(function () { + escaperInstance.escapeHtml = jasmine.createSpy().and.callFake(function () { return escapedMessage; }); diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Sales/adminhtml/js/grid/tree-massactions.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Sales/adminhtml/js/grid/tree-massactions.test.js index 7e33a7ad3c1fa..4a1a8219a56c1 100644 --- a/dev/tests/js/jasmine/tests/app/code/Magento/Sales/adminhtml/js/grid/tree-massactions.test.js +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Sales/adminhtml/js/grid/tree-massactions.test.js @@ -105,7 +105,7 @@ define([ result = obj.defaultCallback(action, data); expect(typeof result).toBe('object'); - spyOn(utils, 'submit').and.callThrough(); + utils.submit = jasmine.createSpy().and.callThrough(); utils.submit({ url: action.url, data: data.selected diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Theme/view/frontend/web/js/cookie-status.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Theme/view/frontend/web/js/cookie-status.test.js index 67ba24fe3bcfb..f54ebb6b769a5 100644 --- a/dev/tests/js/jasmine/tests/app/code/Magento/Theme/view/frontend/web/js/cookie-status.test.js +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Theme/view/frontend/web/js/cookie-status.test.js @@ -22,26 +22,18 @@ define([ $(document.body).append(htmlContainer); }); - afterEach(function () { - window.navigator = navigator; - }); - it('defines cookieStatus widget', function () { expect($.fn.cookieStatus).toBeDefined(); }); it('does not show a modal when cookies are supported', function () { - window.navigator = { - cookieEnabled: true - }; + Object.defineProperty(navigator,'cookieEnabled',{value: true, configurable: true}); widget._init(); expect($(document.body).html()).not.toContain('