VYPR
Critical severityCISA KEVNVD Advisory· Published Jun 13, 2024· Updated Oct 21, 2025

XXE can expose crypt key and other secrets granting full admin access

CVE-2024-34102

Description

Adobe Commerce versions 2.4.7, 2.4.6-p5, 2.4.5-p7, 2.4.4-p8 and earlier are affected by an Improper Restriction of XML External Entity Reference ('XXE') vulnerability that could result in arbitrary code execution. An attacker could exploit this vulnerability by sending a crafted XML document that references external entities. Exploitation of this issue does not require user interaction.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
magento/community-editionPackagist
>= 2.4.6-p1, < 2.4.6-p62.4.6-p6
magento/community-editionPackagist
>= 2.4.5-p1, < 2.4.5-p82.4.5-p8
magento/community-editionPackagist
< 2.4.4-p92.4.4-p9

Affected products

1
  • Adobe/Adobe Commercev5
    Range: 0

Patches

4
d10435b11ada

Magento Release 2.4.7-p1

https://github.com/magento/magento2magento packaging serviceJun 6, 2024via ghsa
268 files changed · +3542 3349
  • app/code/Magento/AdminAnalytics/composer.json+14 12 modified
    @@ -1,24 +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.6",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-backend": "*",
    -        "magento/module-config": "*",
    -        "magento/module-store": "*",
    -        "magento/module-ui": "*",
    -        "magento/module-release-notification": "*",
    -        "magento/module-csp": "*"
    +        "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"
    @@ -28,3 +29,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/AdminNotification/composer.json+13 11 modified
    @@ -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.6",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.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 @@
             }
         }
     }
    +
    
  • app/code/Magento/AdvancedPricingImportExport/composer.json+16 14 modified
    @@ -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.7",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.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 @@
             }
         }
     }
    +
    
  • app/code/Magento/AdvancedSearch/composer.json+15 13 modified
    @@ -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.5",
         "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": "~8.1.0||~8.2.0||~8.3.0"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -29,3 +30,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Amqp/composer.json+10 8 modified
    @@ -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.4",
         "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": "~8.1.0||~8.2.0||~8.3.0"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -24,3 +25,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Analytics/composer.json+10 8 modified
    @@ -1,19 +1,20 @@
     {
         "name": "magento/module-analytics",
         "description": "N/A",
    -    "require": {
    -        "php": "~8.1.0||~8.2.0||~8.3.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.7",
    +    "require": {
    +        "php": "~8.1.0||~8.2.0||~8.3.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 @@
             }
         }
     }
    +
    
  • app/code/Magento/ApplicationPerformanceMonitor/composer.json+17 15 modified
    @@ -1,18 +1,20 @@
     {
    -  "name": "magento/module-application-performance-monitor",
    -  "license": "OSL-3.0",
    -  "type": "magento2-module",
    -  "description": "Performance Monitor for Application",
    -  "autoload": {
    -    "files": [
    -      "registration.php"
    -    ],
    -    "psr-4": {
    -      "Magento\\ApplicationPerformanceMonitor\\": ""
    +    "name": "magento/module-application-performance-monitor",
    +    "description": "Performance Monitor for Application",
    +    "type": "magento2-module",
    +    "license": "OSL-3.0",
    +    "version": "100.4.0",
    +    "require": {
    +        "php": "~8.1.0||~8.2.0||~8.3.0",
    +        "magento/framework": "103.0.*"
    +    },
    +    "autoload": {
    +        "files": [
    +            "registration.php"
    +        ],
    +        "psr-4": {
    +            "Magento\\ApplicationPerformanceMonitor\\": ""
    +        }
         }
    -  },
    -  "require": {
    -    "php": "~8.1.0||~8.2.0||~8.3.0",
    -    "magento/framework": "*"
    -  }
     }
    +
    
  • app/code/Magento/ApplicationPerformanceMonitorNewRelic/composer.json+22 20 modified
    @@ -1,23 +1,25 @@
     {
    -  "name": "magento/module-application-performance-monitor-new-relic",
    -  "license": "OSL-3.0",
    -  "type": "magento2-module",
    -  "description": "Performance data about Application into New Relic",
    -  "autoload": {
    -    "files": [
    -      "registration.php"
    -    ],
    -    "psr-4": {
    -      "Magento\\ApplicationPerformanceMonitorNewRelic\\": ""
    +    "name": "magento/module-application-performance-monitor-new-relic",
    +    "description": "Performance data about Application into New Relic",
    +    "type": "magento2-module",
    +    "license": "OSL-3.0",
    +    "version": "100.4.0",
    +    "require": {
    +        "php": "~8.1.0||~8.2.0||~8.3.0",
    +        "magento/framework": "103.0.*",
    +        "magento/module-application-performance-monitor": "100.4.*",
    +        "magento/module-new-relic-reporting": "100.4.*"
    +    },
    +    "suggest": {
    +        "ext-newrelic": "This module requires New Relic's PHP extension in order to function."
    +    },
    +    "autoload": {
    +        "files": [
    +            "registration.php"
    +        ],
    +        "psr-4": {
    +            "Magento\\ApplicationPerformanceMonitorNewRelic\\": ""
    +        }
         }
    -  },
    -  "require": {
    -    "php": "~8.1.0||~8.2.0||~8.3.0",
    -    "magento/framework": "*",
    -    "magento/module-application-performance-monitor": "*",
    -    "magento/module-new-relic-reporting": "*"
    -  },
    -  "suggest": {
    -    "ext-newrelic": "This module requires New Relic's PHP extension in order to function."
    -  }
     }
    +
    
  • app/code/Magento/AsyncConfig/composer.json+7 5 modified
    @@ -1,15 +1,16 @@
     {
         "name": "magento/module-async-config",
         "description": "N/A",
    -    "require": {
    -        "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-config": "*"
    -    },
         "type": "magento2-module",
         "license": [
             "proprietary"
         ],
    +    "version": "100.4.0",
    +    "require": {
    +        "php": "~8.1.0||~8.2.0||~8.3.0",
    +        "magento/framework": "103.0.*",
    +        "magento/module-config": "101.2.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -19,3 +20,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/AsynchronousOperations/composer.json+14 12 modified
    @@ -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.7",
         "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": "~8.1.0||~8.2.0||~8.3.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 @@
             }
         }
     }
    +
    
  • app/code/Magento/Authorization/composer.json+9 7 modified
    @@ -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.7",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.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 @@
             }
         }
     }
    +
    
  • app/code/Magento/AwsS3/composer.json+8 6 modified
    @@ -1,18 +1,19 @@
     {
         "name": "magento/module-aws-s3",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "proprietary"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.5",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-remote-storage": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-remote-storage": "100.4.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "proprietary"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -22,3 +23,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Backend/composer.json+26 24 modified
    @@ -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.7",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.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 @@
             }
         }
     }
    +
    
  • app/code/Magento/Backup/composer.json+11 9 modified
    @@ -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.7",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.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 @@
             }
         }
     }
    +
    
  • app/code/Magento/Bundle/composer.json+27 25 modified
    @@ -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.7",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.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 @@
             }
         }
     }
    +
    
  • app/code/Magento/BundleGraphQl/composer.json+15 13 modified
    @@ -2,23 +2,24 @@
         "name": "magento/module-bundle-graph-ql",
         "description": "N/A",
         "type": "magento2-module",
    -    "require": {
    -        "php": "~8.1.0||~8.2.0||~8.3.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.7",
    +    "require": {
    +        "php": "~8.1.0||~8.2.0||~8.3.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 @@
             }
         }
     }
    +
    
  • app/code/Magento/BundleImportExport/composer.json+14 12 modified
    @@ -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.6",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.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 @@
             }
         }
     }
    +
    
  • app/code/Magento/CacheInvalidate/composer.json+9 7 modified
    @@ -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.5",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.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 @@
             }
         }
     }
    +
    
  • app/code/Magento/Captcha/composer.json+14 12 modified
    @@ -1,26 +1,27 @@
     {
         "name": "magento/module-captcha",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.7",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.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.12",
             "laminas/laminas-db": "^2.19"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -30,3 +31,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/CardinalCommerce/composer.json+11 9 modified
    @@ -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.5",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.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 @@
             }
         }
     }
    +
    
  • app/code/Magento/CatalogAnalytics/composer.json+8 6 modified
    @@ -1,17 +1,18 @@
     {
         "name": "magento/module-catalog-analytics",
         "description": "N/A",
    -    "require": {
    -        "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-catalog": "*",
    -        "magento/module-analytics": "*"
    -    },
         "type": "magento2-module",
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "100.4.4",
    +    "require": {
    +        "php": "~8.1.0||~8.2.0||~8.3.0",
    +        "magento/framework": "103.0.*",
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-analytics": "100.4.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -21,3 +22,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/CatalogCmsGraphQl/composer.json+12 10 modified
    @@ -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.3",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.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 @@
             }
         }
     }
    +
    
  • app/code/Magento/Catalog/composer.json+38 36 modified
    @@ -1,50 +1,51 @@
     {
         "name": "magento/module-catalog",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "104.0.7-p1",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/framework-bulk": "*",
    -        "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/module-aws-s3": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/framework-bulk": "101.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.*",
    +        "magento/module-aws-s3": "100.4.*"
         },
         "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"
    @@ -54,3 +55,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/CatalogCustomerGraphQl/composer.json+9 7 modified
    @@ -2,17 +2,18 @@
         "name": "magento/module-catalog-customer-graph-ql",
         "description": "N/A",
         "type": "magento2-module",
    -    "require": {
    -        "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-catalog": "*",
    -        "magento/module-customer": "*",
    -        "magento/module-catalog-graph-ql": "*"
    -    },
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "100.4.6",
    +    "require": {
    +        "php": "~8.1.0||~8.2.0||~8.3.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 @@
             }
         }
     }
    +
    
  • app/code/Magento/CatalogGraphQl/composer.json+21 19 modified
    @@ -2,30 +2,31 @@
         "name": "magento/module-catalog-graph-ql",
         "description": "N/A",
         "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
    +    "version": "100.4.7",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.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-graph-ql-resolver-cache": "*",
    -        "magento/module-config": "*",
    -        "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-graph-ql-resolver-cache": "100.4.*",
    +        "magento/module-config": "101.2.*",
    +        "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"
    @@ -35,3 +36,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/CatalogImportExport/composer.json+19 17 modified
    @@ -1,30 +1,31 @@
     {
         "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.7",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.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/module-aws-s3": "*"
    +        "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.*",
    +        "magento/module-aws-s3": "100.4.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -34,3 +35,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/CatalogInventory/composer.json+15 13 modified
    @@ -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.7",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.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-metapackage"
     }
    +
    
  • app/code/Magento/CatalogInventoryGraphQl/composer.json+10 8 modified
    @@ -2,18 +2,19 @@
         "name": "magento/module-catalog-inventory-graph-ql",
         "description": "N/A",
         "type": "magento2-module",
    -    "require": {
    -        "php": "~8.1.0||~8.2.0||~8.3.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.4",
    +    "require": {
    +        "php": "~8.1.0||~8.2.0||~8.3.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 @@
             }
         }
     }
    +
    
  • app/code/Magento/CatalogRule/composer.json+17 15 modified
    @@ -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.7",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.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 @@
             }
         }
     }
    +
    
  • app/code/Magento/CatalogRuleConfigurable/composer.json+12 10 modified
    @@ -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.6",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.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 @@
             }
         }
     }
    +
    
  • app/code/Magento/CatalogRuleGraphQl/composer.json+8 6 modified
    @@ -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.4",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.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 @@
             }
         }
     }
    +
    
  • app/code/Magento/CatalogSearch/composer.json+20 18 modified
    @@ -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.7",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.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 @@
             }
         }
     }
    +
    
  • app/code/Magento/Catalog/Test/Mftf/Test/AdminApplyTierPriceToProductTest/StoreFrontDeleteProductImagesAssignedDifferentRolesTest.xml+1 1 modified
    @@ -77,7 +77,7 @@
                 <!-- Go to the product page on StoreFront and see the Base image -->
                 <amOnPage url="{{StorefrontProductPage.url($simpleProductOne.custom_attributes[url_key]$)}}" stepKey="goToProductPage"/>
                 <waitForPageLoad stepKey="waitForPageLoad"/>
    -            <seeElement selector="{{StorefrontProductMediaSection.imageFile('/adobe-base')}}" stepKey="seeBaseImageOnProductPage"/>
    +            <waitForElementVisible selector="{{StorefrontProductMediaSection.imageFile('/adobe-base')}}" stepKey="seeBaseImageOnProductPage"/>
                 <!-- Go to the category page and see the Small image -->
                 <amOnPage url="{{StorefrontCategoryPage.url($testCategory.custom_attributes[url_key]$)}}" stepKey="goToCategoryPage"/>
                 <waitForPageLoad stepKey="waitForPageLoadingToFinish"/>
    
  • app/code/Magento/CatalogUrlRewrite/composer.json+17 15 modified
    @@ -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.7",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.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 @@
             }
         }
     }
    +
    
  • app/code/Magento/CatalogUrlRewriteGraphQl/composer.json+14 12 modified
    @@ -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.5",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.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 @@
             }
         }
     }
    +
    
  • app/code/Magento/CatalogWidget/composer.json+17 15 modified
    @@ -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.7",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.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 @@
             }
         }
     }
    +
    
  • app/code/Magento/CheckoutAgreements/composer.json+12 10 modified
    @@ -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.6",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.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 @@
             }
         }
     }
    +
    
  • app/code/Magento/CheckoutAgreementsGraphQl/composer.json+10 8 modified
    @@ -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.3",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.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 @@
             }
         }
     }
    +
    
  • app/code/Magento/Checkout/composer.json+30 28 modified
    @@ -1,42 +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.7",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.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/module-csp": "*"
    +        "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"
    @@ -46,3 +47,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Cms/composer.json+18 16 modified
    @@ -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.7",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.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 @@
             }
         }
     }
    +
    
  • app/code/Magento/CmsGraphQl/composer.json+14 12 modified
    @@ -2,23 +2,24 @@
         "name": "magento/module-cms-graph-ql",
         "description": "N/A",
         "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
    +    "version": "100.4.4",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-cms": "*",
    -        "magento/module-widget": "*",
    -        "magento/module-store": "*",
    -        "magento/module-graph-ql-resolver-cache": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-cms": "104.0.*",
    +        "magento/module-widget": "101.2.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/module-graph-ql-resolver-cache": "100.4.*"
         },
         "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"
    @@ -28,3 +29,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/CmsUrlRewrite/composer.json+11 9 modified
    @@ -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.6",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.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 @@
             }
         }
     }
    +
    
  • app/code/Magento/CmsUrlRewriteGraphQl/composer.json+13 11 modified
    @@ -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.5",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.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 @@
             }
         }
     }
    +
    
  • app/code/Magento/CompareListGraphQl/composer.json+8 6 modified
    @@ -2,16 +2,17 @@
         "name": "magento/module-compare-list-graph-ql",
         "description": "N/A",
         "type": "magento2-module",
    -    "require": {
    -        "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-catalog": "*",
    -        "magento/module-customer": "*"
    -    },
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "100.4.3",
    +    "require": {
    +        "php": "~8.1.0||~8.2.0||~8.3.0",
    +        "magento/framework": "103.0.*",
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-customer": "103.0.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -21,3 +22,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Config/composer.json+15 13 modified
    @@ -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.7",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.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 @@
             }
         }
     }
    +
    
  • app/code/Magento/ConfigurableImportExport/composer.json+14 12 modified
    @@ -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.5",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.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 @@
             }
         }
     }
    +
    
  • app/code/Magento/ConfigurableProduct/composer.json+27 25 modified
    @@ -1,39 +1,40 @@
     {
         "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.7",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.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-catalog-widget": "*"
    +        "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.*",
    +        "magento/module-catalog-widget": "100.4.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -43,3 +44,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/ConfigurableProductGraphQl/composer.json+13 11 modified
    @@ -2,21 +2,22 @@
         "name": "magento/module-configurable-product-graph-ql",
         "description": "N/A",
         "type": "magento2-module",
    -    "require": {
    -        "php": "~8.1.0||~8.2.0||~8.3.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.7",
    +    "require": {
    +        "php": "~8.1.0||~8.2.0||~8.3.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 @@
             }
         }
     }
    +
    
  • app/code/Magento/ConfigurableProductSales/composer.json+12 10 modified
    @@ -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.4",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.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 @@
             }
         }
     }
    +
    
  • app/code/Magento/Contact/composer.json+12 10 modified
    @@ -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.6",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.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 @@
             }
         }
     }
    +
    
  • app/code/Magento/ContactGraphQl/composer.json+9 7 modified
    @@ -2,21 +2,22 @@
         "name": "magento/module-contact-graph-ql",
         "description": "N/A",
         "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.0",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-contact": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-contact": "100.4.*"
         },
         "suggest": {
    -        "magento/module-graph-ql": "*"
    +        "magento/module-graph-ql": "100.4.*"
         },
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -26,3 +27,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Cookie/composer.json+10 8 modified
    @@ -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.7",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.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 @@
             }
         }
     }
    +
    
  • app/code/Magento/Cron/composer.json+10 8 modified
    @@ -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.7",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.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 @@
             }
         }
     }
    +
    
  • app/code/Magento/Csp/composer.json+11 9 modified
    @@ -1,21 +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.6",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-store": "*",
    -        "magento/module-require-js": "*",
    -        "magento/module-deploy": "*"
    +        "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"
    @@ -25,3 +26,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/CurrencySymbol/composer.json+13 11 modified
    @@ -1,23 +1,24 @@
     {
         "name": "magento/module-currency-symbol",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.5",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-backend": "*",
    -        "magento/module-config": "*",
    -        "magento/module-directory": "*",
    -        "magento/module-page-cache": "*",
    -        "magento/module-store": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-config": "101.2.*",
    +        "magento/module-directory": "100.4.*",
    +        "magento/module-page-cache": "100.4.*",
    +        "magento/module-store": "101.1.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -27,3 +28,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/CustomerAnalytics/composer.json+8 6 modified
    @@ -1,17 +1,18 @@
     {
         "name": "magento/module-customer-analytics",
         "description": "N/A",
    -    "require": {
    -        "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-customer": "*",
    -        "magento/module-analytics": "*"
    -    },
         "type": "magento2-module",
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "100.4.4",
    +    "require": {
    +        "php": "~8.1.0||~8.2.0||~8.3.0",
    +        "magento/framework": "103.0.*",
    +        "magento/module-customer": "103.0.*",
    +        "magento/module-analytics": "100.4.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -21,3 +22,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Customer/composer.json+30 28 modified
    @@ -1,42 +1,43 @@
     {
         "name": "magento/module-customer",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "103.0.7-p1",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-authorization": "*",
    -        "magento/module-backend": "*",
    -        "magento/module-catalog": "*",
    -        "magento/module-checkout": "*",
    -        "magento/module-config": "*",
    -        "magento/module-directory": "*",
    -        "magento/module-eav": "*",
    -        "magento/module-integration": "*",
    -        "magento/module-media-storage": "*",
    -        "magento/module-newsletter": "*",
    -        "magento/module-page-cache": "*",
    -        "magento/module-quote": "*",
    -        "magento/module-sales": "*",
    -        "magento/module-store": "*",
    -        "magento/module-tax": "*",
    -        "magento/module-theme": "*",
    -        "magento/module-ui": "*",
    -        "magento/module-wishlist": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-authorization": "100.4.*",
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-checkout": "100.4.*",
    +        "magento/module-config": "101.2.*",
    +        "magento/module-directory": "100.4.*",
    +        "magento/module-eav": "102.1.*",
    +        "magento/module-integration": "100.4.*",
    +        "magento/module-media-storage": "100.4.*",
    +        "magento/module-newsletter": "100.4.*",
    +        "magento/module-page-cache": "100.4.*",
    +        "magento/module-quote": "101.2.*",
    +        "magento/module-sales": "103.0.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/module-tax": "100.4.*",
    +        "magento/module-theme": "101.1.*",
    +        "magento/module-ui": "101.2.*",
    +        "magento/module-wishlist": "101.2.*"
         },
         "suggest": {
    -        "magento/module-cookie": "*",
    -        "magento/module-customer-sample-data": "*",
    -        "magento/module-webapi": "*",
    -        "magento/module-asynchronous-operations": "*"
    +        "magento/module-cookie": "100.4.*",
    +        "magento/module-customer-sample-data": "Sample Data version: 100.4.*",
    +        "magento/module-webapi": "100.4.*",
    +        "magento/module-asynchronous-operations": "100.4.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -46,3 +47,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/CustomerDownloadableGraphQl/composer.json+10 8 modified
    @@ -2,19 +2,20 @@
         "name": "magento/module-customer-downloadable-graph-ql",
         "description": "N/A",
         "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
    +    "version": "100.4.3",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/module-downloadable-graph-ql": "*",
    -        "magento/module-graph-ql": "*",
    -        "magento/framework": "*"
    +        "magento/module-downloadable-graph-ql": "100.4.*",
    +        "magento/module-graph-ql": "100.4.*",
    +        "magento/framework": "103.0.*"
         },
         "suggest": {
    -        "magento/module-catalog-graph-ql": "*"
    +        "magento/module-catalog-graph-ql": "100.4.*"
         },
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -24,3 +25,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Customer/etc/webapi_rest/di.xml+0 3 modified
    @@ -31,9 +31,6 @@
                 </argument>
             </arguments>
         </type>
    -    <type name="Magento\Webapi\Controller\Rest\ParamsOverrider">
    -        <plugin name="validateCustomerData" type="Magento\Customer\Plugin\Webapi\Controller\Rest\ValidateCustomerData" sortOrder="1" disabled="false" />
    -    </type>
         <preference for="Magento\Customer\Api\AccountManagementInterface"
                     type="Magento\Customer\Model\AccountManagementApi" />
     </config>
    
  • app/code/Magento/CustomerGraphQl/composer.json+18 16 modified
    @@ -2,26 +2,27 @@
         "name": "magento/module-customer-graph-ql",
         "description": "N/A",
         "type": "magento2-module",
    -    "require": {
    -        "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/module-authorization": "*",
    -        "magento/module-customer": "*",
    -        "magento/module-eav": "*",
    -        "magento/module-eav-graph-ql": "*",
    -        "magento/module-graph-ql": "*",
    -        "magento/module-newsletter": "*",
    -        "magento/module-integration": "*",
    -        "magento/module-store": "*",
    -        "magento/framework": "*",
    -        "magento/module-directory": "*",
    -        "magento/module-tax": "*",
    -        "magento/module-graph-ql-cache": "*",
    -        "magento/module-graph-ql-resolver-cache": "*"
    -    },
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "100.4.7",
    +    "require": {
    +        "php": "~8.1.0||~8.2.0||~8.3.0",
    +        "magento/module-authorization": "100.4.*",
    +        "magento/module-customer": "103.0.*",
    +        "magento/module-eav": "102.1.*",
    +        "magento/module-eav-graph-ql": "100.4.*",
    +        "magento/module-graph-ql": "100.4.*",
    +        "magento/module-newsletter": "100.4.*",
    +        "magento/module-integration": "100.4.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/framework": "103.0.*",
    +        "magento/module-directory": "100.4.*",
    +        "magento/module-tax": "100.4.*",
    +        "magento/module-graph-ql-cache": "100.4.*",
    +        "magento/module-graph-ql-resolver-cache": "100.4.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -31,3 +32,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/CustomerImportExport/composer.json+14 12 modified
    @@ -1,24 +1,25 @@
     {
         "name": "magento/module-customer-import-export",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.7",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-backend": "*",
    -        "magento/module-customer": "*",
    -        "magento/module-directory": "*",
    -        "magento/module-eav": "*",
    -        "magento/module-import-export": "*",
    -        "magento/module-store": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-customer": "103.0.*",
    +        "magento/module-directory": "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 @@
             }
         }
     }
    +
    
  • app/code/Magento/Customer/Model/Plugin/UpdateCustomer.php+11 6 modified
    @@ -52,14 +52,19 @@ public function beforeSave(
             CustomerInterface $customer,
             ?string $passwordHash = null
         ): array {
    -        $customerSessionId = $this->userContext->getUserType() === $this->userContext::USER_TYPE_CUSTOMER ?
    -                             (int)$this->userContext->getUserId() : 0;
    +        $userType = $this->userContext->getUserType();
    +        $customerSessionId = (int)$this->userContext->getUserId();
             $customerId = (int)$this->request->getParam('customerId');
             $bodyParams = $this->request->getBodyParams();
    -        if (!isset($bodyParams['customer']['Id']) && $customerId) {
    -            if ($customerId === $customerSessionId || $customerSessionId === 0) {
    -                $customer = $this->getUpdatedCustomer($customerRepository->getById($customerId), $customer);
    -            }
    +
    +        if ($userType === UserContextInterface::USER_TYPE_CUSTOMER &&
    +            !isset($bodyParams['customer']['Id']) &&
    +            $customerId &&
    +            $customerId === $customerSessionId
    +        ) {
    +            $customer = $this->getUpdatedCustomer($customerRepository->getById($customerId), $customer);
    +        } elseif ($userType === UserContextInterface::USER_TYPE_ADMIN && $customerId) {
    +            $customer = $this->getUpdatedCustomer($customerRepository->getById($customerId), $customer);
             }
     
             return [$customer, $passwordHash];
    
  • app/code/Magento/Customer/Plugin/Webapi/Controller/Rest/ValidateCustomerData.php+0 56 removed
    @@ -1,56 +0,0 @@
    -<?php
    -/**
    - * Copyright © Magento, Inc. All rights reserved.
    - * See COPYING.txt for license details.
    - */
    -declare(strict_types=1);
    -
    -namespace Magento\Customer\Plugin\Webapi\Controller\Rest;
    -
    -use Magento\Webapi\Controller\Rest\ParamsOverrider;
    -
    -/**
    - * Validates Customer Data
    - */
    -class ValidateCustomerData
    -{
    -    private const CUSTOMER_KEY = 'customer';
    -
    -    /**
    -     * Before Overriding to validate data
    -     *
    -     * @param ParamsOverrider $subject
    -     * @param array $inputData
    -     * @param array $parameters
    -     * @return array[]
    -     *
    -     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
    -     */
    -    public function beforeOverride(ParamsOverrider $subject, array $inputData, array $parameters): array
    -    {
    -        if (isset($inputData[self::CUSTOMER_KEY])) {
    -            $inputData[self::CUSTOMER_KEY] = $this->validateInputData($inputData[self::CUSTOMER_KEY]);
    -        }
    -        return [$inputData, $parameters];
    -    }
    -
    -    /**
    -     * Validates InputData
    -     *
    -     * @param array $inputData
    -     * @return array
    -     */
    -    private function validateInputData(array $inputData): array
    -    {
    -        $result = [];
    -
    -        $data = array_filter($inputData, function ($k) use (&$result) {
    -            $key = is_string($k) ? strtolower(str_replace('_', "", $k)) : $k;
    -            return !isset($result[$key]) && ($result[$key] = true);
    -        }, ARRAY_FILTER_USE_KEY);
    -
    -        return array_map(function ($value) {
    -            return is_array($value) ? $this->validateInputData($value) : $value;
    -        }, $data);
    -    }
    -}
    
  • app/code/Magento/Customer/Test/Unit/Plugin/Webapi/Controller/Rest/ValidateCustomerDataTest.php+0 123 removed
    @@ -1,123 +0,0 @@
    -<?php
    -/**
    - * Copyright © Magento, Inc. All rights reserved.
    - * See COPYING.txt for license details.
    - */
    -declare(strict_types=1);
    -
    -namespace Magento\Customer\Test\Unit\Plugin\Webapi\Controller\Rest;
    -
    -use Exception;
    -use Magento\Customer\Plugin\Webapi\Controller\Rest\ValidateCustomerData;
    -use Magento\Framework\App\ObjectManager;
    -use PHPUnit\Framework\TestCase;
    -use ReflectionClass;
    -
    -/**
    - * Unit test for ValidateCustomerData plugin
    - */
    -class ValidateCustomerDataTest extends TestCase
    -{
    -
    -    /**
    -     * @var ValidateCustomerData
    -     */
    -    private $validateCustomerDataObject;
    -
    -    /**
    -     * @var ReflectionClass
    -     *
    -     */
    -    private $reflectionObject;
    -
    -    /**
    -     * @inheritdoc
    -     */
    -    protected function setUp(): void
    -    {
    -        $this->validateCustomerDataObject = ObjectManager::getInstance()->get(ValidateCustomerData::class);
    -        $this->reflectionObject = new ReflectionClass(get_class($this->validateCustomerDataObject));
    -    }
    -
    -    /**
    -     * Test if the customer Info is valid
    -     *
    -     * @param array $customerInfo
    -     * @param array $result
    -     * @dataProvider dataProviderInputData
    -     * @throws Exception
    -     */
    -    public function testValidateInputData(array $customerInfo, array $result)
    -    {
    -        $this->assertEquals(
    -            $result,
    -            $this->invokeValidateInputData('validateInputData', [$customerInfo])
    -        );
    -    }
    -
    -    /**
    -     * @param string $methodName
    -     * @param array $arguments
    -     * @return mixed
    -     * @throws Exception
    -     */
    -    private function invokeValidateInputData(string $methodName, array $arguments = [])
    -    {
    -        $validateInputDataMethod = $this->reflectionObject->getMethod($methodName);
    -        $validateInputDataMethod->setAccessible(true);
    -        return $validateInputDataMethod->invokeArgs($this->validateCustomerDataObject, $arguments);
    -    }
    -
    -    /**
    -     * @return array
    -     */
    -    public function dataProviderInputData(): array
    -    {
    -        return [
    -            [
    -                ['customer' => [
    -                        'id' => -1,
    -                        'Id' => 1,
    -                        'name' => [
    -                                'firstName' => 'Test',
    -                                'LastName' => 'user'
    -                            ],
    -                        'isHavingOwnHouse' => 1,
    -                        'address' => [
    -                                'street' => '1st Street',
    -                                'Street' => '3rd Street',
    -                                'city' => 'London'
    -                            ],
    -                    ]
    -                ],
    -                ['customer' => [
    -                        'id' => -1,
    -                        'name' => [
    -                                'firstName' => 'Test',
    -                                'LastName' => 'user'
    -                            ],
    -                        'isHavingOwnHouse' => 1,
    -                        'address' => [
    -                                'street' => '1st Street',
    -                                'city' => 'London'
    -                            ],
    -                    ]
    -                ],
    -                ['customer' => [
    -                    'id' => -1,
    -                    '_Id' => 1,
    -                    'name' => [
    -                        'firstName' => 'Test',
    -                        'LastName' => 'user'
    -                    ],
    -                    'isHavingOwnHouse' => 1,
    -                    'address' => [
    -                        'street' => '1st Street',
    -                        'city' => 'London'
    -                    ],
    -                ]
    -                ],
    -            ]
    -        ];
    -    }
    -}
    
  • app/code/Magento/Deploy/composer.json+12 10 modified
    @@ -1,22 +1,23 @@
     {
         "name": "magento/module-deploy",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.7",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-config": "*",
    -        "magento/module-require-js": "*",
    -        "magento/module-store": "*",
    -        "magento/module-user": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-config": "101.2.*",
    +        "magento/module-require-js": "100.4.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/module-user": "101.2.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "cli_commands.php",
    @@ -27,3 +28,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Developer/composer.json+10 8 modified
    @@ -1,20 +1,21 @@
     {
         "name": "magento/module-developer",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.7",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-config": "*",
    -        "magento/module-store": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-config": "101.2.*",
    +        "magento/module-store": "101.1.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -24,3 +25,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Dhl/composer.json+18 16 modified
    @@ -1,31 +1,32 @@
     {
         "name": "magento/module-dhl",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.6",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
             "lib-libxml": "*",
    -        "magento/framework": "*",
    -        "magento/module-backend": "*",
    -        "magento/module-catalog": "*",
    -        "magento/module-catalog-inventory": "*",
    -        "magento/module-config": "*",
    -        "magento/module-directory": "*",
    -        "magento/module-quote": "*",
    -        "magento/module-sales": "*",
    -        "magento/module-shipping": "*",
    -        "magento/module-store": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-catalog-inventory": "100.4.*",
    +        "magento/module-config": "101.2.*",
    +        "magento/module-directory": "100.4.*",
    +        "magento/module-quote": "101.2.*",
    +        "magento/module-sales": "103.0.*",
    +        "magento/module-shipping": "100.4.*",
    +        "magento/module-store": "101.1.*"
         },
         "suggest": {
    -        "magento/module-checkout": "*"
    +        "magento/module-checkout": "100.4.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -35,3 +36,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Directory/composer.json+11 9 modified
    @@ -1,22 +1,23 @@
     {
         "name": "magento/module-directory",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.7",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
             "lib-libxml": "*",
    -        "magento/framework": "*",
    -        "magento/module-backend": "*",
    -        "magento/module-config": "*",
    -        "magento/module-store": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-config": "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 @@
             }
         }
     }
    +
    
  • app/code/Magento/DirectoryGraphQl/composer.json+9 7 modified
    @@ -2,17 +2,18 @@
         "name": "magento/module-directory-graph-ql",
         "description": "N/A",
         "type": "magento2-module",
    -    "require": {
    -        "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/module-directory": "*",
    -        "magento/module-store": "*",
    -        "magento/module-graph-ql": "*",
    -        "magento/framework": "*"
    -    },
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "100.4.5",
    +    "require": {
    +        "php": "~8.1.0||~8.2.0||~8.3.0",
    +        "magento/module-directory": "100.4.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/module-graph-ql": "100.4.*",
    +        "magento/framework": "103.0.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -22,3 +23,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Downloadable/composer.json+25 23 modified
    @@ -1,37 +1,38 @@
     {
         "name": "magento/module-downloadable",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.7",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-backend": "*",
    -        "magento/module-catalog": "*",
    -        "magento/module-catalog-inventory": "*",
    -        "magento/module-checkout": "*",
    -        "magento/module-config": "*",
    -        "magento/module-customer": "*",
    -        "magento/module-directory": "*",
    -        "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-theme": "*",
    -        "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-config": "101.2.*",
    +        "magento/module-customer": "103.0.*",
    +        "magento/module-directory": "100.4.*",
    +        "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-theme": "101.1.*",
    +        "magento/module-ui": "101.2.*"
         },
         "suggest": {
    -        "magento/module-downloadable-sample-data": "*"
    +        "magento/module-downloadable-sample-data": "Sample Data version: 100.4.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -41,3 +42,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/DownloadableGraphQl/composer.json+15 13 modified
    @@ -2,24 +2,25 @@
         "name": "magento/module-downloadable-graph-ql",
         "description": "N/A",
         "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
    +    "version": "100.4.7",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/module-store": "*",
    -        "magento/module-catalog": "*",
    -        "magento/module-downloadable": "*",
    -        "magento/module-quote": "*",
    -        "magento/module-sales": "*",
    -        "magento/module-quote-graph-ql": "*",
    -        "magento/framework": "*"
    +        "magento/module-store": "101.1.*",
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-downloadable": "100.4.*",
    +        "magento/module-quote": "101.2.*",
    +        "magento/module-sales": "103.0.*",
    +        "magento/module-quote-graph-ql": "100.4.*",
    +        "magento/framework": "103.0.*"
         },
         "suggest": {
    -        "magento/module-catalog-graph-ql": "*",
    -        "magento/module-sales-graph-ql": "*"
    +        "magento/module-catalog-graph-ql": "100.4.*",
    +        "magento/module-sales-graph-ql": "100.4.*"
         },
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -29,3 +30,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/DownloadableImportExport/composer.json+14 12 modified
    @@ -1,24 +1,25 @@
     {
         "name": "magento/module-downloadable-import-export",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.6",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-catalog": "*",
    -        "magento/module-catalog-import-export": "*",
    -        "magento/module-downloadable": "*",
    -        "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-downloadable": "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 @@
             }
         }
     }
    +
    
  • app/code/Magento/Eav/composer.json+13 11 modified
    @@ -1,23 +1,24 @@
     {
         "name": "magento/module-eav",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "102.1.7",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-backend": "*",
    -        "magento/module-catalog": "*",
    -        "magento/module-config": "*",
    -        "magento/module-media-storage": "*",
    -        "magento/module-store": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-config": "101.2.*",
    +        "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"
    @@ -27,3 +28,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/EavGraphQl/composer.json+9 7 modified
    @@ -2,18 +2,19 @@
         "name": "magento/module-eav-graph-ql",
         "description": "N/A",
         "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
    +    "version": "100.4.4",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-eav": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-eav": "102.1.*"
         },
         "suggest": {
    -        "magento/module-graph-ql": "*"
    +        "magento/module-graph-ql": "100.4.*"
         },
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -23,3 +24,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Elasticsearch7/composer.json+13 11 modified
    @@ -1,23 +1,24 @@
     {
         "name": "magento/module-elasticsearch-7",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
    +    "version": "100.4.7",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-elasticsearch": "*",
    +        "magento/framework": "103.0.*",
    +        "magento/module-elasticsearch": "101.0.*",
             "elasticsearch/elasticsearch": "^7.17",
    -        "magento/module-advanced-search": "*",
    -        "magento/module-catalog-search": "*",
    -        "magento/module-search": "*"
    +        "magento/module-advanced-search": "100.4.*",
    +        "magento/module-catalog-search": "102.0.*",
    +        "magento/module-search": "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"
    @@ -27,3 +28,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Elasticsearch/composer.json+17 15 modified
    @@ -1,27 +1,28 @@
     {
         "name": "magento/module-elasticsearch",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
    +    "version": "101.0.7",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/module-advanced-search": "*",
    -        "magento/module-catalog": "*",
    -        "magento/module-catalog-search": "*",
    -        "magento/module-customer": "*",
    -        "magento/module-eav": "*",
    -        "magento/module-search": "*",
    -        "magento/module-store": "*",
    -        "magento/module-catalog-inventory": "*",
    -        "magento/framework": "*",
    +        "magento/module-advanced-search": "100.4.*",
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-catalog-search": "102.0.*",
    +        "magento/module-customer": "103.0.*",
    +        "magento/module-eav": "102.1.*",
    +        "magento/module-search": "101.1.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/module-catalog-inventory": "100.4.*",
    +        "magento/framework": "103.0.*",
             "elasticsearch/elasticsearch": "~7.17.0 || ~8.5.0"
         },
         "suggest": {
    -        "magento/module-config": "*"
    +        "magento/module-config": "101.2.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -31,3 +32,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Email/composer.json+18 16 modified
    @@ -1,30 +1,31 @@
     {
         "name": "magento/module-email",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "101.1.7",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-backend": "*",
    -        "magento/module-cms": "*",
    -        "magento/module-config": "*",
    -        "magento/module-store": "*",
    -        "magento/module-theme": "*",
    -        "magento/module-require-js": "*",
    -        "magento/module-media-storage": "*",
    -        "magento/module-variable": "*",
    -        "magento/module-ui": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-cms": "104.0.*",
    +        "magento/module-config": "101.2.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/module-theme": "101.1.*",
    +        "magento/module-require-js": "100.4.*",
    +        "magento/module-media-storage": "100.4.*",
    +        "magento/module-variable": "100.4.*",
    +        "magento/module-ui": "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"
    @@ -34,3 +35,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/EncryptionKey/composer.json+10 8 modified
    @@ -1,20 +1,21 @@
     {
         "name": "magento/module-encryption-key",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.5",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-backend": "*",
    -        "magento/module-config": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-config": "101.2.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -24,3 +25,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Fedex/composer.json+16 14 modified
    @@ -1,27 +1,28 @@
     {
         "name": "magento/module-fedex",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.5",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
             "lib-libxml": "*",
    -        "magento/framework": "*",
    -        "magento/module-catalog": "*",
    -        "magento/module-catalog-inventory": "*",
    -        "magento/module-config": "*",
    -        "magento/module-directory": "*",
    -        "magento/module-quote": "*",
    -        "magento/module-sales": "*",
    -        "magento/module-shipping": "*",
    -        "magento/module-store": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-catalog-inventory": "100.4.*",
    +        "magento/module-config": "101.2.*",
    +        "magento/module-directory": "100.4.*",
    +        "magento/module-quote": "101.2.*",
    +        "magento/module-sales": "103.0.*",
    +        "magento/module-shipping": "100.4.*",
    +        "magento/module-store": "101.1.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -31,3 +32,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/GiftMessage/composer.json+18 16 modified
    @@ -1,30 +1,31 @@
     {
         "name": "magento/module-gift-message",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.6",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-backend": "*",
    -        "magento/module-catalog": "*",
    -        "magento/module-checkout": "*",
    -        "magento/module-customer": "*",
    -        "magento/module-quote": "*",
    -        "magento/module-sales": "*",
    -        "magento/module-store": "*",
    -        "magento/module-ui": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-checkout": "100.4.*",
    +        "magento/module-customer": "103.0.*",
    +        "magento/module-quote": "101.2.*",
    +        "magento/module-sales": "103.0.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/module-ui": "101.2.*"
         },
         "suggest": {
    -        "magento/module-eav": "*",
    -        "magento/module-multishipping": "*"
    +        "magento/module-eav": "102.1.*",
    +        "magento/module-multishipping": "100.4.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -34,3 +35,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/GiftMessageGraphQl/composer.json+9 7 modified
    @@ -2,18 +2,19 @@
         "name": "magento/module-gift-message-graph-ql",
         "description": "N/A",
         "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
    +    "version": "100.4.5",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-gift-message": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-gift-message": "100.4.*"
         },
         "suggest": {
    -        "magento/module-graph-ql": "*"
    +        "magento/module-graph-ql": "100.4.*"
         },
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -23,3 +24,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/GoogleAdwords/composer.json+10 8 modified
    @@ -1,20 +1,21 @@
     {
         "name": "magento/module-google-adwords",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.4",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-sales": "*",
    -        "magento/module-store": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-sales": "103.0.*",
    +        "magento/module-store": "101.1.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -24,3 +25,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/GoogleAnalytics/composer.json+12 10 modified
    @@ -1,24 +1,25 @@
     {
         "name": "magento/module-google-analytics",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.3",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-cookie": "*",
    -        "magento/module-sales": "*",
    -        "magento/module-store": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-cookie": "100.4.*",
    +        "magento/module-sales": "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"
    @@ -28,3 +29,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/GoogleGtag/composer.json+12 10 modified
    @@ -1,24 +1,25 @@
     {
         "name": "magento/module-google-gtag",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.2",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-cookie": "*",
    -        "magento/module-sales": "*",
    -        "magento/module-store": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-cookie": "100.4.*",
    +        "magento/module-sales": "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"
    @@ -28,3 +29,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/GoogleOptimizer/composer.json+15 13 modified
    @@ -1,25 +1,26 @@
     {
         "name": "magento/module-google-optimizer",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.6",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-backend": "*",
    -        "magento/module-catalog": "*",
    -        "magento/module-cms": "*",
    -        "magento/module-google-analytics": "*",
    -        "magento/module-google-gtag": "*",
    -        "magento/module-store": "*",
    -        "magento/module-ui": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-cms": "104.0.*",
    +        "magento/module-google-analytics": "100.4.*",
    +        "magento/module-google-gtag": "100.4.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/module-ui": "101.2.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -29,3 +30,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/GraphQlCache/composer.json+10 8 modified
    @@ -2,18 +2,19 @@
         "name": "magento/module-graph-ql-cache",
         "description": "N/A",
         "type": "magento2-module",
    -    "require": {
    -        "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-page-cache": "*",
    -        "magento/module-graph-ql": "*",
    -        "magento/module-authorization": "*",
    -        "magento/module-integration": "*"
    -    },
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "100.4.4",
    +    "require": {
    +        "php": "~8.1.0||~8.2.0||~8.3.0",
    +        "magento/framework": "103.0.*",
    +        "magento/module-page-cache": "100.4.*",
    +        "magento/module-graph-ql": "100.4.*",
    +        "magento/module-authorization": "100.4.*",
    +        "magento/module-integration": "100.4.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -23,3 +24,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/GraphQl/composer.json+11 9 modified
    @@ -2,21 +2,22 @@
         "name": "magento/module-graph-ql",
         "description": "N/A",
         "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
    +    "version": "100.4.7",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/module-eav": "*",
    -        "magento/framework": "*",
    -        "magento/module-webapi": "*",
    -        "magento/module-authorization": "*",
    +        "magento/module-eav": "102.1.*",
    +        "magento/framework": "103.0.*",
    +        "magento/module-webapi": "100.4.*",
    +        "magento/module-authorization": "100.4.*",
             "webonyx/graphql-php": "^15.0"
         },
         "suggest": {
    -        "magento/module-graph-ql-cache": "*"
    +        "magento/module-graph-ql-cache": "100.4.*"
         },
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -26,3 +27,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/GraphQlNewRelic/composer.json+8 6 modified
    @@ -2,16 +2,17 @@
         "name": "magento/module-graph-ql-new-relic",
         "description": "N/A",
         "type": "magento2-module",
    -    "require": {
    -        "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-new-relic-reporting": "*",
    -        "magento/module-graph-ql": "*"
    -    },
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "100.4.0",
    +    "require": {
    +        "php": "~8.1.0||~8.2.0||~8.3.0",
    +        "magento/framework": "103.0.*",
    +        "magento/module-new-relic-reporting": "100.4.*",
    +        "magento/module-graph-ql": "100.4.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -21,3 +22,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/GraphQlResolverCache/composer.json+7 5 modified
    @@ -2,15 +2,16 @@
         "name": "magento/module-graph-ql-resolver-cache",
         "description": "N/A",
         "type": "magento2-module",
    -    "require": {
    -        "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-graph-ql": "*"
    -    },
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "100.4.0",
    +    "require": {
    +        "php": "~8.1.0||~8.2.0||~8.3.0",
    +        "magento/framework": "103.0.*",
    +        "magento/module-graph-ql": "100.4.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -20,3 +21,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/GroupedCatalogInventory/composer.json+11 9 modified
    @@ -1,21 +1,22 @@
     {
         "name": "magento/module-grouped-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": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-catalog": "*",
    -        "magento/module-catalog-inventory": "*",
    -        "magento/module-grouped-product": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-catalog-inventory": "100.4.*",
    +        "magento/module-grouped-product": "100.4.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -25,3 +26,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/GroupedImportExport/composer.json+13 11 modified
    @@ -1,23 +1,24 @@
     {
         "name": "magento/module-grouped-import-export",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.5",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-catalog": "*",
    -        "magento/module-catalog-import-export": "*",
    -        "magento/module-eav": "*",
    -        "magento/module-grouped-product": "*",
    -        "magento/module-import-export": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-catalog-import-export": "101.1.*",
    +        "magento/module-eav": "102.1.*",
    +        "magento/module-grouped-product": "100.4.*",
    +        "magento/module-import-export": "101.0.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -27,3 +28,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/GroupedProduct/composer.json+22 20 modified
    @@ -1,34 +1,35 @@
     {
         "name": "magento/module-grouped-product",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.7",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.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-msrp": "*",
    -        "magento/module-quote": "*",
    -        "magento/module-sales": "*",
    -        "magento/module-store": "*",
    -        "magento/module-ui": "*",
    -        "magento/module-wishlist": "*"
    +        "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-msrp": "100.4.*",
    +        "magento/module-quote": "101.2.*",
    +        "magento/module-sales": "103.0.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/module-ui": "101.2.*",
    +        "magento/module-wishlist": "101.2.*"
         },
         "suggest": {
    -        "magento/module-grouped-product-sample-data": "*"
    +        "magento/module-grouped-product-sample-data": "Sample Data version: 100.4.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -38,3 +39,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/GroupedProductGraphQl/composer.json+9 7 modified
    @@ -2,17 +2,18 @@
         "name": "magento/module-grouped-product-graph-ql",
         "description": "N/A",
         "type": "magento2-module",
    -    "require": {
    -        "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/module-grouped-product": "*",
    -        "magento/module-catalog": "*",
    -        "magento/module-catalog-graph-ql": "*",
    -        "magento/framework": "*"
    -    },
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "100.4.7",
    +    "require": {
    +        "php": "~8.1.0||~8.2.0||~8.3.0",
    +        "magento/module-grouped-product": "100.4.*",
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-catalog-graph-ql": "100.4.*",
    +        "magento/framework": "103.0.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -22,3 +23,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/ImportExport/composer.json+14 12 modified
    @@ -1,25 +1,26 @@
     {
         "name": "magento/module-import-export",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "101.0.7",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
             "ext-ctype": "*",
    -        "magento/framework": "*",
    -        "magento/module-backend": "*",
    -        "magento/module-catalog": "*",
    -        "magento/module-eav": "*",
    -        "magento/module-media-storage": "*",
    -        "magento/module-store": "*",
    -        "magento/module-ui": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-eav": "102.1.*",
    +        "magento/module-media-storage": "100.4.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/module-ui": "101.2.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -29,3 +30,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Indexer/composer.json+11 9 modified
    @@ -1,21 +1,22 @@
     {
         "name": "magento/module-indexer",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.7",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/framework-amqp": "*",
    -        "magento/module-backend": "*",
    -        "magento/module-customer": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/framework-amqp": "100.4.*",
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-customer": "103.0.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -25,3 +26,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/InstantPurchase/composer.json+10 8 modified
    @@ -6,16 +6,17 @@
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "100.4.6",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/module-store": "*",
    -        "magento/module-catalog": "*",
    -        "magento/module-customer": "*",
    -        "magento/module-sales": "*",
    -        "magento/module-shipping": "*",
    -        "magento/module-quote": "*",
    -        "magento/module-vault": "*",
    -        "magento/framework": "*"
    +        "magento/module-store": "101.1.*",
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-customer": "103.0.*",
    +        "magento/module-sales": "103.0.*",
    +        "magento/module-shipping": "100.4.*",
    +        "magento/module-quote": "101.2.*",
    +        "magento/module-vault": "101.2.*",
    +        "magento/framework": "103.0.*"
         },
         "autoload": {
             "files": [
    @@ -26,3 +27,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Integration/composer.json+15 13 modified
    @@ -1,25 +1,26 @@
     {
         "name": "magento/module-integration",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.7",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-authorization": "*",
    -        "magento/module-backend": "*",
    -        "magento/module-customer": "*",
    -        "magento/module-security": "*",
    -        "magento/module-store": "*",
    -        "magento/module-user": "*",
    -        "magento/module-ui": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-authorization": "100.4.*",
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-customer": "103.0.*",
    +        "magento/module-security": "100.4.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/module-user": "101.2.*",
    +        "magento/module-ui": "101.2.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -29,3 +30,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/IntegrationGraphQl/composer.json+6 4 modified
    @@ -2,14 +2,15 @@
         "name": "magento/module-integration-graph-ql",
         "description": "N/A",
         "type": "magento2-module",
    -    "require": {
    -        "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*"
    -    },
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "100.4.0",
    +    "require": {
    +        "php": "~8.1.0||~8.2.0||~8.3.0",
    +        "magento/framework": "103.0.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -19,3 +20,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/JwtFrameworkAdapter/composer.json+8 6 modified
    @@ -1,19 +1,20 @@
     {
         "name": "magento/module-jwt-framework-adapter",
         "description": "JWT Manager implementation based on jwt-framework",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.3",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    +        "magento/framework": "103.0.*",
             "web-token/jwt-framework": "^3.1.2"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -23,3 +24,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/JwtUserToken/composer.json+10 8 modified
    @@ -1,20 +1,21 @@
     {
         "name": "magento/module-jwt-user-token",
         "description": "Introduces JWT token support for web API authentication",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.2",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-integration": "*",
    -        "magento/module-authorization": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-integration": "100.4.*",
    +        "magento/module-authorization": "100.4.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -24,3 +25,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/LayeredNavigation/composer.json+10 8 modified
    @@ -1,20 +1,21 @@
     {
         "name": "magento/module-layered-navigation",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.7",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-catalog": "*",
    -        "magento/module-config": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-config": "101.2.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -24,3 +25,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/LoginAsCustomerAdminUi/composer.json+15 14 modified
    @@ -1,24 +1,24 @@
     {
         "name": "magento/module-login-as-customer-admin-ui",
    -    "description": "",
    -    "require": {
    -        "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-login-as-customer-api": "*",
    -        "magento/module-login-as-customer-frontend-ui": "*",
    -        "magento/module-backend": "*",
    -        "magento/module-customer": "*",
    -        "magento/module-sales": "*",
    -        "magento/module-store": "*"
    -    },
    -    "suggest": {
    -        "magento/module-login-as-customer": "*"
    -    },
         "type": "magento2-module",
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "100.4.7",
    +    "require": {
    +        "php": "~8.1.0||~8.2.0||~8.3.0",
    +        "magento/framework": "103.0.*",
    +        "magento/module-login-as-customer-api": "100.4.*",
    +        "magento/module-login-as-customer-frontend-ui": "100.4.*",
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-customer": "103.0.*",
    +        "magento/module-sales": "103.0.*",
    +        "magento/module-store": "101.1.*"
    +    },
    +    "suggest": {
    +        "magento/module-login-as-customer": "100.4.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -28,3 +28,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/LoginAsCustomerApi/composer.json+6 4 modified
    @@ -1,15 +1,16 @@
     {
         "name": "magento/module-login-as-customer-api",
         "description": "Allow for admin to enter a customer account",
    -    "require": {
    -        "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*"
    -    },
         "type": "magento2-module",
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "100.4.6",
    +    "require": {
    +        "php": "~8.1.0||~8.2.0||~8.3.0",
    +        "magento/framework": "103.0.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -19,3 +20,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/LoginAsCustomerAssistance/composer.json+15 14 modified
    @@ -1,24 +1,24 @@
     {
         "name": "magento/module-login-as-customer-assistance",
    -    "description": "",
    -    "require": {
    -        "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-authorization": "*",
    -        "magento/module-backend": "*",
    -        "magento/module-customer": "*",
    -        "magento/module-store": "*",
    -        "magento/module-login-as-customer": "*",
    -        "magento/module-login-as-customer-api": "*"
    -    },
    -    "suggest": {
    -        "magento/module-login-as-customer-admin-ui": "*"
    -    },
         "type": "magento2-module",
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "100.4.6",
    +    "require": {
    +        "php": "~8.1.0||~8.2.0||~8.3.0",
    +        "magento/framework": "103.0.*",
    +        "magento/module-authorization": "100.4.*",
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-customer": "103.0.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/module-login-as-customer": "100.4.*",
    +        "magento/module-login-as-customer-api": "100.4.*"
    +    },
    +    "suggest": {
    +        "magento/module-login-as-customer-admin-ui": "100.4.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -28,3 +28,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/LoginAsCustomer/composer.json+12 10 modified
    @@ -1,21 +1,22 @@
     {
         "name": "magento/module-login-as-customer",
         "description": "Allow for admin to enter a customer account",
    -    "require": {
    -        "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-backend": "*",
    -        "magento/module-customer": "*",
    -        "magento/module-login-as-customer-api": "*"
    -    },
    -    "suggest": {
    -        "magento/module-backend": "*"
    -    },
         "type": "magento2-module",
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "100.4.7",
    +    "require": {
    +        "php": "~8.1.0||~8.2.0||~8.3.0",
    +        "magento/framework": "103.0.*",
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-customer": "103.0.*",
    +        "magento/module-login-as-customer-api": "100.4.*"
    +    },
    +    "suggest": {
    +        "magento/module-backend": "102.0.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -25,3 +26,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/LoginAsCustomerFrontendUi/composer.json+9 8 modified
    @@ -1,18 +1,18 @@
     {
         "name": "magento/module-login-as-customer-frontend-ui",
    -    "description": "",
    -    "require": {
    -        "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-login-as-customer-api": "*",
    -        "magento/module-customer": "*",
    -        "magento/module-store": "*"
    -    },
         "type": "magento2-module",
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "100.4.6",
    +    "require": {
    +        "php": "~8.1.0||~8.2.0||~8.3.0",
    +        "magento/framework": "103.0.*",
    +        "magento/module-login-as-customer-api": "100.4.*",
    +        "magento/module-customer": "103.0.*",
    +        "magento/module-store": "101.1.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -22,3 +22,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/LoginAsCustomerGraphQl/composer.json+14 12 modified
    @@ -1,23 +1,24 @@
     {
         "name": "magento/module-login-as-customer-graph-ql",
         "description": "Flexible login as a customer so a merchant or merchant admin can log into an end customer's account to assist them with their account.",
    -    "require": {
    -        "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-login-as-customer-api": "*",
    -        "magento/module-login-as-customer-assistance": "*",
    -        "magento/module-integration": "*",
    -        "magento/module-store": "*",
    -        "magento/module-customer": "*"
    -    },
    -    "suggest": {
    -        "magento/module-login-as-customer": "*"
    -    },
         "type": "magento2-module",
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "100.4.4",
    +    "require": {
    +        "php": "~8.1.0||~8.2.0||~8.3.0",
    +        "magento/framework": "103.0.*",
    +        "magento/module-login-as-customer-api": "100.4.*",
    +        "magento/module-login-as-customer-assistance": "100.4.*",
    +        "magento/module-integration": "100.4.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/module-customer": "103.0.*"
    +    },
    +    "suggest": {
    +        "magento/module-login-as-customer": "100.4.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -27,3 +28,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/LoginAsCustomerLog/composer.json+14 13 modified
    @@ -1,23 +1,23 @@
     {
         "name": "magento/module-login-as-customer-log",
    -    "description": "",
    -    "require": {
    -        "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-backend": "*",
    -        "magento/module-customer": "*",
    -        "magento/module-login-as-customer-api": "*",
    -        "magento/module-ui": "*",
    -        "magento/module-user": "*"
    -    },
    -    "suggest": {
    -        "magento/module-login-as-customer": "*"
    -    },
         "type": "magento2-module",
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "100.4.5",
    +    "require": {
    +        "php": "~8.1.0||~8.2.0||~8.3.0",
    +        "magento/framework": "103.0.*",
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-customer": "103.0.*",
    +        "magento/module-login-as-customer-api": "100.4.*",
    +        "magento/module-ui": "101.2.*",
    +        "magento/module-user": "101.2.*"
    +    },
    +    "suggest": {
    +        "magento/module-login-as-customer": "100.4.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -27,3 +27,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/LoginAsCustomerPageCache/composer.json+11 10 modified
    @@ -1,20 +1,20 @@
     {
         "name": "magento/module-login-as-customer-page-cache",
    -    "description": "",
    -    "require": {
    -        "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-store": "*",
    -        "magento/module-login-as-customer-api": "*"
    -    },
    -    "suggest": {
    -        "magento/module-page-cache": "*"
    -    },
         "type": "magento2-module",
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "100.4.6",
    +    "require": {
    +        "php": "~8.1.0||~8.2.0||~8.3.0",
    +        "magento/framework": "103.0.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/module-login-as-customer-api": "100.4.*"
    +    },
    +    "suggest": {
    +        "magento/module-page-cache": "100.4.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -24,3 +24,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/LoginAsCustomerQuote/composer.json+12 11 modified
    @@ -1,21 +1,21 @@
     {
         "name": "magento/module-login-as-customer-quote",
    -    "description": "",
    -    "require": {
    -        "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-checkout": "*",
    -        "magento/module-customer": "*",
    -        "magento/module-quote": "*"
    -    },
    -    "suggest": {
    -        "magento/module-login-as-customer-api": "*"
    -    },
         "type": "magento2-module",
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "100.4.5",
    +    "require": {
    +        "php": "~8.1.0||~8.2.0||~8.3.0",
    +        "magento/framework": "103.0.*",
    +        "magento/module-checkout": "100.4.*",
    +        "magento/module-customer": "103.0.*",
    +        "magento/module-quote": "101.2.*"
    +    },
    +    "suggest": {
    +        "magento/module-login-as-customer-api": "100.4.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -25,3 +25,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/LoginAsCustomerSales/composer.json+12 11 modified
    @@ -1,21 +1,21 @@
     {
         "name": "magento/module-login-as-customer-sales",
    -    "description": "",
    -    "require": {
    -        "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-backend": "*",
    -        "magento/module-user": "*",
    -        "magento/module-login-as-customer-api": "*"
    -    },
    -    "suggest": {
    -        "magento/module-sales": "*"
    -    },
         "type": "magento2-module",
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "100.4.6",
    +    "require": {
    +        "php": "~8.1.0||~8.2.0||~8.3.0",
    +        "magento/framework": "103.0.*",
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-user": "101.2.*",
    +        "magento/module-login-as-customer-api": "100.4.*"
    +    },
    +    "suggest": {
    +        "magento/module-sales": "103.0.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -25,3 +25,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Marketplace/composer.json+9 7 modified
    @@ -1,19 +1,20 @@
     {
         "name": "magento/module-marketplace",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.5",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.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 @@
             }
         }
     }
    +
    
  • app/code/Magento/MediaContentApi/composer.json+7 5 modified
    @@ -1,16 +1,17 @@
     {
         "name": "magento/module-media-content-api",
         "description": "Magento module provides the API interfaces for managing relations between content and media files used in that content",
    -    "require": {
    -        "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/module-media-gallery-api": "*",
    -        "magento/framework": "*"
    -    },
         "type": "magento2-module",
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "100.4.6",
    +    "require": {
    +        "php": "~8.1.0||~8.2.0||~8.3.0",
    +        "magento/module-media-gallery-api": "101.0.*",
    +        "magento/framework": "103.0.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -20,3 +21,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/MediaContentCatalog/composer.json+10 8 modified
    @@ -1,19 +1,20 @@
     {
         "name": "magento/module-media-content-catalog",
         "description": "Magento module provides the implementation of MediaContent functionality for Magento_Catalog module",
    -    "require": {
    -        "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/module-media-content-api": "*",
    -        "magento/module-catalog": "*",
    -        "magento/module-eav": "*",
    -        "magento/module-store": "*",
    -        "magento/framework": "*"
    -    },
         "type": "magento2-module",
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "100.4.5",
    +    "require": {
    +        "php": "~8.1.0||~8.2.0||~8.3.0",
    +        "magento/module-media-content-api": "100.4.*",
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-eav": "102.1.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/framework": "103.0.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -23,3 +24,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/MediaContentCms/composer.json+8 6 modified
    @@ -1,17 +1,18 @@
     {
         "name": "magento/module-media-content-cms",
         "description": "Magento module provides the implementation of MediaContent functionality for Magento_Cms module",
    -    "require": {
    -        "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/module-media-content-api": "*",
    -        "magento/module-cms": "*",
    -        "magento/framework": "*"
    -    },
         "type": "magento2-module",
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "100.4.5",
    +    "require": {
    +        "php": "~8.1.0||~8.2.0||~8.3.0",
    +        "magento/module-media-content-api": "100.4.*",
    +        "magento/module-cms": "104.0.*",
    +        "magento/framework": "103.0.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -21,3 +22,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/MediaContent/composer.json+8 6 modified
    @@ -1,17 +1,18 @@
     {
         "name": "magento/module-media-content",
         "description": "Magento module provides the implementation for managing relations between content and media files used in that content",
    -    "require": {
    -        "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-media-content-api": "*",
    -        "magento/module-media-gallery-api": "*"
    -    },
         "type": "magento2-module",
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "100.4.5",
    +    "require": {
    +        "php": "~8.1.0||~8.2.0||~8.3.0",
    +        "magento/framework": "103.0.*",
    +        "magento/module-media-content-api": "100.4.*",
    +        "magento/module-media-gallery-api": "101.0.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -21,3 +22,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/MediaContentSynchronizationApi/composer.json+7 5 modified
    @@ -1,16 +1,17 @@
     {
         "name": "magento/module-media-content-synchronization-api",
         "description": "Magento module responsible for the media content synchronization implementation API",
    -    "require": {
    -        "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-media-content-api": "*"
    -    },
         "type": "magento2-module",
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "100.4.5",
    +    "require": {
    +        "php": "~8.1.0||~8.2.0||~8.3.0",
    +        "magento/framework": "103.0.*",
    +        "magento/module-media-content-api": "100.4.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -20,3 +21,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/MediaContentSynchronizationCatalog/composer.json+9 7 modified
    @@ -1,18 +1,19 @@
     {
         "name": "magento/module-media-content-synchronization-catalog",
         "description": "Magento module provides the implementation of MediaContentSynchronization functionality for Magento_Catalog module",
    -    "require": {
    -        "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-media-content-synchronization-api": "*",
    -        "magento/module-media-gallery-synchronization-api": "*",
    -        "magento/module-media-content-api": "*"
    -    },
         "type": "magento2-module",
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "100.4.4",
    +    "require": {
    +        "php": "~8.1.0||~8.2.0||~8.3.0",
    +        "magento/framework": "103.0.*",
    +        "magento/module-media-content-synchronization-api": "100.4.*",
    +        "magento/module-media-gallery-synchronization-api": "100.4.*",
    +        "magento/module-media-content-api": "100.4.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -22,3 +23,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/MediaContentSynchronizationCms/composer.json+9 7 modified
    @@ -1,18 +1,19 @@
     {
         "name": "magento/module-media-content-synchronization-cms",
         "description": "Magento module provides the implementation of MediaContentSynchronization functionality for Magento_Cms module",
    -    "require": {
    -        "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-media-content-synchronization-api": "*",
    -        "magento/module-media-gallery-synchronization-api": "*",
    -        "magento/module-media-content-api": "*"
    -    },
         "type": "magento2-module",
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "100.4.4",
    +    "require": {
    +        "php": "~8.1.0||~8.2.0||~8.3.0",
    +        "magento/framework": "103.0.*",
    +        "magento/module-media-content-synchronization-api": "100.4.*",
    +        "magento/module-media-gallery-synchronization-api": "100.4.*",
    +        "magento/module-media-content-api": "100.4.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -22,3 +23,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/MediaContentSynchronization/composer.json+13 11 modified
    @@ -1,22 +1,23 @@
     {
         "name": "magento/module-media-content-synchronization",
         "description": "Magento module provides implementation of the media content data synchronization.",
    -    "require": {
    -        "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/framework-bulk": "*",
    -        "magento/module-media-content-synchronization-api": "*",
    -        "magento/module-media-content-api": "*",
    -        "magento/module-asynchronous-operations": "*"
    -    },
    -    "suggest": {
    -        "magento/module-media-gallery-synchronization": "*"
    -    },
         "type": "magento2-module",
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "100.4.6",
    +    "require": {
    +        "php": "~8.1.0||~8.2.0||~8.3.0",
    +        "magento/framework": "103.0.*",
    +        "magento/framework-bulk": "101.0.*",
    +        "magento/module-media-content-synchronization-api": "100.4.*",
    +        "magento/module-media-content-api": "100.4.*",
    +        "magento/module-asynchronous-operations": "100.4.*"
    +    },
    +    "suggest": {
    +        "magento/module-media-gallery-synchronization": "100.4.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -26,3 +27,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/MediaGalleryApi/composer.json+6 4 modified
    @@ -1,15 +1,16 @@
     {
         "name": "magento/module-media-gallery-api",
         "description": "Magento module responsible for media gallery asset attributes storage and management",
    -    "require": {
    -        "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*"
    -    },
         "type": "magento2-module",
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "101.0.6",
    +    "require": {
    +        "php": "~8.1.0||~8.2.0||~8.3.0",
    +        "magento/framework": "103.0.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -19,3 +20,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/MediaGalleryCatalog/composer.json+8 6 modified
    @@ -1,17 +1,18 @@
     {
         "name": "magento/module-media-gallery-catalog",
         "description": "Magento module responsible for catalog gallery processor delete operation handling",
    -    "require": {
    -        "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-media-gallery-api": "*",
    -        "magento/module-catalog": "*"
    -    },
         "type": "magento2-module",
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "100.4.4",
    +    "require": {
    +        "php": "~8.1.0||~8.2.0||~8.3.0",
    +        "magento/framework": "103.0.*",
    +        "magento/module-media-gallery-api": "101.0.*",
    +        "magento/module-catalog": "104.0.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -21,3 +22,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/MediaGalleryCatalogIntegration/composer.json+13 11 modified
    @@ -1,22 +1,23 @@
     {
         "name": "magento/module-media-gallery-catalog-integration",
         "description": "Magento module responsible for extending catalog image uploader functionality",
    -    "require": {
    -        "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-cms": "*",
    -        "magento/module-media-gallery-api": "*",
    -        "magento/module-media-gallery-synchronization-api": "*",
    -        "magento/module-media-gallery-ui-api": "*"
    -    },
    -    "suggest": {
    -        "magento/module-catalog": "*"
    -    },
         "type": "magento2-module",
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "100.4.4",
    +    "require": {
    +        "php": "~8.1.0||~8.2.0||~8.3.0",
    +        "magento/framework": "103.0.*",
    +        "magento/module-cms": "104.0.*",
    +        "magento/module-media-gallery-api": "101.0.*",
    +        "magento/module-media-gallery-synchronization-api": "100.4.*",
    +        "magento/module-media-gallery-ui-api": "100.4.*"
    +    },
    +    "suggest": {
    +        "magento/module-catalog": "104.0.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -26,3 +27,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/MediaGalleryCatalogUi/composer.json+11 9 modified
    @@ -1,20 +1,21 @@
     {
         "name": "magento/module-media-gallery-catalog-ui",
         "description": "Magento module that implement category grid for media gallery.",
    -    "require": {
    -        "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-cms": "*",
    -        "magento/module-backend": "*",
    -        "magento/module-catalog": "*",
    -        "magento/module-store": "*",
    -        "magento/module-ui": "*"
    -    },
         "type": "magento2-module",
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "100.4.4",
    +    "require": {
    +        "php": "~8.1.0||~8.2.0||~8.3.0",
    +        "magento/framework": "103.0.*",
    +        "magento/module-cms": "104.0.*",
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/module-ui": "101.2.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -24,3 +25,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/MediaGalleryCmsUi/composer.json+8 6 modified
    @@ -1,17 +1,18 @@
     {
         "name": "magento/module-media-gallery-cms-ui",
         "description": "Cms related UI elements in the magento media gallery",
    -    "require": {
    -        "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-cms": "*",
    -        "magento/module-backend": "*"
    -    },
         "type": "magento2-module",
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "100.4.4",
    +    "require": {
    +        "php": "~8.1.0||~8.2.0||~8.3.0",
    +        "magento/framework": "103.0.*",
    +        "magento/module-cms": "104.0.*",
    +        "magento/module-backend": "102.0.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -21,3 +22,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/MediaGallery/composer.json+8 6 modified
    @@ -1,17 +1,18 @@
     {
         "name": "magento/module-media-gallery",
         "description": "Magento module responsible for media handling",
    -    "require": {
    -        "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-media-gallery-api": "*",
    -        "magento/module-cms": "*"
    -    },
         "type": "magento2-module",
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "100.4.6",
    +    "require": {
    +        "php": "~8.1.0||~8.2.0||~8.3.0",
    +        "magento/framework": "103.0.*",
    +        "magento/module-media-gallery-api": "101.0.*",
    +        "magento/module-cms": "104.0.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -21,3 +22,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/MediaGalleryIntegration/composer.json+17 15 modified
    @@ -1,32 +1,34 @@
     {
         "name": "magento/module-media-gallery-integration",
         "description": "Magento module responsible for integration of enhanced media gallery",
    -    "require": {
    -        "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-media-gallery-ui-api": "*",
    -        "magento/module-media-gallery-api": "*",
    -        "magento/module-media-gallery-synchronization-api": "*",
    -        "magento/module-ui": "*"
    -    },
    -    "require-dev": {
    -        "magento/module-cms": "*"
    -    },
    -    "suggest": {
    -        "magento/module-catalog": "*",
    -        "magento/module-cms": "*"
    -    },
         "type": "magento2-module",
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "100.4.6",
    +    "require": {
    +        "php": "~8.1.0||~8.2.0||~8.3.0",
    +        "magento/framework": "103.0.*",
    +        "magento/module-media-gallery-ui-api": "100.4.*",
    +        "magento/module-media-gallery-api": "101.0.*",
    +        "magento/module-media-gallery-synchronization-api": "100.4.*",
    +        "magento/module-ui": "101.2.*"
    +    },
    +    "suggest": {
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-cms": "104.0.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
             ],
             "psr-4": {
                 "Magento\\MediaGalleryIntegration\\": ""
             }
    +    },
    +    "require-dev": {
    +        "magento/module-cms": "*"
         }
     }
    +
    
  • app/code/Magento/MediaGalleryMetadataApi/composer.json+6 4 modified
    @@ -1,15 +1,16 @@
     {
         "name": "magento/module-media-gallery-metadata-api",
         "description": "Magento module responsible for media gallery metadata implementation API",
    -    "require": {
    -        "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*"
    -    },
         "type": "magento2-module",
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "100.4.4",
    +    "require": {
    +        "php": "~8.1.0||~8.2.0||~8.3.0",
    +        "magento/framework": "103.0.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -19,3 +20,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/MediaGalleryMetadata/composer.json+7 5 modified
    @@ -1,16 +1,17 @@
     {
         "name": "magento/module-media-gallery-metadata",
         "description": "Magento module responsible for images metadata processing",
    -    "require": {
    -        "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-media-gallery-metadata-api": "*"
    -    },
         "type": "magento2-module",
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "100.4.5",
    +    "require": {
    +        "php": "~8.1.0||~8.2.0||~8.3.0",
    +        "magento/framework": "103.0.*",
    +        "magento/module-media-gallery-metadata-api": "100.4.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -20,3 +21,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/MediaGalleryRenditionsApi/composer.json+6 4 modified
    @@ -1,15 +1,16 @@
     {
         "name": "magento/module-media-gallery-renditions-api",
         "description": "Magento module that is responsible for the API implementation of Media Gallery Renditions.",
    -    "require": {
    -        "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*"
    -    },
         "type": "magento2-module",
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "100.4.4",
    +    "require": {
    +        "php": "~8.1.0||~8.2.0||~8.3.0",
    +        "magento/framework": "103.0.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -19,3 +20,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/MediaGalleryRenditions/composer.json+13 11 modified
    @@ -1,22 +1,23 @@
     {
         "name": "magento/module-media-gallery-renditions",
         "description": "Magento module that implements height and width fields for for media gallery items.",
    -    "require": {
    -        "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-media-gallery-renditions-api": "*",
    -        "magento/module-media-gallery-api": "*",
    -        "magento/framework-message-queue": "*",
    -        "magento/module-cms": "*"
    -    },
    -    "suggest": {
    -        "magento/module-media-content-api": "*"
    -    },
         "type": "magento2-module",
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "100.4.5",
    +    "require": {
    +        "php": "~8.1.0||~8.2.0||~8.3.0",
    +        "magento/framework": "103.0.*",
    +        "magento/module-media-gallery-renditions-api": "100.4.*",
    +        "magento/module-media-gallery-api": "101.0.*",
    +        "magento/framework-message-queue": "100.4.*",
    +        "magento/module-cms": "104.0.*"
    +    },
    +    "suggest": {
    +        "magento/module-media-content-api": "100.4.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -26,3 +27,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/MediaGallerySynchronizationApi/composer.json+7 5 modified
    @@ -1,16 +1,17 @@
     {
         "name": "magento/module-media-gallery-synchronization-api",
         "description": "Magento module responsible for the media gallery synchronization implementation API",
    -    "require": {
    -        "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-media-gallery-api": "*"
    -    },
         "type": "magento2-module",
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "100.4.5",
    +    "require": {
    +        "php": "~8.1.0||~8.2.0||~8.3.0",
    +        "magento/framework": "103.0.*",
    +        "magento/module-media-gallery-api": "101.0.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -20,3 +21,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/MediaGallerySynchronization/composer.json+9 7 modified
    @@ -1,18 +1,19 @@
     {
         "name": "magento/module-media-gallery-synchronization",
         "description": "Magento module provides implementation of the media gallery data synchronization.",
    -    "require": {
    -        "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-media-gallery-api": "*",
    -        "magento/module-media-gallery-synchronization-api": "*",
    -        "magento/framework-message-queue": "*"
    -    },
         "type": "magento2-module",
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "100.4.6",
    +    "require": {
    +        "php": "~8.1.0||~8.2.0||~8.3.0",
    +        "magento/framework": "103.0.*",
    +        "magento/module-media-gallery-api": "101.0.*",
    +        "magento/module-media-gallery-synchronization-api": "100.4.*",
    +        "magento/framework-message-queue": "100.4.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -22,3 +23,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/MediaGallerySynchronizationMetadata/composer.json+9 7 modified
    @@ -1,18 +1,19 @@
     {
         "name": "magento/module-media-gallery-synchronization-metadata",
         "description": "Magento module responsible for images metadata synchronization",
    -    "require": {
    -        "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-media-gallery-api": "*",
    -        "magento/module-media-gallery-metadata-api": "*",
    -        "magento/module-media-gallery-synchronization-api": "*"
    -    },
         "type": "magento2-module",
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "100.4.3",
    +    "require": {
    +        "php": "~8.1.0||~8.2.0||~8.3.0",
    +        "magento/framework": "103.0.*",
    +        "magento/module-media-gallery-api": "101.0.*",
    +        "magento/module-media-gallery-metadata-api": "100.4.*",
    +        "magento/module-media-gallery-synchronization-api": "100.4.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -22,3 +23,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/MediaGalleryUiApi/composer.json+9 7 modified
    @@ -1,18 +1,19 @@
     {
         "name": "magento/module-media-gallery-ui-api",
         "description": "Magento module responsible for the media gallery UI implementation API",
    -    "require": {
    -        "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*"
    -    },
    -    "suggest": {
    -        "magento/module-cms": "*"
    -    },
         "type": "magento2-module",
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "100.4.5",
    +    "require": {
    +        "php": "~8.1.0||~8.2.0||~8.3.0",
    +        "magento/framework": "103.0.*"
    +    },
    +    "suggest": {
    +        "magento/module-cms": "104.0.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -22,3 +23,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/MediaGalleryUi/composer.json+17 15 modified
    @@ -1,26 +1,27 @@
     {
         "name": "magento/module-media-gallery-ui",
         "description": "Magento module responsible for the media gallery UI implementation",
    -    "require": {
    -        "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-backend": "*",
    -        "magento/module-ui": "*",
    -        "magento/module-store": "*",
    -        "magento/module-media-gallery-ui-api": "*",
    -        "magento/module-media-gallery-api": "*",
    -        "magento/module-media-gallery-metadata-api": "*",
    -        "magento/module-media-gallery-synchronization-api": "*",
    -        "magento/module-media-content-api": "*",
    -        "magento/module-cms": "*",
    -        "magento/module-directory": "*",
    -        "magento/module-authorization": "*"
    -    },
         "type": "magento2-module",
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "100.4.6",
    +    "require": {
    +        "php": "~8.1.0||~8.2.0||~8.3.0",
    +        "magento/framework": "103.0.*",
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-ui": "101.2.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/module-media-gallery-ui-api": "100.4.*",
    +        "magento/module-media-gallery-api": "101.0.*",
    +        "magento/module-media-gallery-metadata-api": "100.4.*",
    +        "magento/module-media-gallery-synchronization-api": "100.4.*",
    +        "magento/module-media-content-api": "100.4.*",
    +        "magento/module-cms": "104.0.*",
    +        "magento/module-directory": "100.4.*",
    +        "magento/module-authorization": "100.4.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -30,3 +31,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/MediaStorage/composer.json+16 14 modified
    @@ -1,26 +1,27 @@
     {
         "name": "magento/module-media-storage",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.6",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/framework-bulk": "*",
    -        "magento/module-backend": "*",
    -        "magento/module-config": "*",
    -        "magento/module-store": "*",
    -        "magento/module-catalog": "*",
    -        "magento/module-theme": "*",
    -        "magento/module-asynchronous-operations": "*",
    -        "magento/module-authorization": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/framework-bulk": "101.0.*",
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-config": "101.2.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-theme": "101.1.*",
    +        "magento/module-asynchronous-operations": "100.4.*",
    +        "magento/module-authorization": "100.4.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -30,3 +31,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/MessageQueue/composer.json+9 7 modified
    @@ -1,20 +1,21 @@
     {
         "name": "magento/module-message-queue",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.7",
         "require": {
    -        "magento/framework": "*",
    -        "magento/framework-message-queue": "*",
    +        "magento/framework": "103.0.*",
    +        "magento/framework-message-queue": "100.4.*",
             "magento/magento-composer-installer": "*",
             "php": "~8.1.0||~8.2.0||~8.3.0"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -24,3 +25,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Msrp/composer.json+15 13 modified
    @@ -1,27 +1,28 @@
     {
         "name": "magento/module-msrp",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.6",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-catalog": "*",
    -        "magento/module-downloadable": "*",
    -        "magento/module-eav": "*",
    -        "magento/module-store": "*",
    -        "magento/module-tax": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-downloadable": "100.4.*",
    +        "magento/module-eav": "102.1.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/module-tax": "100.4.*"
         },
         "suggest": {
    -        "magento/module-bundle": "*",
    -        "magento/module-msrp-sample-data": "*"
    +        "magento/module-bundle": "101.0.*",
    +        "magento/module-msrp-sample-data": "Sample Data version: 100.4.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -31,3 +32,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/MsrpConfigurableProduct/composer.json+11 9 modified
    @@ -1,21 +1,22 @@
     {
         "name": "magento/module-msrp-configurable-product",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.4",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-catalog": "*",
    -        "magento/module-msrp": "*",
    -        "magento/module-configurable-product": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-msrp": "100.4.*",
    +        "magento/module-configurable-product": "100.4.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -25,3 +26,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/MsrpGroupedProduct/composer.json+11 9 modified
    @@ -1,21 +1,22 @@
     {
         "name": "magento/module-msrp-grouped-product",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.4",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-catalog": "*",
    -        "magento/module-msrp": "*",
    -        "magento/module-grouped-product": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-msrp": "100.4.*",
    +        "magento/module-grouped-product": "100.4.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -25,3 +26,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Multishipping/composer.json+18 16 modified
    @@ -1,28 +1,29 @@
     {
         "name": "magento/module-multishipping",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.7",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-checkout": "*",
    -        "magento/module-customer": "*",
    -        "magento/module-directory": "*",
    -        "magento/module-payment": "*",
    -        "magento/module-quote": "*",
    -        "magento/module-sales": "*",
    -        "magento/module-store": "*",
    -        "magento/module-tax": "*",
    -        "magento/module-theme": "*",
    -        "magento/module-captcha": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-checkout": "100.4.*",
    +        "magento/module-customer": "103.0.*",
    +        "magento/module-directory": "100.4.*",
    +        "magento/module-payment": "100.4.*",
    +        "magento/module-quote": "101.2.*",
    +        "magento/module-sales": "103.0.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/module-tax": "100.4.*",
    +        "magento/module-theme": "101.1.*",
    +        "magento/module-captcha": "100.4.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -32,3 +33,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/MysqlMq/composer.json+10 8 modified
    @@ -1,21 +1,22 @@
     {
         "name": "magento/module-mysql-mq",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.5",
         "require": {
    -        "magento/framework": "*",
    -        "magento/framework-message-queue": "*",
    +        "magento/framework": "103.0.*",
    +        "magento/framework-message-queue": "100.4.*",
             "magento/magento-composer-installer": "*",
    -        "magento/module-store": "*",
    +        "magento/module-store": "101.1.*",
             "php": "~8.1.0||~8.2.0||~8.3.0"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -25,3 +26,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/NewRelicReporting/composer.json+14 12 modified
    @@ -1,25 +1,26 @@
     {
         "name": "magento/module-new-relic-reporting",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.5",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    +        "magento/framework": "103.0.*",
             "magento/magento-composer-installer": "*",
    -        "magento/module-backend": "*",
    -        "magento/module-catalog": "*",
    -        "magento/module-config": "*",
    -        "magento/module-configurable-product": "*",
    -        "magento/module-customer": "*",
    -        "magento/module-store": "*"
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-config": "101.2.*",
    +        "magento/module-configurable-product": "100.4.*",
    +        "magento/module-customer": "103.0.*",
    +        "magento/module-store": "101.1.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -29,3 +30,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Newsletter/composer.json+17 15 modified
    @@ -1,27 +1,28 @@
     {
         "name": "magento/module-newsletter",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.7",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-backend": "*",
    -        "magento/module-cms": "*",
    -        "magento/module-customer": "*",
    -        "magento/module-eav": "*",
    -        "magento/module-email": "*",
    -        "magento/module-require-js": "*",
    -        "magento/module-store": "*",
    -        "magento/module-widget": "*",
    -        "magento/module-ui": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-cms": "104.0.*",
    +        "magento/module-customer": "103.0.*",
    +        "magento/module-eav": "102.1.*",
    +        "magento/module-email": "101.1.*",
    +        "magento/module-require-js": "100.4.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/module-widget": "101.2.*",
    +        "magento/module-ui": "101.2.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -31,3 +32,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/NewsletterGraphQl/composer.json+13 11 modified
    @@ -1,23 +1,24 @@
     {
         "name": "magento/module-newsletter-graph-ql",
         "description": "Provides GraphQl functionality for the newsletter subscriptions.",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    -    "type": "magento2-module",
    +    "version": "100.4.4",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-customer": "*",
    -        "magento/module-newsletter": "*",
    -        "magento/module-store": "*",
    -        "magento/module-graph-ql": "*",
    -        "magento/module-graph-ql-resolver-cache": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-customer": "103.0.*",
    +        "magento/module-newsletter": "100.4.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/module-graph-ql": "100.4.*",
    +        "magento/module-graph-ql-resolver-cache": "100.4.*"
         },
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -27,3 +28,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/OfflinePayments/composer.json+12 10 modified
    @@ -1,24 +1,25 @@
     {
         "name": "magento/module-offline-payments",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.5",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-checkout": "*",
    -        "magento/module-payment": "*",
    -        "magento/module-quote": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-checkout": "100.4.*",
    +        "magento/module-payment": "100.4.*",
    +        "magento/module-quote": "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"
    @@ -28,3 +29,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/OfflineShipping/composer.json+20 18 modified
    @@ -1,32 +1,33 @@
     {
         "name": "magento/module-offline-shipping",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.6",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-backend": "*",
    -        "magento/module-catalog": "*",
    -        "magento/module-config": "*",
    -        "magento/module-directory": "*",
    -        "magento/module-quote": "*",
    -        "magento/module-sales": "*",
    -        "magento/module-sales-rule": "*",
    -        "magento/module-shipping": "*",
    -        "magento/module-store": "*",
    -        "magento/module-async-config": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-config": "101.2.*",
    +        "magento/module-directory": "100.4.*",
    +        "magento/module-quote": "101.2.*",
    +        "magento/module-sales": "103.0.*",
    +        "magento/module-sales-rule": "101.2.*",
    +        "magento/module-shipping": "100.4.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/module-async-config": "100.4.*"
         },
         "suggest": {
    -        "magento/module-checkout": "*",
    -        "magento/module-offline-shipping-sample-data": "*"
    +        "magento/module-checkout": "100.4.*",
    +        "magento/module-offline-shipping-sample-data": "Sample Data version: 100.4.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -36,3 +37,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/OpenSearch/composer.json+12 10 modified
    @@ -1,21 +1,22 @@
     {
         "name": "magento/module-open-search",
         "description": "N/A",
    -    "require": {
    -        "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-advanced-search": "*",
    -        "magento/module-catalog-search": "*",
    -        "magento/module-elasticsearch": "*",
    -        "magento/module-search": "*",
    -        "magento/module-config": "*",
    -        "opensearch-project/opensearch-php": "^1.0 || ^2.0"
    -    },
         "type": "magento2-module",
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "100.4.1",
    +    "require": {
    +        "php": "~8.1.0||~8.2.0||~8.3.0",
    +        "magento/framework": "103.0.*",
    +        "magento/module-advanced-search": "100.4.*",
    +        "magento/module-catalog-search": "102.0.*",
    +        "magento/module-elasticsearch": "101.0.*",
    +        "magento/module-search": "101.1.*",
    +        "magento/module-config": "101.2.*",
    +        "opensearch-project/opensearch-php": "^1.0 || ^2.0"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -25,3 +26,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/OrderCancellation/composer.json+11 9 modified
    @@ -1,21 +1,22 @@
     {
         "name": "magento/module-order-cancellation",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.0",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-config": "*",
    -        "magento/module-store": "*",
    -        "magento/module-sales": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-config": "101.2.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/module-sales": "103.0.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -25,3 +26,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/OrderCancellationGraphQl/composer.json+11 9 modified
    @@ -1,21 +1,22 @@
     {
         "name": "magento/module-order-cancellation-graph-ql",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.0",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-sales": "*",
    -        "magento/module-sales-graph-ql": "*",
    -        "magento/module-order-cancellation": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-sales": "103.0.*",
    +        "magento/module-sales-graph-ql": "100.4.*",
    +        "magento/module-order-cancellation": "100.4.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -25,3 +26,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/OrderCancellationUi/composer.json+9 7 modified
    @@ -1,18 +1,19 @@
     {
         "name": "magento/module-order-cancellation-ui",
         "description": "Magento module that implements order cancellation UI.",
    -    "require": {
    -        "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-customer": "*",
    -        "magento/module-order-cancellation": "*",
    -        "magento/module-sales": "*"
    -    },
         "type": "magento2-module",
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "100.4.0",
    +    "require": {
    +        "php": "~8.1.0||~8.2.0||~8.3.0",
    +        "magento/framework": "103.0.*",
    +        "magento/module-customer": "103.0.*",
    +        "magento/module-order-cancellation": "100.4.*",
    +        "magento/module-sales": "103.0.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -22,3 +23,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/PageCache/composer.json+12 10 modified
    @@ -1,22 +1,23 @@
     {
         "name": "magento/module-page-cache",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.7",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-backend": "*",
    -        "magento/module-config": "*",
    -        "magento/module-store": "*",
    -        "magento/module-catalog": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-config": "101.2.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/module-catalog": "104.0.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -26,3 +27,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Payment/composer.json+15 13 modified
    @@ -1,25 +1,26 @@
     {
         "name": "magento/module-payment",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.7",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-checkout": "*",
    -        "magento/module-config": "*",
    -        "magento/module-directory": "*",
    -        "magento/module-quote": "*",
    -        "magento/module-sales": "*",
    -        "magento/module-store": "*",
    -        "magento/module-ui": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-checkout": "100.4.*",
    +        "magento/module-config": "101.2.*",
    +        "magento/module-directory": "100.4.*",
    +        "magento/module-quote": "101.2.*",
    +        "magento/module-sales": "103.0.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/module-ui": "101.2.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -29,3 +30,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/PaymentGraphQl/composer.json+10 8 modified
    @@ -2,19 +2,20 @@
         "name": "magento/module-payment-graph-ql",
         "description": "N/A",
         "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
    +    "version": "100.4.2",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-payment": "*",
    -        "magento/module-graph-ql": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-payment": "100.4.*",
    +        "magento/module-graph-ql": "100.4.*"
         },
         "suggest": {
    -        "magento/module-store-graph-ql": "*"
    +        "magento/module-store-graph-ql": "100.4.*"
         },
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -24,3 +25,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/PaypalCaptcha/composer.json+12 10 modified
    @@ -1,24 +1,25 @@
     {
         "name": "magento/module-paypal-captcha",
         "description": "Provides CAPTCHA validation for PayPal Payflow Pro",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.4",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-captcha": "*",
    -        "magento/module-checkout": "*",
    -        "magento/module-store": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-captcha": "100.4.*",
    +        "magento/module-checkout": "100.4.*",
    +        "magento/module-store": "101.1.*"
         },
         "suggest": {
    -        "magento/module-paypal": "*"
    +        "magento/module-paypal": "101.0.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -28,3 +29,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Paypal/composer.json+27 25 modified
    @@ -1,40 +1,41 @@
     {
         "name": "magento/module-paypal",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "101.0.7",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
             "lib-libxml": "*",
    -        "magento/framework": "*",
    -        "magento/module-authorization": "*",
    -        "magento/module-backend": "*",
    -        "magento/module-catalog": "*",
    -        "magento/module-checkout": "*",
    -        "magento/module-config": "*",
    -        "magento/module-customer": "*",
    -        "magento/module-directory": "*",
    -        "magento/module-eav": "*",
    -        "magento/module-instant-purchase": "*",
    -        "magento/module-payment": "*",
    -        "magento/module-quote": "*",
    -        "magento/module-sales": "*",
    -        "magento/module-store": "*",
    -        "magento/module-tax": "*",
    -        "magento/module-theme": "*",
    -        "magento/module-ui": "*",
    -        "magento/module-vault": "*",
    -        "magento/module-csp": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-authorization": "100.4.*",
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-checkout": "100.4.*",
    +        "magento/module-config": "101.2.*",
    +        "magento/module-customer": "103.0.*",
    +        "magento/module-directory": "100.4.*",
    +        "magento/module-eav": "102.1.*",
    +        "magento/module-instant-purchase": "100.4.*",
    +        "magento/module-payment": "100.4.*",
    +        "magento/module-quote": "101.2.*",
    +        "magento/module-sales": "103.0.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/module-tax": "100.4.*",
    +        "magento/module-theme": "101.1.*",
    +        "magento/module-ui": "101.2.*",
    +        "magento/module-vault": "101.2.*",
    +        "magento/module-csp": "100.4.*"
         },
         "suggest": {
    -        "magento/module-checkout-agreements": "*"
    +        "magento/module-checkout-agreements": "100.4.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -44,3 +45,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/PaypalGraphQl/composer.json+18 16 modified
    @@ -1,30 +1,31 @@
     {
         "name": "magento/module-paypal-graph-ql",
         "description": "GraphQl support for Paypal",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.5",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-quote": "*",
    -        "magento/module-checkout": "*",
    -        "magento/module-paypal": "*",
    -        "magento/module-quote-graph-ql": "*",
    -        "magento/module-sales": "*",
    -        "magento/module-payment": "*",
    -        "magento/module-store": "*",
    -        "magento/module-vault": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-quote": "101.2.*",
    +        "magento/module-checkout": "100.4.*",
    +        "magento/module-paypal": "101.0.*",
    +        "magento/module-quote-graph-ql": "100.4.*",
    +        "magento/module-sales": "103.0.*",
    +        "magento/module-payment": "100.4.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/module-vault": "101.2.*"
         },
         "suggest": {
    -        "magento/module-graph-ql": "*",
    -        "magento/module-store-graph-ql": "*"
    +        "magento/module-graph-ql": "100.4.*",
    +        "magento/module-store-graph-ql": "100.4.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -34,3 +35,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Persistent/composer.json+15 13 modified
    @@ -1,27 +1,28 @@
     {
         "name": "magento/module-persistent",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.7",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-checkout": "*",
    -        "magento/module-cron": "*",
    -        "magento/module-customer": "*",
    -        "magento/module-page-cache": "*",
    -        "magento/module-quote": "*",
    -        "magento/module-store": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-checkout": "100.4.*",
    +        "magento/module-cron": "100.4.*",
    +        "magento/module-customer": "103.0.*",
    +        "magento/module-page-cache": "100.4.*",
    +        "magento/module-quote": "101.2.*",
    +        "magento/module-store": "101.1.*"
         },
         "suggest": {
    -        "magento/module-login-as-customer-api": "*"
    +        "magento/module-login-as-customer-api": "100.4.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -31,3 +32,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/ProductAlert/composer.json+17 15 modified
    @@ -1,29 +1,30 @@
     {
         "name": "magento/module-product-alert",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.6",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/framework-bulk": "*",
    -        "magento/module-asynchronous-operations": "*",
    -        "magento/module-authorization": "*",
    -        "magento/module-backend": "*",
    -        "magento/module-catalog": "*",
    -        "magento/module-customer": "*",
    -        "magento/module-store": "*",
    -        "magento/module-theme": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/framework-bulk": "101.0.*",
    +        "magento/module-asynchronous-operations": "100.4.*",
    +        "magento/module-authorization": "100.4.*",
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-customer": "103.0.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/module-theme": "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"
    @@ -33,3 +34,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/ProductVideo/composer.json+16 14 modified
    @@ -1,29 +1,30 @@
     {
         "name": "magento/module-product-video",
         "description": "Add Video to Products",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.7",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    +        "magento/framework": "103.0.*",
             "magento/magento-composer-installer": "*",
    -        "magento/module-backend": "*",
    -        "magento/module-catalog": "*",
    -        "magento/module-eav": "*",
    -        "magento/module-media-storage": "*",
    -        "magento/module-store": "*"
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-eav": "102.1.*",
    +        "magento/module-media-storage": "100.4.*",
    +        "magento/module-store": "101.1.*"
         },
         "suggest": {
    -        "magento/module-customer": "*",
    -        "magento/module-config": "*",
    -        "magento/module-theme": "*"
    +        "magento/module-customer": "103.0.*",
    +        "magento/module-config": "101.2.*",
    +        "magento/module-theme": "101.1.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -33,3 +34,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/QuoteAnalytics/composer.json+8 6 modified
    @@ -1,17 +1,18 @@
     {
         "name": "magento/module-quote-analytics",
         "description": "N/A",
    -    "require": {
    -        "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-quote": "*",
    -        "magento/module-analytics": "*"
    -    },
         "type": "magento2-module",
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "100.4.6",
    +    "require": {
    +        "php": "~8.1.0||~8.2.0||~8.3.0",
    +        "magento/framework": "103.0.*",
    +        "magento/module-quote": "101.2.*",
    +        "magento/module-analytics": "100.4.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -21,3 +22,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/QuoteBundleOptions/composer.json+7 5 modified
    @@ -1,16 +1,17 @@
     {
         "name": "magento/module-quote-bundle-options",
         "description": "Magento module provides data provider for creating buy request for bundle products",
    -    "require": {
    -        "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-quote": "*"
    -    },
         "type": "magento2-module",
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "100.4.3",
    +    "require": {
    +        "php": "~8.1.0||~8.2.0||~8.3.0",
    +        "magento/framework": "103.0.*",
    +        "magento/module-quote": "101.2.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -20,3 +21,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Quote/composer.json+23 21 modified
    @@ -1,35 +1,36 @@
     {
         "name": "magento/module-quote",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "101.2.7-p1",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-authorization": "*",
    -        "magento/module-backend": "*",
    -        "magento/module-catalog": "*",
    -        "magento/module-catalog-inventory": "*",
    -        "magento/module-checkout": "*",
    -        "magento/module-customer": "*",
    -        "magento/module-directory": "*",
    -        "magento/module-eav": "*",
    -        "magento/module-payment": "*",
    -        "magento/module-sales": "*",
    -        "magento/module-sales-sequence": "*",
    -        "magento/module-shipping": "*",
    -        "magento/module-store": "*",
    -        "magento/module-tax": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-authorization": "100.4.*",
    +        "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-directory": "100.4.*",
    +        "magento/module-eav": "102.1.*",
    +        "magento/module-payment": "100.4.*",
    +        "magento/module-sales": "103.0.*",
    +        "magento/module-sales-sequence": "100.4.*",
    +        "magento/module-shipping": "100.4.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/module-tax": "100.4.*"
         },
         "suggest": {
    -        "magento/module-webapi": "*"
    +        "magento/module-webapi": "100.4.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -39,3 +40,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/QuoteConfigurableOptions/composer.json+7 5 modified
    @@ -1,16 +1,17 @@
     {
         "name": "magento/module-quote-configurable-options",
         "description": "Magento module provides data provider for creating buy request for configurable products",
    -    "require": {
    -        "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-quote": "*"
    -    },
         "type": "magento2-module",
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "100.4.3",
    +    "require": {
    +        "php": "~8.1.0||~8.2.0||~8.3.0",
    +        "magento/framework": "103.0.*",
    +        "magento/module-quote": "101.2.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -20,3 +21,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/QuoteDownloadableLinks/composer.json+7 5 modified
    @@ -1,16 +1,17 @@
     {
         "name": "magento/module-quote-downloadable-links",
         "description": "Magento module provides data provider for creating buy request for links of downloadable products",
    -    "require": {
    -        "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-quote": "*"
    -    },
         "type": "magento2-module",
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "100.4.3",
    +    "require": {
    +        "php": "~8.1.0||~8.2.0||~8.3.0",
    +        "magento/framework": "103.0.*",
    +        "magento/module-quote": "101.2.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -20,3 +21,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Quote/etc/webapi_rest/di.xml+3 2 modified
    @@ -18,8 +18,9 @@
         </type>
         <type name="Magento\Quote\Model\Quote">
             <plugin name="updateQuoteStoreId" type="Magento\Quote\Model\Quote\Plugin\UpdateQuoteStoreId" />
    +        <plugin name="validateQuoteAddress" type="Magento\Quote\Plugin\QuoteAddress" />
         </type>
    -    <type name="Magento\Webapi\Controller\Rest\ParamsOverrider">
    -        <plugin name="validateQuoteData" type="Magento\Quote\Plugin\Webapi\Controller\Rest\ValidateQuoteData" sortOrder="1" disabled="false" />
    +    <type name="Magento\Quote\Api\CartRepositoryInterface">
    +        <plugin name="quoteValidateOrderId" type="Magento\Quote\Plugin\ValidateQuoteOrigOrder"/>
         </type>
     </config>
    
  • app/code/Magento/QuoteGraphQl/composer.json+23 21 modified
    @@ -2,32 +2,33 @@
         "name": "magento/module-quote-graph-ql",
         "description": "N/A",
         "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
    +    "version": "100.4.7",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-quote": "*",
    -        "magento/module-checkout": "*",
    -        "magento/module-catalog": "*",
    -        "magento/module-store": "*",
    -        "magento/module-customer": "*",
    -        "magento/module-customer-graph-ql": "*",
    -        "magento/module-sales": "*",
    -        "magento/module-sales-graph-ql": "*",
    -        "magento/module-directory": "*",
    -        "magento/module-graph-ql": "*",
    -        "magento/module-gift-message": "*",
    -        "magento/module-catalog-inventory": "*",
    -        "magento/module-eav-graph-ql": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-quote": "101.2.*",
    +        "magento/module-checkout": "100.4.*",
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/module-customer": "103.0.*",
    +        "magento/module-customer-graph-ql": "100.4.*",
    +        "magento/module-sales": "103.0.*",
    +        "magento/module-sales-graph-ql": "100.4.*",
    +        "magento/module-directory": "100.4.*",
    +        "magento/module-graph-ql": "100.4.*",
    +        "magento/module-gift-message": "100.4.*",
    +        "magento/module-catalog-inventory": "100.4.*",
    +        "magento/module-eav-graph-ql": "100.4.*"
         },
         "suggest": {
    -        "magento/module-graph-ql-cache": "*",
    -        "magento/module-catalog-inventory-graph-ql": "*",
    -        "magento/module-payment-graph-ql": "*"
    +        "magento/module-graph-ql-cache": "100.4.*",
    +        "magento/module-catalog-inventory-graph-ql": "100.4.*",
    +        "magento/module-payment-graph-ql": "100.4.*"
         },
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -37,3 +38,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Quote/i18n/en_US.csv+1 0 modified
    @@ -74,3 +74,4 @@ Carts,Carts
     "Please select a valid rate limit period in seconds: %1.","Please select a valid rate limit period in seconds: %1."
     "Identity type not found","Identity type not found"
     "Invalid order backpressure limit config","Invalid order backpressure limit config"
    +"Please check input parameters.","Please check input parameters."
    
  • app/code/Magento/Quote/Model/BillingAddressManagement.php+0 4 modified
    @@ -77,10 +77,6 @@ public function assign($cartId, AddressInterface $address, $useForShipping = fal
         {
             /** @var \Magento\Quote\Model\Quote $quote */
             $quote = $this->quoteRepository->getActive($cartId);
    -
    -        // validate the address
    -        $this->addressValidator->validateWithExistingAddress($quote, $address);
    -
             $address->setCustomerId($quote->getCustomerId());
             $quote->removeAddress($quote->getBillingAddress()->getId());
             $quote->setBillingAddress($address);
    
  • app/code/Magento/Quote/Model/QuoteAddressValidator.php+2 26 modified
    @@ -121,27 +121,6 @@ public function validate(AddressInterface $addressData): bool
             return true;
         }
     
    -    /**
    -     * Validate Quest Address for guest user
    -     *
    -     * @param AddressInterface $address
    -     * @param CartInterface $cart
    -     * @return void
    -     * @throws NoSuchEntityException
    -     */
    -    private function doValidateForGuestQuoteAddress(AddressInterface $address, CartInterface $cart): void
    -    {
    -        //validate guest cart address
    -        if ($address->getId() !== null) {
    -            $old = $cart->getAddressById($address->getId());
    -            if ($old === false) {
    -                throw new NoSuchEntityException(
    -                    __('Invalid quote address id %1', $address->getId())
    -                );
    -            }
    -        }
    -    }
    -
         /**
          * Validate address to be used for cart.
          *
    @@ -153,9 +132,6 @@ private function doValidateForGuestQuoteAddress(AddressInterface $address, CartI
          */
         public function validateForCart(CartInterface $cart, AddressInterface $address): void
         {
    -        if ($cart->getCustomerIsGuest()) {
    -            $this->doValidateForGuestQuoteAddress($address, $cart);
    -        }
             $this->doValidate($address, $cart->getCustomerIsGuest() ? null : (int) $cart->getCustomer()->getId());
         }
     
    @@ -171,8 +147,8 @@ public function validateWithExistingAddress(CartInterface $cart, AddressInterfac
         {
             // check if address belongs to quote.
             if ($address->getId() !== null) {
    -            $old = $cart->getAddressesCollection()->getItemById($address->getId());
    -            if ($old === null) {
    +            $old = $cart->getAddressById($address->getId());
    +            if (empty($old)) {
                     throw new NoSuchEntityException(
                         __('Invalid quote address id %1', $address->getId())
                     );
    
  • app/code/Magento/Quote/Plugin/QuoteAddress.php+67 0 added
    @@ -0,0 +1,67 @@
    +<?php
    +/**
    + * Copyright © Magento, Inc. All rights reserved.
    + * See COPYING.txt for license details.
    + */
    +declare(strict_types=1);
    +
    +namespace Magento\Quote\Plugin;
    +
    +use Magento\Framework\Exception\NoSuchEntityException;
    +use Magento\Quote\Model\Quote;
    +use Magento\Quote\Api\Data\AddressInterface;
    +use Magento\Quote\Model\QuoteAddressValidator;
    +
    +/**
    + * Quote address plugin
    + */
    +class QuoteAddress
    +{
    +    /**
    +     * @var QuoteAddressValidator
    +     */
    +    protected QuoteAddressValidator $addressValidator;
    +
    +    /**
    +     * @param QuoteAddressValidator $addressValidator
    +     */
    +    public function __construct(
    +        QuoteAddressValidator $addressValidator
    +    ) {
    +        $this->addressValidator = $addressValidator;
    +    }
    +
    +    /**
    +     * Validate address before setting billing address
    +     *
    +     * @param Quote $subject
    +     * @param AddressInterface|null $address
    +     * @return array
    +     * @throws NoSuchEntityException
    +     */
    +    public function beforeSetBillingAddress(Quote $subject, AddressInterface $address = null): array
    +    {
    +        if ($address !== null) {
    +            $this->addressValidator->validateWithExistingAddress($subject, $address);
    +        }
    +
    +        return [$address];
    +    }
    +
    +    /**
    +     * Validate address before setting shipping address
    +     *
    +     * @param Quote $subject
    +     * @param AddressInterface|null $address
    +     * @return array
    +     * @throws NoSuchEntityException
    +     */
    +    public function beforeSetShippingAddress(Quote $subject, AddressInterface $address = null): array
    +    {
    +        if ($address !== null) {
    +            $this->addressValidator->validateWithExistingAddress($subject, $address);
    +        }
    +
    +        return [$address];
    +    }
    +}
    
  • app/code/Magento/Quote/Plugin/ValidateQuoteOrigOrder.php+65 0 added
    @@ -0,0 +1,65 @@
    +<?php
    +/**
    + * Copyright © Magento, Inc. All rights reserved.
    + * See COPYING.txt for license details.
    + */
    +
    +declare(strict_types=1);
    +
    +namespace Magento\Quote\Plugin;
    +
    +use Magento\Framework\Exception\NoSuchEntityException;
    +use Magento\Framework\Webapi\Rest\Request as RestRequest;
    +use Magento\Quote\Api\CartRepositoryInterface;
    +use Magento\Quote\Api\Data\CartInterface;
    +use Magento\Sales\Api\OrderRepositoryInterface;
    +
    +/**
    + * Validate order id from request param
    + */
    +class ValidateQuoteOrigOrder
    +{
    +    /**
    +     * @var OrderRepositoryInterface
    +     */
    +    private $orderRepository;
    +
    +    /**
    +     * @var RestRequest $request
    +     */
    +    private $request;
    +
    +    /**
    +     * @param RestRequest $request
    +     * @param OrderRepositoryInterface $orderRepository
    +     */
    +    public function __construct(RestRequest $request, OrderRepositoryInterface $orderRepository)
    +    {
    +        $this->request = $request;
    +        $this->orderRepository = $orderRepository;
    +    }
    +
    +    /**
    +     * Validate the user authorization to order
    +     *
    +     * @param CartRepositoryInterface $cartRepository
    +     * @param CartInterface $quote
    +     * @return void
    +     * @throws NoSuchEntityException
    +     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
    +     */
    +    public function beforeSave(
    +        CartRepositoryInterface $cartRepository,
    +        CartInterface $quote
    +    ): void {
    +        $params = $this->request->getBodyParams();
    +        if (!empty($params) && isset($params['quote']['orig_order_id'])) {
    +            $orderId = $params['quote']['orig_order_id'];
    +            $order = $this->orderRepository->get($orderId);
    +            $orderCustomer = (int)$order->getCustomerId();
    +            if ($quote->getCustomerId() !== $orderCustomer) {
    +                throw new NoSuchEntityException(__('Please check input parameters.'));
    +            }
    +        }
    +    }
    +}
    
  • app/code/Magento/Quote/Plugin/Webapi/Controller/Rest/ValidateQuoteData.php+0 56 removed
    @@ -1,56 +0,0 @@
    -<?php
    -/**
    - * Copyright © Magento, Inc. All rights reserved.
    - * See COPYING.txt for license details.
    - */
    -declare(strict_types=1);
    -namespace Magento\Quote\Plugin\Webapi\Controller\Rest;
    -
    -use Magento\Webapi\Controller\Rest\ParamsOverrider;
    -
    -/**
    - * Validates Quote Data
    - */
    -class ValidateQuoteData
    -{
    -    private const QUOTE_KEY = 'quote';
    -
    -    /**
    -     * Before Overriding to validate data
    -     *
    -     * @param ParamsOverrider $subject
    -     * @param array $inputData
    -     * @param array $parameters
    -     * @return array[]
    -     *
    -     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
    -     */
    -
    -    public function beforeOverride(ParamsOverrider $subject, array $inputData, array $parameters): array
    -    {
    -        if (isset($inputData[self:: QUOTE_KEY])) {
    -            $inputData[self:: QUOTE_KEY] = $this->validateInputData($inputData[self:: QUOTE_KEY]);
    -        };
    -        return [$inputData, $parameters];
    -    }
    -
    -    /**
    -     * Validates InputData
    -     *
    -     * @param array $inputData
    -     * @return array
    -     */
    -    private function validateInputData(array $inputData): array
    -    {
    -        $result = [];
    -
    -        $data = array_filter($inputData, function ($k) use (&$result) {
    -            $key = is_string($k) ? strtolower($k) : $k;
    -            return !isset($result[$key]) && ($result[$key] = true);
    -        }, ARRAY_FILTER_USE_KEY);
    -
    -        return array_map(function ($value) {
    -            return is_array($value) ? $this->validateInputData($value) : $value;
    -        }, $data);
    -    }
    -}
    
  • app/code/Magento/Quote/Test/Unit/Plugin/Webapi/Controller/Rest/ValidateQuoteDataTest.php+0 114 removed
    @@ -1,114 +0,0 @@
    -<?php
    -/**
    - * Copyright © Magento, Inc. All rights reserved.
    - * See COPYING.txt for license details.
    - */
    -declare(strict_types=1);
    -
    -namespace Magento\Quote\Test\Unit\Plugin\Webapi\Controller\Rest;
    -
    -use Exception;
    -use Magento\Framework\App\ObjectManager;
    -use Magento\Quote\Plugin\Webapi\Controller\Rest\ValidateQuoteData;
    -use PHPUnit\Framework\TestCase;
    -use ReflectionClass;
    -
    -/**
    - * Unit test for ValidateQuoteData plugin
    - */
    -class ValidateQuoteDataTest extends TestCase
    -{
    -
    -    /**
    -     * @var ValidateQuoteData
    -     */
    -    private $validateQuoteDataObject;
    -
    -    /**
    -     * @var ReflectionClass
    -     *
    -     */
    -    private $reflectionObject;
    -
    -    /**
    -     * @inheritdoc
    -     */
    -    protected function setUp(): void
    -    {
    -        $this->validateQuoteDataObject = ObjectManager::getInstance()->get(ValidateQuoteData::class);
    -        $this->reflectionObject = new ReflectionClass(get_class($this->validateQuoteDataObject));
    -    }
    -    /**
    -     * Test if the quote array is valid
    -     *
    -     * @param array $array
    -     * @param array $result
    -     * @dataProvider dataProviderInputData
    -     * @throws Exception
    -     */
    -    public function testValidateInputData(array $array, array $result)
    -    {
    -        $this->assertEquals(
    -            $result,
    -            $this->invokeValidateInputData('validateInputData', [$array])
    -        );
    -    }
    -
    -    /**
    -     * @param string $methodName
    -     * @param array $arguments
    -     * @return mixed
    -     * @throws Exception
    -     */
    -    private function invokeValidateInputData(string $methodName, array $arguments = [])
    -    {
    -        $validateInputDataMethod = $this->reflectionObject->getMethod($methodName);
    -        $validateInputDataMethod->setAccessible(true);
    -        return $validateInputDataMethod->invokeArgs($this->validateQuoteDataObject, $arguments);
    -    }
    -
    -    /**
    -     * @return array
    -     */
    -    public function dataProviderInputData(): array
    -    {
    -        return [
    -            [
    -                ['person' =>
    -                    [
    -                        'id' => -1,
    -                        'Id' => 1,
    -                        'name' =>
    -                            [
    -                                'firstName' => 'John',
    -                                'LastName' => 'S'
    -                            ],
    -                        'isHavingVehicle' => 1,
    -                        'address' =>
    -                            [
    -                                'street' => '4th Street',
    -                                'Street' => '2nd Street',
    -                                'city' => 'Atlanta'
    -                            ],
    -                    ]
    -                ],
    -                ['person' =>
    -                    [
    -                        'id' => -1,
    -                        'name' =>
    -                            [
    -                                'firstName' => 'John',
    -                                'LastName' => 'S'
    -                            ],
    -                        'isHavingVehicle' => 1,
    -                        'address' =>
    -                            [
    -                                'street' => '4th Street',
    -                                'city' => 'Atlanta'
    -                            ],
    -                    ]
    -                ],
    -            ]
    -        ];
    -    }
    -}
    
  • app/code/Magento/RelatedProductGraphQl/composer.json+10 8 modified
    @@ -2,19 +2,20 @@
         "name": "magento/module-related-product-graph-ql",
         "description": "N/A",
         "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
    +    "version": "100.4.4",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/module-catalog": "*",
    -        "magento/module-catalog-graph-ql": "*",
    -        "magento/framework": "*"
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-catalog-graph-ql": "100.4.*",
    +        "magento/framework": "103.0.*"
         },
         "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 @@
             }
         }
     }
    +
    
  • app/code/Magento/ReleaseNotification/composer.json+12 10 modified
    @@ -1,21 +1,22 @@
     {
         "name": "magento/module-release-notification",
         "description": "N/A",
    -    "require": {
    -        "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/module-user": "*",
    -        "magento/module-backend": "*",
    -        "magento/module-ui": "*",
    -        "magento/framework": "*"
    -    },
    -    "suggest": {
    -        "magento/module-config": "*"
    -    },
         "type": "magento2-module",
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "100.4.5",
    +    "require": {
    +        "php": "~8.1.0||~8.2.0||~8.3.0",
    +        "magento/module-user": "101.2.*",
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-ui": "101.2.*",
    +        "magento/framework": "103.0.*"
    +    },
    +    "suggest": {
    +        "magento/module-config": "101.2.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -25,3 +26,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/RemoteStorage/composer.json+19 17 modified
    @@ -1,31 +1,32 @@
     {
         "name": "magento/module-remote-storage",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
    +    "version": "100.4.5",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    +        "magento/framework": "103.0.*",
             "league/flysystem": "^2.4",
             "league/flysystem-aws-s3-v3": "^2.4"
         },
         "suggest": {
    -        "magento/module-backend": "*",
    -        "magento/module-sitemap": "*",
    -        "magento/module-cms": "*",
    -        "magento/module-downloadable": "*",
    -        "magento/module-catalog": "*",
    -        "magento/module-media-storage": "*",
    -        "magento/module-media-gallery-metadata": "*",
    -        "magento/module-media-gallery-synchronization": "*",
    -        "magento/module-import-export": "*",
    -        "magento/module-catalog-import-export": "*",
    -        "magento/module-downloadable-import-export": "*",
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-sitemap": "100.4.*",
    +        "magento/module-cms": "104.0.*",
    +        "magento/module-downloadable": "100.4.*",
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-media-storage": "100.4.*",
    +        "magento/module-media-gallery-metadata": "100.4.*",
    +        "magento/module-media-gallery-synchronization": "100.4.*",
    +        "magento/module-import-export": "101.0.*",
    +        "magento/module-catalog-import-export": "101.1.*",
    +        "magento/module-downloadable-import-export": "100.4.*",
             "predis/predis": "*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -35,3 +36,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Reports/composer.json+25 23 modified
    @@ -1,35 +1,36 @@
     {
         "name": "magento/module-reports",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.7",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-backend": "*",
    -        "magento/module-catalog": "*",
    -        "magento/module-catalog-inventory": "*",
    -        "magento/module-cms": "*",
    -        "magento/module-config": "*",
    -        "magento/module-customer": "*",
    -        "magento/module-downloadable": "*",
    -        "magento/module-eav": "*",
    -        "magento/module-quote": "*",
    -        "magento/module-review": "*",
    -        "magento/module-sales": "*",
    -        "magento/module-sales-rule": "*",
    -        "magento/module-store": "*",
    -        "magento/module-tax": "*",
    -        "magento/module-widget": "*",
    -        "magento/module-wishlist": "*",
    -        "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-cms": "104.0.*",
    +        "magento/module-config": "101.2.*",
    +        "magento/module-customer": "103.0.*",
    +        "magento/module-downloadable": "100.4.*",
    +        "magento/module-eav": "102.1.*",
    +        "magento/module-quote": "101.2.*",
    +        "magento/module-review": "100.4.*",
    +        "magento/module-sales": "103.0.*",
    +        "magento/module-sales-rule": "101.2.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/module-tax": "100.4.*",
    +        "magento/module-widget": "101.2.*",
    +        "magento/module-wishlist": "101.2.*",
    +        "magento/module-directory": "100.4.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -39,3 +40,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/RequireJs/composer.json+8 6 modified
    @@ -1,18 +1,19 @@
     {
         "name": "magento/module-require-js",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.3",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*"
    +        "magento/framework": "103.0.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -22,3 +23,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/ReviewAnalytics/composer.json+8 6 modified
    @@ -1,17 +1,18 @@
     {
         "name": "magento/module-review-analytics",
         "description": "N/A",
    -    "require": {
    -        "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-review": "*",
    -        "magento/module-analytics": "*"
    -    },
         "type": "magento2-module",
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "100.4.4",
    +    "require": {
    +        "php": "~8.1.0||~8.2.0||~8.3.0",
    +        "magento/framework": "103.0.*",
    +        "magento/module-review": "100.4.*",
    +        "magento/module-analytics": "100.4.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -21,3 +22,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Review/composer.json+18 16 modified
    @@ -1,30 +1,31 @@
     {
         "name": "magento/module-review",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.7",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-backend": "*",
    -        "magento/module-catalog": "*",
    -        "magento/module-customer": "*",
    -        "magento/module-eav": "*",
    -        "magento/module-newsletter": "*",
    -        "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-customer": "103.0.*",
    +        "magento/module-eav": "102.1.*",
    +        "magento/module-newsletter": "100.4.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/module-theme": "101.1.*",
    +        "magento/module-ui": "101.2.*"
         },
         "suggest": {
    -        "magento/module-cookie": "*",
    -        "magento/module-review-sample-data": "*"
    +        "magento/module-cookie": "100.4.*",
    +        "magento/module-review-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 @@
             }
         }
     }
    +
    
  • app/code/Magento/ReviewGraphQl/composer.json+12 10 modified
    @@ -2,21 +2,22 @@
         "name": "magento/module-review-graph-ql",
         "description": "N/A",
         "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
    +    "version": "100.4.3",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/module-catalog": "*",
    -        "magento/module-review": "*",
    -        "magento/module-store": "*",
    -        "magento/framework": "*"
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-review": "100.4.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/framework": "103.0.*"
         },
         "suggest": {
    -        "magento/module-graph-ql": "*",
    -        "magento/module-graph-ql-cache": "*"
    +        "magento/module-graph-ql": "100.4.*",
    +        "magento/module-graph-ql-cache": "100.4.*"
         },
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -26,3 +27,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Robots/composer.json+10 8 modified
    @@ -1,22 +1,23 @@
     {
         "name": "magento/module-robots",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "101.1.3",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-store": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-store": "101.1.*"
         },
         "suggest": {
    -        "magento/module-theme": "*"
    +        "magento/module-theme": "101.1.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -26,3 +27,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Rss/composer.json+11 9 modified
    @@ -1,21 +1,22 @@
     {
         "name": "magento/module-rss",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.5",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-backend": "*",
    -        "magento/module-customer": "*",
    -        "magento/module-store": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-customer": "103.0.*",
    +        "magento/module-store": "101.1.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -25,3 +26,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Rule/composer.json+12 10 modified
    @@ -1,23 +1,24 @@
     {
         "name": "magento/module-rule",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.6",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
             "lib-libxml": "*",
    -        "magento/framework": "*",
    -        "magento/module-backend": "*",
    -        "magento/module-catalog": "*",
    -        "magento/module-eav": "*",
    -        "magento/module-store": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-eav": "102.1.*",
    +        "magento/module-store": "101.1.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -27,3 +28,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/SalesAnalytics/composer.json+8 6 modified
    @@ -1,17 +1,18 @@
     {
         "name": "magento/module-sales-analytics",
         "description": "N/A",
    -    "require": {
    -        "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-sales": "*",
    -        "magento/module-analytics": "*"
    -    },
         "type": "magento2-module",
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "100.4.4",
    +    "require": {
    +        "php": "~8.1.0||~8.2.0||~8.3.0",
    +        "magento/framework": "103.0.*",
    +        "magento/module-sales": "103.0.*",
    +        "magento/module-analytics": "100.4.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -21,3 +22,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Sales/composer.json+33 31 modified
    @@ -1,45 +1,46 @@
     {
         "name": "magento/module-sales",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "103.0.7-p1",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-authorization": "*",
    -        "magento/module-backend": "*",
    -        "magento/module-catalog": "*",
    -        "magento/module-bundle": "*",
    -        "magento/module-catalog-inventory": "*",
    -        "magento/module-checkout": "*",
    -        "magento/module-config": "*",
    -        "magento/module-customer": "*",
    -        "magento/module-directory": "*",
    -        "magento/module-eav": "*",
    -        "magento/module-gift-message": "*",
    -        "magento/module-media-storage": "*",
    -        "magento/module-payment": "*",
    -        "magento/module-quote": "*",
    -        "magento/module-reports": "*",
    -        "magento/module-sales-rule": "*",
    -        "magento/module-sales-sequence": "*",
    -        "magento/module-shipping": "*",
    -        "magento/module-store": "*",
    -        "magento/module-tax": "*",
    -        "magento/module-theme": "*",
    -        "magento/module-ui": "*",
    -        "magento/module-widget": "*",
    -        "magento/module-wishlist": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-authorization": "100.4.*",
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-bundle": "101.0.*",
    +        "magento/module-catalog-inventory": "100.4.*",
    +        "magento/module-checkout": "100.4.*",
    +        "magento/module-config": "101.2.*",
    +        "magento/module-customer": "103.0.*",
    +        "magento/module-directory": "100.4.*",
    +        "magento/module-eav": "102.1.*",
    +        "magento/module-gift-message": "100.4.*",
    +        "magento/module-media-storage": "100.4.*",
    +        "magento/module-payment": "100.4.*",
    +        "magento/module-quote": "101.2.*",
    +        "magento/module-reports": "100.4.*",
    +        "magento/module-sales-rule": "101.2.*",
    +        "magento/module-sales-sequence": "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-widget": "101.2.*",
    +        "magento/module-wishlist": "101.2.*"
         },
         "suggest": {
    -        "magento/module-sales-sample-data": "*"
    +        "magento/module-sales-sample-data": "Sample Data version: 100.4.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -49,3 +50,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/SalesGraphQl/composer.json+14 12 modified
    @@ -2,22 +2,23 @@
         "name": "magento/module-sales-graph-ql",
         "description": "N/A",
         "type": "magento2-module",
    -    "require": {
    -        "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-sales": "*",
    -        "magento/module-store": "*",
    -        "magento/module-catalog": "*",
    -        "magento/module-tax": "*",
    -        "magento/module-quote": "*",
    -        "magento/module-graph-ql": "*",
    -        "magento/module-shipping": "*",
    -        "magento/module-catalog-graph-ql": "*"
    -    },
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "100.4.7",
    +    "require": {
    +        "php": "~8.1.0||~8.2.0||~8.3.0",
    +        "magento/framework": "103.0.*",
    +        "magento/module-sales": "103.0.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-tax": "100.4.*",
    +        "magento/module-quote": "101.2.*",
    +        "magento/module-graph-ql": "100.4.*",
    +        "magento/module-shipping": "100.4.*",
    +        "magento/module-catalog-graph-ql": "100.4.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -27,3 +28,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Sales/Helper/Admin.php+0 78 modified
    @@ -160,84 +160,6 @@ public function applySalableProductTypesFilter($collection)
          */
         public function escapeHtmlWithLinks($data, $allowedTags = null)
         {
    -        if (!empty($data) && is_array($allowedTags) && in_array('a', $allowedTags)) {
    -            $wrapperElementId = uniqid();
    -            $domDocument = $this->domDocumentFactory->create();
    -
    -            $internalErrors = libxml_use_internal_errors(true);
    -
    -            $convmap = [0x80, 0x10FFFF, 0, 0x1FFFFF];
    -            $data = mb_encode_numericentity(
    -                $data,
    -                $convmap,
    -                'UTF-8'
    -            );
    -
    -            $domDocument->loadHTML(
    -                '<html><body id="' . $wrapperElementId . '">' . $data . '</body></html>'
    -            );
    -
    -            libxml_use_internal_errors($internalErrors);
    -
    -            $linkTags = $domDocument->getElementsByTagName('a');
    -
    -            foreach ($linkTags as $linkNode) {
    -                $linkAttributes = [];
    -                foreach ($linkNode->attributes as $attribute) {
    -                    $linkAttributes[$attribute->name] = $attribute->value;
    -                }
    -
    -                foreach ($linkAttributes as $attributeName => $attributeValue) {
    -                    if ($attributeName === 'href') {
    -                        $url = $this->filterUrl($attributeValue ?? '');
    -                        $url = $this->escaper->escapeUrl($url);
    -                        $linkNode->setAttribute('href', $url);
    -                    } else {
    -                        $linkNode->removeAttribute($attributeName);
    -                    }
    -                }
    -            }
    -
    -            $result = mb_decode_numericentity(
    -                // phpcs:ignore Magento2.Functions.DiscouragedFunction
    -                html_entity_decode(
    -                    $domDocument->saveHTML(),
    -                    ENT_QUOTES|ENT_SUBSTITUTE,
    -                    'UTF-8'
    -                ),
    -                $convmap,
    -                'UTF-8'
    -            );
    -
    -            preg_match('/<body id="' . $wrapperElementId . '">(.+)<\/body><\/html>$/si', $result, $matches);
    -            $data = !empty($matches) ? $matches[1] : '';
    -        }
    -
             return $this->escaper->escapeHtml($data, $allowedTags);
         }
    -
    -    /**
    -     * Filter the URL for allowed protocols.
    -     *
    -     * @param string $url
    -     * @return string
    -     */
    -    private function filterUrl(string $url): string
    -    {
    -        if ($url) {
    -            //Revert the sprintf escaping
    -            // phpcs:ignore Magento2.Functions.DiscouragedFunction
    -            $urlScheme = parse_url($url, PHP_URL_SCHEME);
    -            $urlScheme = $urlScheme ? strtolower($urlScheme) : '';
    -            if ($urlScheme !== 'http' && $urlScheme !== 'https') {
    -                $url = null;
    -            }
    -        }
    -
    -        if (!$url) {
    -            $url = '#';
    -        }
    -
    -        return $url;
    -    }
     }
    
  • app/code/Magento/SalesInventory/composer.json+12 10 modified
    @@ -1,22 +1,23 @@
     {
         "name": "magento/module-sales-inventory",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.4",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-catalog": "*",
    -        "magento/module-catalog-inventory": "*",
    -        "magento/module-sales": "*",
    -        "magento/module-store": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-catalog-inventory": "100.4.*",
    +        "magento/module-sales": "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 @@
             }
         }
     }
    +
    
  • app/code/Magento/SalesRule/composer.json+30 28 modified
    @@ -1,42 +1,43 @@
     {
         "name": "magento/module-sales-rule",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "101.2.7",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/framework-bulk": "*",
    -        "magento/module-backend": "*",
    -        "magento/module-catalog": "*",
    -        "magento/module-catalog-rule": "*",
    -        "magento/module-config": "*",
    -        "magento/module-customer": "*",
    -        "magento/module-directory": "*",
    -        "magento/module-eav": "*",
    -        "magento/module-payment": "*",
    -        "magento/module-quote": "*",
    -        "magento/module-reports": "*",
    -        "magento/module-rule": "*",
    -        "magento/module-sales": "*",
    -        "magento/module-shipping": "*",
    -        "magento/module-store": "*",
    -        "magento/module-ui": "*",
    -        "magento/module-widget": "*",
    -        "magento/module-captcha": "*",
    -        "magento/module-checkout": "*",
    -        "magento/module-authorization": "*",
    -        "magento/module-asynchronous-operations": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/framework-bulk": "101.0.*",
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-catalog-rule": "101.2.*",
    +        "magento/module-config": "101.2.*",
    +        "magento/module-customer": "103.0.*",
    +        "magento/module-directory": "100.4.*",
    +        "magento/module-eav": "102.1.*",
    +        "magento/module-payment": "100.4.*",
    +        "magento/module-quote": "101.2.*",
    +        "magento/module-reports": "100.4.*",
    +        "magento/module-rule": "100.4.*",
    +        "magento/module-sales": "103.0.*",
    +        "magento/module-shipping": "100.4.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/module-ui": "101.2.*",
    +        "magento/module-widget": "101.2.*",
    +        "magento/module-captcha": "100.4.*",
    +        "magento/module-checkout": "100.4.*",
    +        "magento/module-authorization": "100.4.*",
    +        "magento/module-asynchronous-operations": "100.4.*"
         },
         "suggest": {
    -        "magento/module-sales-rule-sample-data": "*"
    +        "magento/module-sales-rule-sample-data": "Sample Data version: 100.4.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -46,3 +47,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/SalesRuleGraphQl/composer.json+9 7 modified
    @@ -1,19 +1,20 @@
     {
         "name": "magento/module-sales-rule-graph-ql",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.0",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-sales-rule": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-sales-rule": "101.2.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -23,3 +24,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/SalesSequence/composer.json+8 6 modified
    @@ -1,18 +1,19 @@
     {
         "name": "magento/module-sales-sequence",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.4",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*"
    +        "magento/framework": "103.0.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -22,3 +23,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Sales/view/adminhtml/templates/order/comments/view.phtml+12 8 modified
    @@ -5,13 +5,14 @@
      */
     
     /** @var \Magento\Framework\View\Helper\SecureHtmlRenderer $secureRenderer */
    +/** @var \Magento\Framework\Escaper $escaper */
     ?>
     <?php if ($_entity = $block->getEntity()): ?>
     <div id="comments_block" class="edit-order-comments">
         <div class="order-history-block">
             <div class="admin__field field-row">
                 <label class="admin__field-label"
    -                   for="history_comment"><?= $block->escapeHtml(__('Comment Text')) ?></label>
    +                   for="history_comment"><?= $escaper->escapeHtml(__('Comment Text')) ?></label>
                 <div class="admin__field-control">
                     <textarea name="comment[comment]"
                               class="admin__control-textarea"
    @@ -30,7 +31,7 @@
                                    id="history_notify"
                                    value="1" />
                             <label class="admin__field-label"
    -                               for="history_notify"><?= $block->escapeHtml(__('Notify Customer by Email')) ?></label>
    +                               for="history_notify"><?= $escaper->escapeHtml(__('Notify Customer by Email')) ?></label>
                         </div>
                     <?php endif; ?>
                     <div class="admin__field admin__field-option">
    @@ -40,7 +41,7 @@
                                class="admin__control-checkbox"
                                value="1" />
                         <label class="admin__field-label"
    -                           for="history_visible"> <?= $block->escapeHtml(__('Visible on Storefront')) ?></label>
    +                           for="history_visible"> <?= $escaper->escapeHtml(__('Visible on Storefront')) ?></label>
                     </div>
                 </div>
                 <div class="order-history-comments-actions">
    @@ -59,17 +60,20 @@
                         <?= /* @noEscape */ $block->formatTime($_comment->getCreatedAt(), \IntlDateFormatter::MEDIUM) ?>
                     </span>
                     <span class="note-list-customer">
    -                    <?= $block->escapeHtml(__('Customer')) ?>
    +                    <?= $escaper->escapeHtml(__('Customer')) ?>
                         <?php if ($_comment->getIsCustomerNotified()): ?>
    -                        <span class="note-list-customer-notified"><?= $block->escapeHtml(__('Notified')) ?></span>
    +                        <span class="note-list-customer-notified"><?= $escaper->escapeHtml(__('Notified')) ?></span>
                         <?php else: ?>
                             <span class="note-list-customer-not-notified">
    -                            <?= $block->escapeHtml(__('Not Notified')) ?>
    +                            <?= $escaper->escapeHtml(__('Not Notified')) ?>
                             </span>
                         <?php endif; ?>
                     </span>
                     <div class="note-list-comment">
    -                    <?= $block->escapeHtml($_comment->getComment(), ['b', 'br', 'strong', 'i', 'u', 'a']) ?>
    +                    <?= /* @noEscape */ nl2br($escaper->escapeHtml(
    +                        $_comment->getComment(),
    +                        ['b', 'br', 'strong', 'i', 'u', 'a']
    +                    ))?>
                     </div>
                 </li>
             <?php endforeach; ?>
    @@ -78,7 +82,7 @@
         <?php $scriptString = <<<script
     require(['prototype'], function(){
         submitComment = function() {
    -        submitAndReloadArea($('comments_block').parentNode, '{$block->escapeJs($block->getSubmitUrl())}')
    +        submitAndReloadArea($('comments_block').parentNode, '{$escaper->escapeJs($block->getSubmitUrl())}')
         };
         if ($('submit_comment_button')) {
             $('submit_comment_button').observe('click', submitComment);
    
  • app/code/Magento/SampleData/composer.json+9 7 modified
    @@ -1,21 +1,22 @@
     {
         "name": "magento/module-sample-data",
         "description": "Sample Data fixtures",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.5",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*"
    +        "magento/framework": "103.0.*"
         },
         "suggest": {
    -        "magento/sample-data-media": "*"
    +        "magento/sample-data-media": "Sample Data version: 100.4.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "cli_commands.php",
    @@ -26,3 +27,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Search/composer.json+13 11 modified
    @@ -1,23 +1,24 @@
     {
         "name": "magento/module-search",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "101.1.7",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-backend": "*",
    -        "magento/module-catalog-search": "*",
    -        "magento/module-reports": "*",
    -        "magento/module-store": "*",
    -        "magento/module-ui": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-catalog-search": "102.0.*",
    +        "magento/module-reports": "100.4.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/module-ui": "101.2.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -27,3 +28,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Security/composer.json+13 11 modified
    @@ -1,25 +1,26 @@
     {
         "name": "magento/module-security",
         "description": "Security management module",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.7",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-config": "*",
    -        "magento/module-backend": "*",
    -        "magento/module-store": "*",
    -        "magento/module-user": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-config": "101.2.*",
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/module-user": "101.2.*"
         },
         "suggest": {
    -        "magento/module-customer": "*"
    +        "magento/module-customer": "103.0.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -29,3 +30,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/SendFriend/composer.json+14 12 modified
    @@ -1,24 +1,25 @@
     {
         "name": "magento/module-send-friend",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.5",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-catalog": "*",
    -        "magento/module-customer": "*",
    -        "magento/module-store": "*",
    -        "magento/module-captcha": "*",
    -        "magento/module-authorization": "*",
    -        "magento/module-theme": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-customer": "103.0.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/module-captcha": "100.4.*",
    +        "magento/module-authorization": "100.4.*",
    +        "magento/module-theme": "101.1.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -28,3 +29,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/SendFriendGraphQl/composer.json+9 7 modified
    @@ -2,17 +2,18 @@
         "name": "magento/module-send-friend-graph-ql",
         "description": "N/A",
         "type": "magento2-module",
    -    "require": {
    -        "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-catalog": "*",
    -        "magento/module-send-friend": "*",
    -        "magento/module-graph-ql": "*"
    -    },
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "100.4.3",
    +    "require": {
    +        "php": "~8.1.0||~8.2.0||~8.3.0",
    +        "magento/framework": "103.0.*",
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-send-friend": "100.4.*",
    +        "magento/module-graph-ql": "100.4.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -22,3 +23,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Shipping/composer.json+24 22 modified
    @@ -1,37 +1,38 @@
     {
         "name": "magento/module-shipping",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.7",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
             "ext-gd": "*",
    -        "magento/framework": "*",
    -        "magento/module-backend": "*",
    -        "magento/module-catalog": "*",
    -        "magento/module-catalog-inventory": "*",
    -        "magento/module-contact": "*",
    -        "magento/module-customer": "*",
    -        "magento/module-directory": "*",
    -        "magento/module-payment": "*",
    -        "magento/module-quote": "*",
    -        "magento/module-sales": "*",
    -        "magento/module-store": "*",
    -        "magento/module-tax": "*",
    -        "magento/module-ui": "*",
    -        "magento/module-user": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-catalog-inventory": "100.4.*",
    +        "magento/module-contact": "100.4.*",
    +        "magento/module-customer": "103.0.*",
    +        "magento/module-directory": "100.4.*",
    +        "magento/module-payment": "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-user": "101.2.*"
         },
         "suggest": {
    -        "magento/module-fedex": "*",
    -        "magento/module-ups": "*",
    -        "magento/module-config": "*"
    +        "magento/module-fedex": "100.4.*",
    +        "magento/module-ups": "100.4.*",
    +        "magento/module-config": "101.2.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -41,3 +42,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Sitemap/composer.json+18 16 modified
    @@ -1,30 +1,31 @@
     {
         "name": "magento/module-sitemap",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.6",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-backend": "*",
    -        "magento/module-catalog": "*",
    -        "magento/module-catalog-url-rewrite": "*",
    -        "magento/module-cms": "*",
    -        "magento/module-config": "*",
    -        "magento/module-eav": "*",
    -        "magento/module-media-storage": "*",
    -        "magento/module-robots": "*",
    -        "magento/module-store": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-catalog-url-rewrite": "100.4.*",
    +        "magento/module-cms": "104.0.*",
    +        "magento/module-config": "101.2.*",
    +        "magento/module-eav": "102.1.*",
    +        "magento/module-media-storage": "100.4.*",
    +        "magento/module-robots": "101.1.*",
    +        "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"
    @@ -34,3 +35,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Store/composer.json+17 15 modified
    @@ -1,29 +1,30 @@
     {
         "name": "magento/module-store",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "101.1.7",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-catalog": "*",
    -        "magento/module-config": "*",
    -        "magento/module-directory": "*",
    -        "magento/module-media-storage": "*",
    -        "magento/module-ui": "*",
    -        "magento/module-customer": "*",
    -        "magento/module-authorization": "*",
    -        "magento/module-backend": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-config": "101.2.*",
    +        "magento/module-directory": "100.4.*",
    +        "magento/module-media-storage": "100.4.*",
    +        "magento/module-ui": "101.2.*",
    +        "magento/module-customer": "103.0.*",
    +        "magento/module-authorization": "100.4.*",
    +        "magento/module-backend": "102.0.*"
         },
         "suggest": {
    -        "magento/module-deploy": "*"
    +        "magento/module-deploy": "100.4.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -33,3 +34,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/StoreGraphQl/composer.json+10 8 modified
    @@ -2,18 +2,19 @@
         "name": "magento/module-store-graph-ql",
         "description": "N/A",
         "type": "magento2-module",
    -    "require": {
    -        "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-store": "*",
    -        "magento/module-graph-ql": "*",
    -        "magento/module-graph-ql-cache": "*",
    -        "magento/module-graph-ql-resolver-cache": "*"
    -    },
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "100.4.5",
    +    "require": {
    +        "php": "~8.1.0||~8.2.0||~8.3.0",
    +        "magento/framework": "103.0.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/module-graph-ql": "100.4.*",
    +        "magento/module-graph-ql-cache": "100.4.*",
    +        "magento/module-graph-ql-resolver-cache": "100.4.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -23,3 +24,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Swagger/composer.json+8 6 modified
    @@ -1,18 +1,19 @@
     {
         "name": "magento/module-swagger",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.6",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*"
    +        "magento/framework": "103.0.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -22,3 +23,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/SwaggerWebapiAsync/composer.json+10 8 modified
    @@ -1,22 +1,23 @@
     {
         "name": "magento/module-swagger-webapi-async",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.3",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-swagger": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-swagger": "100.4.*"
         },
         "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 @@
             }
         }
     }
    +
    
  • app/code/Magento/SwaggerWebapi/composer.json+9 7 modified
    @@ -1,19 +1,20 @@
     {
         "name": "magento/module-swagger-webapi",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.3",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-swagger": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-swagger": "100.4.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -23,3 +24,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Swatches/composer.json+20 18 modified
    @@ -1,32 +1,33 @@
     {
         "name": "magento/module-swatches",
         "description": "Add Swatches to Products",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.7",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-backend": "*",
    -        "magento/module-catalog": "*",
    -        "magento/module-config": "*",
    -        "magento/module-configurable-product": "*",
    -        "magento/module-customer": "*",
    -        "magento/module-eav": "*",
    -        "magento/module-page-cache": "*",
    -        "magento/module-media-storage": "*",
    -        "magento/module-store": "*",
    -        "magento/module-theme": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-config": "101.2.*",
    +        "magento/module-configurable-product": "100.4.*",
    +        "magento/module-customer": "103.0.*",
    +        "magento/module-eav": "102.1.*",
    +        "magento/module-page-cache": "100.4.*",
    +        "magento/module-media-storage": "100.4.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/module-theme": "101.1.*"
         },
         "suggest": {
    -        "magento/module-layered-navigation": "*",
    -        "magento/module-swatches-sample-data": "*"
    +        "magento/module-layered-navigation": "100.4.*",
    +        "magento/module-swatches-sample-data": "Sample Data version: 100.4.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -36,3 +37,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/SwatchesGraphQl/composer.json+11 9 modified
    @@ -2,20 +2,21 @@
         "name": "magento/module-swatches-graph-ql",
         "description": "N/A",
         "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
    +    "version": "100.4.5",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-swatches": "*",
    -        "magento/module-catalog": "*",
    -        "magento/module-catalog-graph-ql": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-swatches": "100.4.*",
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-catalog-graph-ql": "100.4.*"
         },
         "suggest": {
    -        "magento/module-configurable-product-graph-ql": "*"
    +        "magento/module-configurable-product-graph-ql": "100.4.*"
         },
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -25,3 +26,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/SwatchesLayeredNavigation/composer.json+8 6 modified
    @@ -1,19 +1,20 @@
     {
         "name": "magento/module-swatches-layered-navigation",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.3",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    +        "magento/framework": "103.0.*",
             "magento/magento-composer-installer": "*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -23,3 +24,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Tax/composer.json+23 21 modified
    @@ -1,35 +1,36 @@
     {
         "name": "magento/module-tax",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.7",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-backend": "*",
    -        "magento/module-catalog": "*",
    -        "magento/module-checkout": "*",
    -        "magento/module-config": "*",
    -        "magento/module-customer": "*",
    -        "magento/module-directory": "*",
    -        "magento/module-eav": "*",
    -        "magento/module-page-cache": "*",
    -        "magento/module-quote": "*",
    -        "magento/module-reports": "*",
    -        "magento/module-sales": "*",
    -        "magento/module-shipping": "*",
    -        "magento/module-store": "*",
    -        "magento/module-ui": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-checkout": "100.4.*",
    +        "magento/module-config": "101.2.*",
    +        "magento/module-customer": "103.0.*",
    +        "magento/module-directory": "100.4.*",
    +        "magento/module-eav": "102.1.*",
    +        "magento/module-page-cache": "100.4.*",
    +        "magento/module-quote": "101.2.*",
    +        "magento/module-reports": "100.4.*",
    +        "magento/module-sales": "103.0.*",
    +        "magento/module-shipping": "100.4.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/module-ui": "101.2.*"
         },
         "suggest": {
    -        "magento/module-tax-sample-data": "*"
    +        "magento/module-tax-sample-data": "Sample Data version: 100.4.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -39,3 +40,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/TaxGraphQl/composer.json+9 7 modified
    @@ -2,18 +2,19 @@
         "name": "magento/module-tax-graph-ql",
         "description": "N/A",
         "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
    +    "version": "100.4.3",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*"
    +        "magento/framework": "103.0.*"
         },
         "suggest": {
    -        "magento/module-tax": "*",
    -        "magento/module-catalog-graph-ql": "*"
    +        "magento/module-tax": "100.4.*",
    +        "magento/module-catalog-graph-ql": "100.4.*"
         },
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -23,3 +24,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/TaxImportExport/composer.json+13 11 modified
    @@ -1,23 +1,24 @@
     {
         "name": "magento/module-tax-import-export",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.6",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-backend": "*",
    -        "magento/module-directory": "*",
    -        "magento/module-store": "*",
    -        "magento/module-tax": "*",
    -        "magento/module-ui": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-directory": "100.4.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/module-tax": "100.4.*",
    +        "magento/module-ui": "101.2.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -27,3 +28,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Theme/composer.json+21 19 modified
    @@ -1,33 +1,34 @@
     {
         "name": "magento/module-theme",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "101.1.7",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-backend": "*",
    -        "magento/module-cms": "*",
    -        "magento/module-config": "*",
    -        "magento/module-customer": "*",
    -        "magento/module-eav": "*",
    -        "magento/module-media-storage": "*",
    -        "magento/module-require-js": "*",
    -        "magento/module-store": "*",
    -        "magento/module-ui": "*",
    -        "magento/module-widget": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-cms": "104.0.*",
    +        "magento/module-config": "101.2.*",
    +        "magento/module-customer": "103.0.*",
    +        "magento/module-eav": "102.1.*",
    +        "magento/module-media-storage": "100.4.*",
    +        "magento/module-require-js": "100.4.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/module-ui": "101.2.*",
    +        "magento/module-widget": "101.2.*"
         },
         "suggest": {
    -        "magento/module-theme-sample-data": "*",
    -        "magento/module-deploy": "*",
    -        "magento/module-directory": "*"
    +        "magento/module-theme-sample-data": "Sample Data version: 100.4.*",
    +        "magento/module-deploy": "100.4.*",
    +        "magento/module-directory": "100.4.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -37,3 +38,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/ThemeGraphQl/composer.json+8 6 modified
    @@ -2,17 +2,18 @@
         "name": "magento/module-theme-graph-ql",
         "description": "N/A",
         "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
    +    "version": "100.4.4",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*"
    +        "magento/framework": "103.0.*"
         },
         "suggest": {
    -        "magento/module-store-graph-ql": "*"
    +        "magento/module-store-graph-ql": "100.4.*"
         },
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -22,3 +23,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Translation/composer.json+14 12 modified
    @@ -1,26 +1,27 @@
     {
         "name": "magento/module-translation",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.7",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-backend": "*",
    -        "magento/module-developer": "*",
    -        "magento/module-store": "*",
    -        "magento/module-theme": "*",
    -        "magento/module-deploy": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-developer": "100.4.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/module-theme": "101.1.*",
    +        "magento/module-deploy": "100.4.*"
         },
         "suggest": {
    -        "magento/module-deploy": "*"
    +        "magento/module-deploy": "100.4.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -30,3 +31,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Ui/composer.json+14 12 modified
    @@ -1,26 +1,27 @@
     {
         "name": "magento/module-ui",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "101.2.7",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-authorization": "*",
    -        "magento/module-backend": "*",
    -        "magento/module-eav": "*",
    -        "magento/module-store": "*",
    -        "magento/module-user": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-authorization": "100.4.*",
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-eav": "102.1.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/module-user": "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"
    @@ -30,3 +31,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Ups/composer.json+16 14 modified
    @@ -1,28 +1,29 @@
     {
         "name": "magento/module-ups",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.7-p1",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-backend": "*",
    -        "magento/module-catalog-inventory": "*",
    -        "magento/module-directory": "*",
    -        "magento/module-quote": "*",
    -        "magento/module-sales": "*",
    -        "magento/module-shipping": "*",
    -        "magento/module-store": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-catalog-inventory": "100.4.*",
    +        "magento/module-directory": "100.4.*",
    +        "magento/module-quote": "101.2.*",
    +        "magento/module-sales": "103.0.*",
    +        "magento/module-shipping": "100.4.*",
    +        "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"
    @@ -32,3 +33,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Ups/Model/Carrier.php+22 5 modified
    @@ -432,6 +432,19 @@ public function setRequest(RateRequest $request)
                 $unit = $this->getConfigData('unit_of_measure');
             }
             $rowRequest->setUnitMeasure($unit);
    +
    +        $rowRequest->setPackageHeight($request->getPackageHeight());
    +        $rowRequest->setPackageWidth($request->getPackageWidth());
    +        $rowRequest->setPackageDepth($request->getPackageDepth());
    +
    +        if ($rowRequest->getUnitMeasure() == 'KGS') {
    +            $rowRequest->setUnitDimensions('CM');
    +            $rowRequest->setUnitDimensionsDescription('Centimeter');
    +        } else {
    +            $rowRequest->setUnitDimensions('IN');
    +            $rowRequest->setUnitDimensionsDescription('Inches');
    +        }
    +
             $rowRequest->setIsReturn($request->getIsReturn());
             $rowRequest->setBaseSubtotalInclTax($request->getBaseSubtotalInclTax());
     
    @@ -1134,6 +1147,10 @@ protected function _getRestQuotes()
                 $rateParams['RateRequest']['Shipment']['Service']['Description'] = $serviceDescription;
             }
     
    +        $height = $rowRequest->getPackageHeight() ?? 0;
    +        $width = $rowRequest->getPackageWidth() ?? 0;
    +        $length = $rowRequest->getPackageDepth() ?? 0;
    +
             foreach ($rowRequest->getPackages() as $package) {
                 $rateParams['RateRequest']['Shipment']['Package'][] = [
                     "PackagingType" => [
    @@ -1142,12 +1159,12 @@ protected function _getRestQuotes()
                     ],
                     "Dimensions" => [
                         "UnitOfMeasurement" => [
    -                        "Code" => "IN",
    -                        "Description" => "Inches"
    +                        "Code" => "{$rowRequest->getUnitDimensions()}",
    +                        "Description" => "{$rowRequest->getUnitDimensionsDescription()}"
                         ],
    -                    "Length" => "5",
    -                    "Width" => "5",
    -                    "Height" => "5"
    +                    "Length" => "{$length}",
    +                    "Width" => "{$width}",
    +                    "Height" => "{$height}"
                     ],
                     "PackageWeight" => [
                         "UnitOfMeasurement" => [
    
  • app/code/Magento/UrlRewrite/composer.json+15 13 modified
    @@ -1,25 +1,26 @@
     {
         "name": "magento/module-url-rewrite",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "102.0.6",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-backend": "*",
    -        "magento/module-catalog": "*",
    -        "magento/module-catalog-url-rewrite": "*",
    -        "magento/module-cms": "*",
    -        "magento/module-cms-url-rewrite": "*",
    -        "magento/module-store": "*",
    -        "magento/module-ui": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-catalog-url-rewrite": "100.4.*",
    +        "magento/module-cms": "104.0.*",
    +        "magento/module-cms-url-rewrite": "100.4.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/module-ui": "101.2.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -29,3 +30,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/UrlRewriteGraphQl/composer.json+9 7 modified
    @@ -2,18 +2,19 @@
         "name": "magento/module-url-rewrite-graph-ql",
         "description": "N/A",
         "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
    +    "version": "100.4.6",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-url-rewrite": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-url-rewrite": "102.0.*"
         },
         "suggest": {
    -        "magento/module-graph-ql": "*"
    +        "magento/module-graph-ql": "100.4.*"
         },
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -23,3 +24,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/User/composer.json+15 13 modified
    @@ -1,25 +1,26 @@
     {
         "name": "magento/module-user",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "101.2.7",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-authorization": "*",
    -        "magento/module-backend": "*",
    -        "magento/module-email": "*",
    -        "magento/module-integration": "*",
    -        "magento/module-security": "*",
    -        "magento/module-store": "*",
    -        "magento/module-ui": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-authorization": "100.4.*",
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-email": "101.1.*",
    +        "magento/module-integration": "100.4.*",
    +        "magento/module-security": "100.4.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/module-ui": "101.2.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -29,3 +30,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Usps/composer.json+16 14 modified
    @@ -1,27 +1,28 @@
     {
         "name": "magento/module-usps",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.6",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
             "lib-libxml": "*",
    -        "magento/framework": "*",
    -        "magento/module-catalog": "*",
    -        "magento/module-catalog-inventory": "*",
    -        "magento/module-config": "*",
    -        "magento/module-directory": "*",
    -        "magento/module-quote": "*",
    -        "magento/module-sales": "*",
    -        "magento/module-shipping": "*",
    -        "magento/module-store": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-catalog-inventory": "100.4.*",
    +        "magento/module-config": "101.2.*",
    +        "magento/module-directory": "100.4.*",
    +        "magento/module-quote": "101.2.*",
    +        "magento/module-sales": "103.0.*",
    +        "magento/module-shipping": "100.4.*",
    +        "magento/module-store": "101.1.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -31,3 +32,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Variable/composer.json+12 10 modified
    @@ -1,22 +1,23 @@
     {
         "name": "magento/module-variable",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.5",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-backend": "*",
    -        "magento/module-store": "*",
    -        "magento/module-config": "*",
    -        "magento/module-ui": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/module-config": "101.2.*",
    +        "magento/module-ui": "101.2.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -26,3 +27,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Vault/composer.json+15 14 modified
    @@ -1,25 +1,25 @@
     {
         "name": "magento/module-vault",
    -    "description": "",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "101.2.7",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-checkout": "*",
    -        "magento/module-customer": "*",
    -        "magento/module-payment": "*",
    -        "magento/module-quote": "*",
    -        "magento/module-sales": "*",
    -        "magento/module-store": "*",
    -        "magento/module-theme": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-checkout": "100.4.*",
    +        "magento/module-customer": "103.0.*",
    +        "magento/module-payment": "100.4.*",
    +        "magento/module-quote": "101.2.*",
    +        "magento/module-sales": "103.0.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/module-theme": "101.1.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -29,3 +29,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/VaultGraphQl/composer.json+8 6 modified
    @@ -2,16 +2,17 @@
         "name": "magento/module-vault-graph-ql",
         "description": "N/A",
         "type": "magento2-module",
    -    "require": {
    -        "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-vault": "*",
    -        "magento/module-graph-ql": "*"
    -    },
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "100.4.3",
    +    "require": {
    +        "php": "~8.1.0||~8.2.0||~8.3.0",
    +        "magento/framework": "103.0.*",
    +        "magento/module-vault": "101.2.*",
    +        "magento/module-graph-ql": "100.4.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -21,3 +22,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Version/composer.json+8 6 modified
    @@ -1,18 +1,19 @@
     {
         "name": "magento/module-version",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.4",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*"
    +        "magento/framework": "103.0.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -22,3 +23,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/WebapiAsync/composer.json+13 11 modified
    @@ -1,25 +1,26 @@
     {
         "name": "magento/module-webapi-async",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.5",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-webapi": "*",
    -        "magento/module-asynchronous-operations": "*",
    -        "magento/module-store": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-webapi": "100.4.*",
    +        "magento/module-asynchronous-operations": "100.4.*",
    +        "magento/module-store": "101.1.*"
         },
         "suggest": {
    -        "magento/module-user": "*",
    -        "magento/module-customer": "*"
    +        "magento/module-user": "101.2.*",
    +        "magento/module-customer": "103.0.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -29,3 +30,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Webapi/composer.json+14 12 modified
    @@ -1,26 +1,27 @@
     {
         "name": "magento/module-webapi",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.6-p1",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-authorization": "*",
    -        "magento/module-backend": "*",
    -        "magento/module-integration": "*",
    -        "magento/module-store": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-authorization": "100.4.*",
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-integration": "100.4.*",
    +        "magento/module-store": "101.1.*"
         },
         "suggest": {
    -        "magento/module-user": "*",
    -        "magento/module-customer": "*"
    +        "magento/module-user": "101.2.*",
    +        "magento/module-customer": "103.0.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -30,3 +31,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Webapi/Controller/Rest/InputParamsResolver.php+22 0 modified
    @@ -138,14 +138,36 @@ public function getInputData()
                     $serviceMethodName
                 );
                 $inputData = array_merge($inputData, $this->request->getParams());
    +            $inputData = $this->filterInputData($inputData);
             } else {
                 $inputData = $this->request->getRequestData();
             }
    +
             $this->validateParameters($serviceClassName, $serviceMethodName, array_keys($route->getParameters()));
     
             return $this->paramsOverrider->override($inputData, $route->getParameters());
         }
     
    +    /**
    +     * Validates InputData
    +     *
    +     * @param array $inputData
    +     * @return array
    +     */
    +    private function filterInputData(array $inputData): array
    +    {
    +        $result = [];
    +
    +        $data = array_filter($inputData, function ($k) use (&$result) {
    +            $key = is_string($k) ? strtolower(str_replace('_', "", $k)) : $k;
    +            return !isset($result[$key]) && ($result[$key] = true);
    +        }, ARRAY_FILTER_USE_KEY);
    +
    +        return array_map(function ($value) {
    +            return is_array($value) ? $this->filterInputData($value) : $value;
    +        }, $data);
    +    }
    +
         /**
          * Retrieve current route.
          *
    
  • app/code/Magento/WebapiSecurity/composer.json+9 7 modified
    @@ -1,19 +1,20 @@
     {
         "name": "magento/module-webapi-security",
         "description": "WebapiSecurity module provides option to loosen security on some webapi resources.",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.4",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-webapi": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-webapi": "100.4.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -23,3 +24,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Weee/composer.json+21 19 modified
    @@ -1,33 +1,34 @@
     {
         "name": "magento/module-weee",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.7",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-backend": "*",
    -        "magento/module-catalog": "*",
    -        "magento/module-checkout": "*",
    -        "magento/module-customer": "*",
    -        "magento/module-directory": "*",
    -        "magento/module-eav": "*",
    -        "magento/module-page-cache": "*",
    -        "magento/module-quote": "*",
    -        "magento/module-sales": "*",
    -        "magento/module-store": "*",
    -        "magento/module-tax": "*",
    -        "magento/module-ui": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-checkout": "100.4.*",
    +        "magento/module-customer": "103.0.*",
    +        "magento/module-directory": "100.4.*",
    +        "magento/module-eav": "102.1.*",
    +        "magento/module-page-cache": "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.*"
         },
         "suggest": {
    -        "magento/module-bundle": "*"
    +        "magento/module-bundle": "101.0.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -37,3 +38,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/WeeeGraphQl/composer.json+11 9 modified
    @@ -2,20 +2,21 @@
         "name": "magento/module-weee-graph-ql",
         "description": "N/A",
         "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
    +    "version": "100.4.4",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-store": "*",
    -        "magento/module-tax": "*",
    -        "magento/module-weee": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/module-tax": "100.4.*",
    +        "magento/module-weee": "100.4.*"
         },
         "suggest": {
    -        "magento/module-catalog-graph-ql": "*"
    +        "magento/module-catalog-graph-ql": "100.4.*"
         },
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -25,3 +26,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Widget/composer.json+17 15 modified
    @@ -1,29 +1,30 @@
     {
         "name": "magento/module-widget",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "101.2.7",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-backend": "*",
    -        "magento/module-catalog": "*",
    -        "magento/module-cms": "*",
    -        "magento/module-email": "*",
    -        "magento/module-store": "*",
    -        "magento/module-theme": "*",
    -        "magento/module-variable": "*",
    -        "magento/module-ui": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-cms": "104.0.*",
    +        "magento/module-email": "101.1.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/module-theme": "101.1.*",
    +        "magento/module-variable": "100.4.*",
    +        "magento/module-ui": "101.2.*"
         },
         "suggest": {
    -        "magento/module-widget-sample-data": "*"
    +        "magento/module-widget-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 @@
             }
         }
     }
    +
    
  • app/code/Magento/WishlistAnalytics/composer.json+8 6 modified
    @@ -1,17 +1,18 @@
     {
         "name": "magento/module-wishlist-analytics",
         "description": "N/A",
    -    "require": {
    -        "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-wishlist": "*",
    -        "magento/module-analytics": "*"
    -    },
         "type": "magento2-module",
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "100.4.5",
    +    "require": {
    +        "php": "~8.1.0||~8.2.0||~8.3.0",
    +        "magento/framework": "103.0.*",
    +        "magento/module-wishlist": "101.2.*",
    +        "magento/module-analytics": "100.4.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -21,3 +22,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Wishlist/composer.json+25 23 modified
    @@ -1,37 +1,38 @@
     {
         "name": "magento/module-wishlist",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "101.2.7",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-backend": "*",
    -        "magento/module-catalog": "*",
    -        "magento/module-catalog-inventory": "*",
    -        "magento/module-checkout": "*",
    -        "magento/module-customer": "*",
    -        "magento/module-rss": "*",
    -        "magento/module-sales": "*",
    -        "magento/module-store": "*",
    -        "magento/module-theme": "*",
    -        "magento/module-ui": "*",
    -        "magento/module-captcha": "*"
    +        "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-rss": "100.4.*",
    +        "magento/module-sales": "103.0.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/module-theme": "101.1.*",
    +        "magento/module-ui": "101.2.*",
    +        "magento/module-captcha": "100.4.*"
         },
         "suggest": {
    -        "magento/module-configurable-product": "*",
    -        "magento/module-downloadable": "*",
    -        "magento/module-bundle": "*",
    -        "magento/module-cookie": "*",
    -        "magento/module-grouped-product": "*",
    -        "magento/module-wishlist-sample-data": "*"
    +        "magento/module-configurable-product": "100.4.*",
    +        "magento/module-downloadable": "100.4.*",
    +        "magento/module-bundle": "101.0.*",
    +        "magento/module-cookie": "100.4.*",
    +        "magento/module-grouped-product": "100.4.*",
    +        "magento/module-wishlist-sample-data": "Sample Data version: 100.4.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -41,3 +42,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/WishlistGraphQl/composer.json+12 10 modified
    @@ -2,20 +2,21 @@
         "name": "magento/module-wishlist-graph-ql",
         "description": "N/A",
         "type": "magento2-module",
    -    "require": {
    -        "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/module-wishlist": "*",
    -        "magento/module-store": "*",
    -        "magento/module-quote-graph-ql": "*",
    -        "magento/module-quote": "*",
    -        "magento/module-catalog": "*",
    -        "magento/module-catalog-graph-ql": "*"
    -    },
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "100.4.7",
    +    "require": {
    +        "php": "~8.1.0||~8.2.0||~8.3.0",
    +        "magento/framework": "103.0.*",
    +        "magento/module-wishlist": "101.2.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/module-quote-graph-ql": "100.4.*",
    +        "magento/module-quote": "101.2.*",
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-catalog-graph-ql": "100.4.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -25,3 +26,4 @@
             }
         }
     }
    +
    
  • app/design/adminhtml/Magento/backend/composer.json+8 6 modified
    @@ -1,21 +1,23 @@
     {
         "name": "magento/theme-adminhtml-backend",
         "description": "N/A",
    +    "type": "magento2-theme",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.7-p1",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*"
    +        "magento/framework": "103.0.*"
         },
    -    "type": "magento2-theme",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
             ]
         }
     }
    +
    
  • app/design/adminhtml/Magento/backend/i18n/en_US.csv+1 0 modified
    @@ -547,3 +547,4 @@ Dashboard,Dashboard
     "Web Section","Web Section"
     "Store Email Addresses Section","Store Email Addresses Section"
     "Email to a Friend","Email to a Friend"
    +"Invalid data type","Invalid data type"
    
  • app/design/frontend/Magento/blank/composer.json+8 6 modified
    @@ -1,21 +1,23 @@
     {
         "name": "magento/theme-frontend-blank",
         "description": "N/A",
    +    "type": "magento2-theme",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.7-p1",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*"
    +        "magento/framework": "103.0.*"
         },
    -    "type": "magento2-theme",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
             ]
         }
     }
    +
    
  • app/design/frontend/Magento/blank/i18n/en_US.csv+1 0 modified
    @@ -439,3 +439,4 @@ Summary,Summary
     Test,Test
     test,test
     Two,Two
    +"Invalid data type","Invalid data type"
    
  • app/design/frontend/Magento/luma/composer.json+9 7 modified
    @@ -1,22 +1,24 @@
     {
         "name": "magento/theme-frontend-luma",
         "description": "N/A",
    +    "type": "magento2-theme",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.7-p1",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
    -        "magento/framework": "*",
    -        "magento/theme-frontend-blank": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/theme-frontend-blank": "100.4.*"
         },
    -    "type": "magento2-theme",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
             ]
         }
     }
    +
    
  • app/design/frontend/Magento/luma/i18n/en_US.csv+1 0 modified
    @@ -489,3 +489,4 @@ Remove,Remove
     Test,Test
     test,test
     Two,Two
    +"Invalid data type","Invalid data type"
    
  • app/i18n/Magento/de_DE/composer.json+4 2 modified
    @@ -1,20 +1,22 @@
     {
         "name": "magento/language-de_de",
         "description": "German (Germany) language",
    +    "type": "magento2-language",
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.0",
         "require": {
    -        "magento/framework": "*"
    +        "magento/framework": "103.0.*"
         },
    -    "type": "magento2-language",
         "autoload": {
             "files": [
                 "registration.php"
             ]
         }
     }
    +
    
  • app/i18n/Magento/en_US/composer.json+4 2 modified
    @@ -1,20 +1,22 @@
     {
         "name": "magento/language-en_us",
         "description": "English (United States) language",
    +    "type": "magento2-language",
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.0",
         "require": {
    -        "magento/framework": "*"
    +        "magento/framework": "103.0.*"
         },
    -    "type": "magento2-language",
         "autoload": {
             "files": [
                 "registration.php"
             ]
         }
     }
    +
    
  • app/i18n/Magento/es_ES/composer.json+4 2 modified
    @@ -1,20 +1,22 @@
     {
         "name": "magento/language-es_es",
         "description": "Spanish (Spain) language",
    +    "type": "magento2-language",
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.0",
         "require": {
    -        "magento/framework": "*"
    +        "magento/framework": "103.0.*"
         },
    -    "type": "magento2-language",
         "autoload": {
             "files": [
                 "registration.php"
             ]
         }
     }
    +
    
  • app/i18n/Magento/fr_FR/composer.json+4 2 modified
    @@ -1,20 +1,22 @@
     {
         "name": "magento/language-fr_fr",
         "description": "French (France) language",
    +    "type": "magento2-language",
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.0",
         "require": {
    -        "magento/framework": "*"
    +        "magento/framework": "103.0.*"
         },
    -    "type": "magento2-language",
         "autoload": {
             "files": [
                 "registration.php"
             ]
         }
     }
    +
    
  • app/i18n/Magento/nl_NL/composer.json+4 2 modified
    @@ -1,20 +1,22 @@
     {
         "name": "magento/language-nl_nl",
         "description": "Dutch (Netherlands) language",
    +    "type": "magento2-language",
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.0",
         "require": {
    -        "magento/framework": "*"
    +        "magento/framework": "103.0.*"
         },
    -    "type": "magento2-language",
         "autoload": {
             "files": [
                 "registration.php"
             ]
         }
     }
    +
    
  • app/i18n/Magento/pt_BR/composer.json+4 2 modified
    @@ -1,20 +1,22 @@
     {
         "name": "magento/language-pt_br",
         "description": "Portuguese (Brazil) language",
    +    "type": "magento2-language",
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.0",
         "require": {
    -        "magento/framework": "*"
    +        "magento/framework": "103.0.*"
         },
    -    "type": "magento2-language",
         "autoload": {
             "files": [
                 "registration.php"
             ]
         }
     }
    +
    
  • app/i18n/Magento/zh_Hans_CN/composer.json+4 2 modified
    @@ -1,20 +1,22 @@
     {
         "name": "magento/language-zh_hans_cn",
         "description": "Chinese (China) language",
    +    "type": "magento2-language",
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.0",
         "require": {
    -        "magento/framework": "*"
    +        "magento/framework": "103.0.*"
         },
    -    "type": "magento2-language",
         "autoload": {
             "files": [
                 "registration.php"
             ]
         }
     }
    +
    
  • composer.json+276 274 modified
    @@ -16,6 +16,7 @@
             "preferred-install": "dist",
             "sort-packages": true
         },
    +    "version": "2.4.7-p1",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
             "ext-bcmath": "*",
    @@ -38,7 +39,7 @@
             "colinmollenhour/cache-backend-file": "^1.4",
             "colinmollenhour/cache-backend-redis": "^1.16",
             "colinmollenhour/credis": "^1.15",
    -        "colinmollenhour/php-redis-session-abstract": "^1.5",
    +        "colinmollenhour/php-redis-session-abstract": "~1.5.3",
             "composer/composer": "^2.0, !=2.2.16",
             "elasticsearch/elasticsearch": "~7.17.0 || ~8.5.0",
             "ezyang/htmlpurifier": "^4.17",
    @@ -92,6 +93,30 @@
             "webonyx/graphql-php": "^15.0",
             "wikimedia/less.php": "^3.2"
         },
    +    "suggest": {
    +        "ext-pcntl": "Need for run processes in parallel mode"
    +    },
    +    "autoload": {
    +        "exclude-from-classmap": [
    +            "**/dev/**",
    +            "**/update/**",
    +            "**/Test/**"
    +        ],
    +        "files": [
    +            "app/etc/NonComposerComponentRegistration.php"
    +        ],
    +        "psr-0": {
    +            "": [
    +                "app/code/",
    +                "generated/code/"
    +            ]
    +        },
    +        "psr-4": {
    +            "Magento\\": "app/code/Magento/",
    +            "Magento\\Framework\\": "lib/internal/Magento/Framework/",
    +            "Magento\\Setup\\": "setup/src/Magento/Setup/"
    +        }
    +    },
         "require-dev": {
             "allure-framework/allure-phpunit": "^2",
             "dealerdirect/phpcodesniffer-composer-installer": "^0.7 || ^1.0",
    @@ -107,299 +132,276 @@
             "sebastian/phpcpd": "^6.0",
             "symfony/finder": "^6.4"
         },
    -    "suggest": {
    -        "ext-pcntl": "Need for run processes in parallel mode"
    +    "conflict": {
    +        "gene/bluefoot": "*"
         },
         "replace": {
    -        "magento/module-marketplace": "*",
    -        "magento/module-admin-analytics": "*",
    -        "magento/module-admin-notification": "*",
    -        "magento/module-advanced-pricing-import-export": "*",
    -        "magento/module-advanced-search": "*",
    -        "magento/module-amqp": "*",
    -        "magento/module-analytics": "*",
    -        "magento/module-application-performance-monitor": "*",
    -        "magento/module-application-performance-monitor-new-relic": "*",
    -        "magento/module-asynchronous-operations": "*",
    -        "magento/module-authorization": "*",
    -        "magento/module-backend": "*",
    -        "magento/module-backup": "*",
    -        "magento/module-bundle": "*",
    -        "magento/module-bundle-graph-ql": "*",
    -        "magento/module-bundle-import-export": "*",
    -        "magento/module-cache-invalidate": "*",
    -        "magento/module-captcha": "*",
    -        "magento/module-cardinal-commerce": "*",
    -        "magento/module-catalog": "*",
    -        "magento/module-catalog-customer-graph-ql": "*",
    -        "magento/module-catalog-analytics": "*",
    -        "magento/module-catalog-import-export": "*",
    -        "magento/module-catalog-inventory": "*",
    -        "magento/module-catalog-inventory-graph-ql": "*",
    -        "magento/module-catalog-rule": "*",
    -        "magento/module-catalog-rule-graph-ql": "*",
    -        "magento/module-catalog-rule-configurable": "*",
    -        "magento/module-catalog-search": "*",
    -        "magento/module-catalog-url-rewrite": "*",
    -        "magento/module-catalog-widget": "*",
    -        "magento/module-checkout": "*",
    -        "magento/module-checkout-agreements": "*",
    -        "magento/module-checkout-agreements-graph-ql": "*",
    -        "magento/module-cms": "*",
    -        "magento/module-cms-url-rewrite": "*",
    -        "magento/module-compare-list-graph-ql": "*",
    -        "magento/module-config": "*",
    -        "magento/module-async-config": "*",
    -        "magento/module-configurable-import-export": "*",
    -        "magento/module-configurable-product": "*",
    -        "magento/module-configurable-product-sales": "*",
    -        "magento/module-contact": "*",
    -        "magento/module-contact-graph-ql": "*",
    -        "magento/module-cookie": "*",
    -        "magento/module-cron": "*",
    -        "magento/module-currency-symbol": "*",
    -        "magento/module-customer": "*",
    -        "magento/module-customer-analytics": "*",
    -        "magento/module-customer-downloadable-graph-ql": "*",
    -        "magento/module-customer-import-export": "*",
    -        "magento/module-deploy": "*",
    -        "magento/module-developer": "*",
    -        "magento/module-dhl": "*",
    -        "magento/module-directory": "*",
    -        "magento/module-directory-graph-ql": "*",
    -        "magento/module-downloadable": "*",
    -        "magento/module-downloadable-graph-ql": "*",
    -        "magento/module-downloadable-import-export": "*",
    -        "magento/module-eav": "*",
    -        "magento/module-open-search": "*",
    -        "magento/module-elasticsearch": "*",
    -        "magento/module-elasticsearch-7": "*",
    -        "magento/module-email": "*",
    -        "magento/module-encryption-key": "*",
    -        "magento/module-fedex": "*",
    -        "magento/module-gift-message": "*",
    -        "magento/module-gift-message-graph-ql": "*",
    -        "magento/module-google-adwords": "*",
    -        "magento/module-google-analytics": "*",
    -        "magento/module-google-optimizer": "*",
    -        "magento/module-google-gtag": "*",
    -        "magento/module-graph-ql": "*",
    -        "magento/module-graph-ql-cache": "*",
    -        "magento/module-graph-ql-new-relic": "*",
    -        "magento/module-graph-ql-resolver-cache": "*",
    -        "magento/module-catalog-graph-ql": "*",
    -        "magento/module-catalog-cms-graph-ql": "*",
    -        "magento/module-catalog-url-rewrite-graph-ql": "*",
    -        "magento/module-configurable-product-graph-ql": "*",
    -        "magento/module-customer-graph-ql": "*",
    -        "magento/module-eav-graph-ql": "*",
    -        "magento/module-swatches-graph-ql": "*",
    -        "magento/module-tax-graph-ql": "*",
    -        "magento/module-url-rewrite-graph-ql": "*",
    -        "magento/module-cms-url-rewrite-graph-ql": "*",
    -        "magento/module-weee-graph-ql": "*",
    -        "magento/module-cms-graph-ql": "*",
    -        "magento/module-grouped-import-export": "*",
    -        "magento/module-grouped-product": "*",
    -        "magento/module-grouped-catalog-inventory": "*",
    -        "magento/module-grouped-product-graph-ql": "*",
    -        "magento/module-import-export": "*",
    -        "magento/module-indexer": "*",
    -        "magento/module-instant-purchase": "*",
    -        "magento/module-integration": "*",
    -        "magento/module-integration-graph-ql": "*",
    -        "magento/module-layered-navigation": "*",
    -        "magento/module-login-as-customer": "*",
    -        "magento/module-login-as-customer-admin-ui": "*",
    -        "magento/module-login-as-customer-api": "*",
    -        "magento/module-login-as-customer-assistance": "*",
    -        "magento/module-login-as-customer-frontend-ui": "*",
    -        "magento/module-login-as-customer-graph-ql": "*",
    -        "magento/module-login-as-customer-log": "*",
    -        "magento/module-login-as-customer-quote": "*",
    -        "magento/module-login-as-customer-page-cache": "*",
    -        "magento/module-login-as-customer-sales": "*",
    -        "magento/module-media-content": "*",
    -        "magento/module-media-content-api": "*",
    -        "magento/module-media-content-catalog": "*",
    -        "magento/module-media-content-cms": "*",
    -        "magento/module-media-gallery": "*",
    -        "magento/module-media-gallery-api": "*",
    -        "magento/module-media-gallery-ui": "*",
    -        "magento/module-media-gallery-ui-api": "*",
    -        "magento/module-media-gallery-integration": "*",
    -        "magento/module-media-gallery-synchronization": "*",
    -        "magento/module-media-gallery-synchronization-api": "*",
    -        "magento/module-media-content-synchronization": "*",
    -        "magento/module-media-content-synchronization-api": "*",
    -        "magento/module-media-content-synchronization-catalog": "*",
    -        "magento/module-media-content-synchronization-cms": "*",
    -        "magento/module-media-gallery-synchronization-metadata": "*",
    -        "magento/module-media-gallery-metadata": "*",
    -        "magento/module-media-gallery-metadata-api": "*",
    -        "magento/module-media-gallery-catalog-ui": "*",
    -        "magento/module-media-gallery-cms-ui": "*",
    -        "magento/module-media-gallery-catalog-integration": "*",
    -        "magento/module-media-gallery-catalog": "*",
    -        "magento/module-media-gallery-renditions": "*",
    -        "magento/module-media-gallery-renditions-api": "*",
    -        "magento/module-media-storage": "*",
    -        "magento/module-message-queue": "*",
    -        "magento/module-msrp": "*",
    -        "magento/module-msrp-configurable-product": "*",
    -        "magento/module-msrp-grouped-product": "*",
    -        "magento/module-multishipping": "*",
    -        "magento/module-mysql-mq": "*",
    -        "magento/module-new-relic-reporting": "*",
    -        "magento/module-newsletter": "*",
    -        "magento/module-newsletter-graph-ql": "*",
    -        "magento/module-offline-payments": "*",
    -        "magento/module-offline-shipping": "*",
    -        "magento/module-order-cancellation": "*",
    -        "magento/module-order-cancellation-graph-ql": "*",
    -        "magento/module-order-cancellation-ui": "*",
    -        "magento/module-page-cache": "*",
    -        "magento/module-payment": "*",
    -        "magento/module-payment-graph-ql": "*",
    -        "magento/module-paypal": "*",
    -        "magento/module-paypal-captcha": "*",
    -        "magento/module-paypal-graph-ql": "*",
    -        "magento/module-persistent": "*",
    -        "magento/module-product-alert": "*",
    -        "magento/module-product-video": "*",
    -        "magento/module-quote": "*",
    -        "magento/module-quote-analytics": "*",
    -        "magento/module-quote-bundle-options": "*",
    -        "magento/module-quote-configurable-options": "*",
    -        "magento/module-quote-downloadable-links": "*",
    -        "magento/module-quote-graph-ql": "*",
    -        "magento/module-related-product-graph-ql": "*",
    -        "magento/module-release-notification": "*",
    -        "magento/module-reports": "*",
    -        "magento/module-require-js": "*",
    -        "magento/module-review": "*",
    -        "magento/module-review-graph-ql": "*",
    -        "magento/module-review-analytics": "*",
    -        "magento/module-robots": "*",
    -        "magento/module-rss": "*",
    -        "magento/module-rule": "*",
    -        "magento/module-sales": "*",
    -        "magento/module-sales-analytics": "*",
    -        "magento/module-sales-graph-ql": "*",
    -        "magento/module-sales-inventory": "*",
    -        "magento/module-sales-rule": "*",
    -        "magento/module-sales-rule-graph-ql": "*",
    -        "magento/module-sales-sequence": "*",
    -        "magento/module-sample-data": "*",
    -        "magento/module-search": "*",
    -        "magento/module-security": "*",
    -        "magento/module-send-friend": "*",
    -        "magento/module-send-friend-graph-ql": "*",
    -        "magento/module-shipping": "*",
    -        "magento/module-sitemap": "*",
    -        "magento/module-store": "*",
    -        "magento/module-store-graph-ql": "*",
    -        "magento/module-swagger": "*",
    -        "magento/module-swagger-webapi": "*",
    -        "magento/module-swagger-webapi-async": "*",
    -        "magento/module-swatches": "*",
    -        "magento/module-swatches-layered-navigation": "*",
    -        "magento/module-tax": "*",
    -        "magento/module-tax-import-export": "*",
    -        "magento/module-theme": "*",
    -        "magento/module-theme-graph-ql": "*",
    -        "magento/module-translation": "*",
    -        "magento/module-ui": "*",
    -        "magento/module-ups": "*",
    -        "magento/module-url-rewrite": "*",
    -        "magento/module-user": "*",
    -        "magento/module-usps": "*",
    -        "magento/module-variable": "*",
    -        "magento/module-vault": "*",
    -        "magento/module-vault-graph-ql": "*",
    -        "magento/module-version": "*",
    -        "magento/module-webapi": "*",
    -        "magento/module-webapi-async": "*",
    -        "magento/module-webapi-security": "*",
    -        "magento/module-weee": "*",
    -        "magento/module-widget": "*",
    -        "magento/module-wishlist": "*",
    -        "magento/module-wishlist-graph-ql": "*",
    -        "magento/module-wishlist-analytics": "*",
    -        "magento/theme-adminhtml-backend": "*",
    -        "magento/theme-frontend-blank": "*",
    -        "magento/theme-frontend-luma": "*",
    -        "magento/language-de_de": "*",
    -        "magento/language-en_us": "*",
    -        "magento/language-es_es": "*",
    -        "magento/language-fr_fr": "*",
    -        "magento/language-nl_nl": "*",
    -        "magento/language-pt_br": "*",
    -        "magento/language-zh_hans_cn": "*",
    -        "magento/framework": "*",
    -        "magento/framework-amqp": "*",
    -        "magento/framework-bulk": "*",
    -        "magento/framework-message-queue": "*",
    +        "magento/module-marketplace": "100.4.5",
    +        "magento/module-admin-analytics": "100.4.6",
    +        "magento/module-admin-notification": "100.4.6",
    +        "magento/module-advanced-pricing-import-export": "100.4.7",
    +        "magento/module-advanced-search": "100.4.5",
    +        "magento/module-amqp": "100.4.4",
    +        "magento/module-analytics": "100.4.7",
    +        "magento/module-application-performance-monitor": "100.4.0",
    +        "magento/module-application-performance-monitor-new-relic": "100.4.0",
    +        "magento/module-asynchronous-operations": "100.4.7",
    +        "magento/module-authorization": "100.4.7",
    +        "magento/module-backend": "102.0.7",
    +        "magento/module-backup": "100.4.7",
    +        "magento/module-bundle": "101.0.7",
    +        "magento/module-bundle-graph-ql": "100.4.7",
    +        "magento/module-bundle-import-export": "100.4.6",
    +        "magento/module-cache-invalidate": "100.4.5",
    +        "magento/module-captcha": "100.4.7",
    +        "magento/module-cardinal-commerce": "100.4.5",
    +        "magento/module-catalog": "104.0.7-p1",
    +        "magento/module-catalog-customer-graph-ql": "100.4.6",
    +        "magento/module-catalog-analytics": "100.4.4",
    +        "magento/module-catalog-import-export": "101.1.7",
    +        "magento/module-catalog-inventory": "100.4.7",
    +        "magento/module-catalog-inventory-graph-ql": "100.4.4",
    +        "magento/module-catalog-rule": "101.2.7",
    +        "magento/module-catalog-rule-graph-ql": "100.4.4",
    +        "magento/module-catalog-rule-configurable": "100.4.6",
    +        "magento/module-catalog-search": "102.0.7",
    +        "magento/module-catalog-url-rewrite": "100.4.7",
    +        "magento/module-catalog-widget": "100.4.7",
    +        "magento/module-checkout": "100.4.7",
    +        "magento/module-checkout-agreements": "100.4.6",
    +        "magento/module-checkout-agreements-graph-ql": "100.4.3",
    +        "magento/module-cms": "104.0.7",
    +        "magento/module-cms-url-rewrite": "100.4.6",
    +        "magento/module-compare-list-graph-ql": "100.4.3",
    +        "magento/module-config": "101.2.7",
    +        "magento/module-async-config": "100.4.0",
    +        "magento/module-configurable-import-export": "100.4.5",
    +        "magento/module-configurable-product": "100.4.7",
    +        "magento/module-configurable-product-sales": "100.4.4",
    +        "magento/module-contact": "100.4.6",
    +        "magento/module-contact-graph-ql": "100.4.0",
    +        "magento/module-cookie": "100.4.7",
    +        "magento/module-cron": "100.4.7",
    +        "magento/module-currency-symbol": "100.4.5",
    +        "magento/module-customer": "103.0.7-p1",
    +        "magento/module-customer-analytics": "100.4.4",
    +        "magento/module-customer-downloadable-graph-ql": "100.4.3",
    +        "magento/module-customer-import-export": "100.4.7",
    +        "magento/module-deploy": "100.4.7",
    +        "magento/module-developer": "100.4.7",
    +        "magento/module-dhl": "100.4.6",
    +        "magento/module-directory": "100.4.7",
    +        "magento/module-directory-graph-ql": "100.4.5",
    +        "magento/module-downloadable": "100.4.7",
    +        "magento/module-downloadable-graph-ql": "100.4.7",
    +        "magento/module-downloadable-import-export": "100.4.6",
    +        "magento/module-eav": "102.1.7",
    +        "magento/module-open-search": "100.4.1",
    +        "magento/module-elasticsearch": "101.0.7",
    +        "magento/module-elasticsearch-7": "100.4.7",
    +        "magento/module-email": "101.1.7",
    +        "magento/module-encryption-key": "100.4.5",
    +        "magento/module-fedex": "100.4.5",
    +        "magento/module-gift-message": "100.4.6",
    +        "magento/module-gift-message-graph-ql": "100.4.5",
    +        "magento/module-google-adwords": "100.4.4",
    +        "magento/module-google-analytics": "100.4.3",
    +        "magento/module-google-optimizer": "100.4.6",
    +        "magento/module-google-gtag": "100.4.2",
    +        "magento/module-graph-ql": "100.4.7",
    +        "magento/module-graph-ql-cache": "100.4.4",
    +        "magento/module-graph-ql-new-relic": "100.4.0",
    +        "magento/module-graph-ql-resolver-cache": "100.4.0",
    +        "magento/module-catalog-graph-ql": "100.4.7",
    +        "magento/module-catalog-cms-graph-ql": "100.4.3",
    +        "magento/module-catalog-url-rewrite-graph-ql": "100.4.5",
    +        "magento/module-configurable-product-graph-ql": "100.4.7",
    +        "magento/module-customer-graph-ql": "100.4.7",
    +        "magento/module-eav-graph-ql": "100.4.4",
    +        "magento/module-swatches-graph-ql": "100.4.5",
    +        "magento/module-tax-graph-ql": "100.4.3",
    +        "magento/module-url-rewrite-graph-ql": "100.4.6",
    +        "magento/module-cms-url-rewrite-graph-ql": "100.4.5",
    +        "magento/module-weee-graph-ql": "100.4.4",
    +        "magento/module-cms-graph-ql": "100.4.4",
    +        "magento/module-grouped-import-export": "100.4.5",
    +        "magento/module-grouped-product": "100.4.7",
    +        "magento/module-grouped-catalog-inventory": "100.4.4",
    +        "magento/module-grouped-product-graph-ql": "100.4.7",
    +        "magento/module-import-export": "101.0.7",
    +        "magento/module-indexer": "100.4.7",
    +        "magento/module-instant-purchase": "100.4.6",
    +        "magento/module-integration": "100.4.7",
    +        "magento/module-integration-graph-ql": "100.4.0",
    +        "magento/module-layered-navigation": "100.4.7",
    +        "magento/module-login-as-customer": "100.4.7",
    +        "magento/module-login-as-customer-admin-ui": "100.4.7",
    +        "magento/module-login-as-customer-api": "100.4.6",
    +        "magento/module-login-as-customer-assistance": "100.4.6",
    +        "magento/module-login-as-customer-frontend-ui": "100.4.6",
    +        "magento/module-login-as-customer-graph-ql": "100.4.4",
    +        "magento/module-login-as-customer-log": "100.4.5",
    +        "magento/module-login-as-customer-quote": "100.4.5",
    +        "magento/module-login-as-customer-page-cache": "100.4.6",
    +        "magento/module-login-as-customer-sales": "100.4.6",
    +        "magento/module-media-content": "100.4.5",
    +        "magento/module-media-content-api": "100.4.6",
    +        "magento/module-media-content-catalog": "100.4.5",
    +        "magento/module-media-content-cms": "100.4.5",
    +        "magento/module-media-gallery": "100.4.6",
    +        "magento/module-media-gallery-api": "101.0.6",
    +        "magento/module-media-gallery-ui": "100.4.6",
    +        "magento/module-media-gallery-ui-api": "100.4.5",
    +        "magento/module-media-gallery-integration": "100.4.6",
    +        "magento/module-media-gallery-synchronization": "100.4.6",
    +        "magento/module-media-gallery-synchronization-api": "100.4.5",
    +        "magento/module-media-content-synchronization": "100.4.6",
    +        "magento/module-media-content-synchronization-api": "100.4.5",
    +        "magento/module-media-content-synchronization-catalog": "100.4.4",
    +        "magento/module-media-content-synchronization-cms": "100.4.4",
    +        "magento/module-media-gallery-synchronization-metadata": "100.4.3",
    +        "magento/module-media-gallery-metadata": "100.4.5",
    +        "magento/module-media-gallery-metadata-api": "100.4.4",
    +        "magento/module-media-gallery-catalog-ui": "100.4.4",
    +        "magento/module-media-gallery-cms-ui": "100.4.4",
    +        "magento/module-media-gallery-catalog-integration": "100.4.4",
    +        "magento/module-media-gallery-catalog": "100.4.4",
    +        "magento/module-media-gallery-renditions": "100.4.5",
    +        "magento/module-media-gallery-renditions-api": "100.4.4",
    +        "magento/module-media-storage": "100.4.6",
    +        "magento/module-message-queue": "100.4.7",
    +        "magento/module-msrp": "100.4.6",
    +        "magento/module-msrp-configurable-product": "100.4.4",
    +        "magento/module-msrp-grouped-product": "100.4.4",
    +        "magento/module-multishipping": "100.4.7",
    +        "magento/module-mysql-mq": "100.4.5",
    +        "magento/module-new-relic-reporting": "100.4.5",
    +        "magento/module-newsletter": "100.4.7",
    +        "magento/module-newsletter-graph-ql": "100.4.4",
    +        "magento/module-offline-payments": "100.4.5",
    +        "magento/module-offline-shipping": "100.4.6",
    +        "magento/module-order-cancellation": "100.4.0",
    +        "magento/module-order-cancellation-graph-ql": "100.4.0",
    +        "magento/module-order-cancellation-ui": "100.4.0",
    +        "magento/module-page-cache": "100.4.7",
    +        "magento/module-payment": "100.4.7",
    +        "magento/module-payment-graph-ql": "100.4.2",
    +        "magento/module-paypal": "101.0.7",
    +        "magento/module-paypal-captcha": "100.4.4",
    +        "magento/module-paypal-graph-ql": "100.4.5",
    +        "magento/module-persistent": "100.4.7",
    +        "magento/module-product-alert": "100.4.6",
    +        "magento/module-product-video": "100.4.7",
    +        "magento/module-quote": "101.2.7-p1",
    +        "magento/module-quote-analytics": "100.4.6",
    +        "magento/module-quote-bundle-options": "100.4.3",
    +        "magento/module-quote-configurable-options": "100.4.3",
    +        "magento/module-quote-downloadable-links": "100.4.3",
    +        "magento/module-quote-graph-ql": "100.4.7",
    +        "magento/module-related-product-graph-ql": "100.4.4",
    +        "magento/module-release-notification": "100.4.5",
    +        "magento/module-reports": "100.4.7",
    +        "magento/module-require-js": "100.4.3",
    +        "magento/module-review": "100.4.7",
    +        "magento/module-review-graph-ql": "100.4.3",
    +        "magento/module-review-analytics": "100.4.4",
    +        "magento/module-robots": "101.1.3",
    +        "magento/module-rss": "100.4.5",
    +        "magento/module-rule": "100.4.6",
    +        "magento/module-sales": "103.0.7-p1",
    +        "magento/module-sales-analytics": "100.4.4",
    +        "magento/module-sales-graph-ql": "100.4.7",
    +        "magento/module-sales-inventory": "100.4.4",
    +        "magento/module-sales-rule": "101.2.7",
    +        "magento/module-sales-rule-graph-ql": "100.4.0",
    +        "magento/module-sales-sequence": "100.4.4",
    +        "magento/module-sample-data": "100.4.5",
    +        "magento/module-search": "101.1.7",
    +        "magento/module-security": "100.4.7",
    +        "magento/module-send-friend": "100.4.5",
    +        "magento/module-send-friend-graph-ql": "100.4.3",
    +        "magento/module-shipping": "100.4.7",
    +        "magento/module-sitemap": "100.4.6",
    +        "magento/module-store": "101.1.7",
    +        "magento/module-store-graph-ql": "100.4.5",
    +        "magento/module-swagger": "100.4.6",
    +        "magento/module-swagger-webapi": "100.4.3",
    +        "magento/module-swagger-webapi-async": "100.4.3",
    +        "magento/module-swatches": "100.4.7",
    +        "magento/module-swatches-layered-navigation": "100.4.3",
    +        "magento/module-tax": "100.4.7",
    +        "magento/module-tax-import-export": "100.4.6",
    +        "magento/module-theme": "101.1.7",
    +        "magento/module-theme-graph-ql": "100.4.4",
    +        "magento/module-translation": "100.4.7",
    +        "magento/module-ui": "101.2.7",
    +        "magento/module-ups": "100.4.7-p1",
    +        "magento/module-url-rewrite": "102.0.6",
    +        "magento/module-user": "101.2.7",
    +        "magento/module-usps": "100.4.6",
    +        "magento/module-variable": "100.4.5",
    +        "magento/module-vault": "101.2.7",
    +        "magento/module-vault-graph-ql": "100.4.3",
    +        "magento/module-version": "100.4.4",
    +        "magento/module-webapi": "100.4.6-p1",
    +        "magento/module-webapi-async": "100.4.5",
    +        "magento/module-webapi-security": "100.4.4",
    +        "magento/module-weee": "100.4.7",
    +        "magento/module-widget": "101.2.7",
    +        "magento/module-wishlist": "101.2.7",
    +        "magento/module-wishlist-graph-ql": "100.4.7",
    +        "magento/module-wishlist-analytics": "100.4.5",
    +        "magento/theme-adminhtml-backend": "100.4.7-p1",
    +        "magento/theme-frontend-blank": "100.4.7-p1",
    +        "magento/theme-frontend-luma": "100.4.7-p1",
    +        "magento/language-de_de": "100.4.0",
    +        "magento/language-en_us": "100.4.0",
    +        "magento/language-es_es": "100.4.0",
    +        "magento/language-fr_fr": "100.4.0",
    +        "magento/language-nl_nl": "100.4.0",
    +        "magento/language-pt_br": "100.4.0",
    +        "magento/language-zh_hans_cn": "100.4.0",
    +        "magento/framework": "103.0.7-p1",
    +        "magento/framework-amqp": "100.4.5",
    +        "magento/framework-bulk": "101.0.3",
    +        "magento/framework-message-queue": "100.4.7",
             "trentrichardson/jquery-timepicker-addon": "1.4.3",
             "components/jquery": "1.11.0",
             "components/jqueryui": "1.10.4",
             "twbs/bootstrap": "3.1.0",
             "tinymce/tinymce": "3.4.7",
    -        "magento/module-csp": "*",
    -        "magento/module-aws-s3": "*",
    -        "magento/module-remote-storage": "*",
    -        "magento/module-jwt-framework-adapter": "*",
    -        "magento/module-jwt-user-token": "*"
    +        "magento/module-csp": "100.4.6",
    +        "magento/module-aws-s3": "100.4.5",
    +        "magento/module-remote-storage": "100.4.5",
    +        "magento/module-jwt-framework-adapter": "100.4.3",
    +        "magento/module-jwt-user-token": "100.4.2"
         },
    -    "conflict": {
    -        "gene/bluefoot": "*"
    +    "autoload-dev": {
    +        "psr-4": {
    +            "Magento\\PhpStan\\": "dev/tests/static/framework/Magento/PhpStan/",
    +            "Magento\\Sniffs\\": "dev/tests/static/framework/Magento/Sniffs/",
    +            "Magento\\TestFramework\\Inspection\\": "dev/tests/static/framework/Magento/TestFramework/Inspection/",
    +            "Magento\\TestFramework\\Utility\\": "dev/tests/static/framework/Magento/TestFramework/Utility/",
    +            "Magento\\Tools\\": "dev/tools/Magento/Tools/",
    +            "Magento\\Tools\\Sanity\\": "dev/build/publication/sanity/Magento/Tools/Sanity/"
    +        }
         },
    +    "prefer-stable": true,
         "extra": {
             "component_paths": {
    -            "trentrichardson/jquery-timepicker-addon": "lib/web/jquery/jquery-ui-timepicker-addon.js",
                 "components/jquery": [
                     "lib/web/jquery.js",
                     "lib/web/jquery/jquery.min.js"
                 ],
                 "components/jqueryui": [
                     "lib/web/jquery/jquery-ui.js"
                 ],
    +            "tinymce/tinymce": "lib/web/tiny_mce_5",
    +            "trentrichardson/jquery-timepicker-addon": "lib/web/jquery/jquery-ui-timepicker-addon.js",
                 "twbs/bootstrap": [
                     "lib/web/jquery/jquery.tabs.js"
    -            ],
    -            "tinymce/tinymce": "lib/web/tiny_mce_5"
    -        }
    -    },
    -    "autoload": {
    -        "psr-4": {
    -            "Magento\\Framework\\": "lib/internal/Magento/Framework/",
    -            "Magento\\Setup\\": "setup/src/Magento/Setup/",
    -            "Magento\\": "app/code/Magento/"
    -        },
    -        "psr-0": {
    -            "": [
    -                "app/code/",
    -                "generated/code/"
                 ]
    -        },
    -        "files": [
    -            "app/etc/NonComposerComponentRegistration.php"
    -        ],
    -        "exclude-from-classmap": [
    -            "**/dev/**",
    -            "**/update/**",
    -            "**/Test/**"
    -        ]
    -    },
    -    "autoload-dev": {
    -        "psr-4": {
    -            "Magento\\Sniffs\\": "dev/tests/static/framework/Magento/Sniffs/",
    -            "Magento\\Tools\\": "dev/tools/Magento/Tools/",
    -            "Magento\\Tools\\Sanity\\": "dev/build/publication/sanity/Magento/Tools/Sanity/",
    -            "Magento\\TestFramework\\Inspection\\": "dev/tests/static/framework/Magento/TestFramework/Inspection/",
    -            "Magento\\TestFramework\\Utility\\": "dev/tests/static/framework/Magento/TestFramework/Utility/",
    -            "Magento\\PhpStan\\": "dev/tests/static/framework/Magento/PhpStan/"
             }
    -    },
    -    "prefer-stable": true
    +    }
     }
    +
    
  • composer.lock+7 7 modified
    @@ -4,7 +4,7 @@
             "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
             "This file is @generated automatically"
         ],
    -    "content-hash": "b51a34214644ae13c3de0c65848483e7",
    +    "content-hash": "a504dcb8af9496dc2d370bd0f916e947",
         "packages": [
             {
                 "name": "aws/aws-crt-php",
    @@ -395,16 +395,16 @@
             },
             {
                 "name": "colinmollenhour/php-redis-session-abstract",
    -            "version": "v1.5.4",
    +            "version": "v1.5.5",
                 "source": {
                     "type": "git",
                     "url": "https://github.com/colinmollenhour/php-redis-session-abstract.git",
    -                "reference": "c2e6ed15eb9cb363c9097fafefa590039fbadcb0"
    +                "reference": "5d93866cd53701ef8f866cb41cb5c6d7259d4416"
                 },
                 "dist": {
                     "type": "zip",
    -                "url": "https://api.github.com/repos/colinmollenhour/php-redis-session-abstract/zipball/c2e6ed15eb9cb363c9097fafefa590039fbadcb0",
    -                "reference": "c2e6ed15eb9cb363c9097fafefa590039fbadcb0",
    +                "url": "https://api.github.com/repos/colinmollenhour/php-redis-session-abstract/zipball/5d93866cd53701ef8f866cb41cb5c6d7259d4416",
    +                "reference": "5d93866cd53701ef8f866cb41cb5c6d7259d4416",
                     "shasum": ""
                 },
                 "require": {
    @@ -433,9 +433,9 @@
                 "homepage": "https://github.com/colinmollenhour/php-redis-session-abstract",
                 "support": {
                     "issues": "https://github.com/colinmollenhour/php-redis-session-abstract/issues",
    -                "source": "https://github.com/colinmollenhour/php-redis-session-abstract/tree/v1.5.4"
    +                "source": "https://github.com/colinmollenhour/php-redis-session-abstract/tree/v1.5.5"
                 },
    -            "time": "2024-01-03T14:16:31+00:00"
    +            "time": "2024-02-03T06:04:45+00:00"
             },
             {
                 "name": "composer/ca-bundle",
    
  • dev/tests/api-functional/testsuite/Magento/Customer/Api/CustomerRepositoryTest.php+17 15 modified
    @@ -3,6 +3,7 @@
      * Copyright © Magento, Inc. All rights reserved.
      * See COPYING.txt for license details.
      */
    +declare(strict_types=1);
     
     namespace Magento\Customer\Api;
     
    @@ -142,7 +143,7 @@ protected function tearDown(): void
         }
     
         /**
    -     * Validate update by invalid customer.
    +     * Validate update operation by invalid customer
          *
          */
         public function testInvalidCustomerUpdate()
    @@ -152,9 +153,8 @@ public function testInvalidCustomerUpdate()
             //Create first customer and retrieve customer token.
             $firstCustomerData = $this->_createCustomer();
     
    -        // get customer ID token
    +        //Get customer ID token
             /** @var \Magento\Integration\Api\CustomerTokenServiceInterface $customerTokenService */
    -        //$customerTokenService = $this->objectManager->create(CustomerTokenServiceInterface::class);
             $customerTokenService = Bootstrap::getObjectManager()->create(
                 \Magento\Integration\Api\CustomerTokenServiceInterface::class
             );
    @@ -238,11 +238,9 @@ public function testDeleteCustomer()
                     'operation' => self::SERVICE_NAME . 'DeleteById',
                 ],
             ];
    -        if (TESTS_WEB_API_ADAPTER == self::ADAPTER_SOAP) {
    -            $response = $this->_webApiCall($serviceInfo, ['customerId' => $customerData['id']]);
    -        } else {
    -            $response = $this->_webApiCall($serviceInfo);
    -        }
    +        $response = (TESTS_WEB_API_ADAPTER === self::ADAPTER_SOAP)
    +            ? $this->_webApiCall($serviceInfo, ['customerId' => $customerData['id']])
    +            : $this->_webApiCall($serviceInfo);
     
             $this->assertTrue($response);
     
    @@ -253,7 +251,7 @@ public function testDeleteCustomer()
         }
     
         /**
    -     * Check that non authorized consumer can`t delete customer.
    +     * Check that non-authorized consumer can`t delete customer.
          *
          * @return void
          */
    @@ -347,10 +345,10 @@ public function testDeleteCustomerInvalidCustomerId(): void
         public function testUpdateCustomer(): void
         {
             $customerId = 1;
    -        $updatedLastname = 'Updated lastname';
    +        $updatedLastName = 'Updated lastname';
             $customer = $this->getCustomerData($customerId);
             $customerData = $this->dataObjectProcessor->buildOutputDataArray($customer, Customer::class);
    -        $customerData[Customer::LASTNAME] = $updatedLastname;
    +        $customerData[Customer::LASTNAME] = $updatedLastName;
     
             $serviceInfo = [
                 'rest' => [
    @@ -364,16 +362,20 @@ public function testUpdateCustomer(): void
                 ],
             ];
     
    -        $requestData['customer'] = TESTS_WEB_API_ADAPTER === self::ADAPTER_SOAP
    +        $requestData['customer'] = (TESTS_WEB_API_ADAPTER === self::ADAPTER_SOAP)
                 ? $customerData
    -            : [Customer::LASTNAME => $updatedLastname];
    -
    +            : [
    +                Customer::FIRSTNAME => $customer->getFirstname(),
    +                Customer::LASTNAME => $updatedLastName,
    +                Customer::EMAIL => $customer->getEmail(),
    +                Customer::ID => $customerId,
    +            ];
             $response = $this->_webApiCall($serviceInfo, $requestData);
             $this->assertNotNull($response);
     
             //Verify if the customer is updated
             $existingCustomerDataObject = $this->getCustomerData($customerId);
    -        $this->assertEquals($updatedLastname, $existingCustomerDataObject->getLastname());
    +        $this->assertEquals($updatedLastName, $existingCustomerDataObject->getLastname());
             $this->assertEquals($customerData[Customer::FIRSTNAME], $existingCustomerDataObject->getFirstname());
         }
     
    
  • dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/RetrieveOrdersByOrderNumberTest.php+2 16 modified
    @@ -517,7 +517,7 @@ public function testGetCustomerDescendingSortedOrders()
       customer {
         orders(
           sort: {
    -        sort_field: CREATED_AT,
    +        sort_field: NUMBER,
             sort_direction: DESC
           }
         ) {
    @@ -553,22 +553,8 @@ public function testGetCustomerDescendingSortedOrders()
             $order8 = $this->fixtures->get('or8')->getIncrementId();
     
             $expectedOrderNumbersOptions = [$order8, $order7, $order6, $order5, $order4, $order3, $order2 ];
    -        $expectedOrderNumbers = $scalarTemp = [];
    -        $compDate = $prevComKey = '';
    -        foreach ($expectedOrderNumbersOptions as $comKey => $comData) {
    -            if ($compDate == $customerOrderItemsInResponse[$comKey]['order_date']) {
    -                $expectedOrderNumbers[] = $expectedOrderNumbers[$prevComKey];
    -                $scalarTemp = (array)$comData;
    -                $expectedOrderNumbers[$prevComKey] = $scalarTemp[0];
    -            } else {
    -                $scalarTemp = (array)$comData;
    -                $expectedOrderNumbers[] = $scalarTemp[0];
    -            }
    -            $prevComKey = $comKey;
    -            $compDate = $customerOrderItemsInResponse[$comKey]['order_date'];
    -        }
     
    -        foreach ($expectedOrderNumbers as $key => $data) {
    +        foreach ($expectedOrderNumbersOptions as $key => $data) {
                 $orderItemInResponse = $customerOrderItemsInResponse[$key];
                 $this->assertEquals(
                     $data,
    
  • dev/tests/api-functional/testsuite/Magento/LoginAsCustomerAssistance/Plugin/CustomerAfterPluginTest.php+16 10 modified
    @@ -77,16 +77,19 @@ public function testUpdateCustomer(int $state, bool $expected): void
         {
             $customerId = (int)$this->customerRepository->get('customer@example.com')->getId();
     
    -        $updatedLastname = 'Updated lastname';
    +        $updatedLastName = 'Updated lastname';
             $customer = $this->getCustomerData($customerId);
             $customerData = $this->dataObjectProcessor->buildOutputDataArray($customer, Customer::class);
    -        $customerData[Customer::LASTNAME] = $updatedLastname;
    +        $customerData[Customer::LASTNAME] = $updatedLastName;
             $customerData[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]['assistance_allowed'] = $state;
     
    -        $requestData['customer'] = TESTS_WEB_API_ADAPTER === self::ADAPTER_SOAP
    +        $requestData['customer'] = (TESTS_WEB_API_ADAPTER === self::ADAPTER_SOAP)
                 ? $customerData
                 : [
    -                Customer::LASTNAME => $updatedLastname,
    +                Customer::FIRSTNAME => $customer->getFirstname(),
    +                Customer::LASTNAME => $updatedLastName,
    +                Customer::EMAIL => $customer->getEmail(),
    +                Customer::ID => $customerId,
                     Customer::EXTENSION_ATTRIBUTES_KEY => ['assistance_allowed' => $state]
                 ];
     
    @@ -95,7 +98,7 @@ public function testUpdateCustomer(int $state, bool $expected): void
             $this->assertNotNull($response);
     
             $existingCustomerDataObject = $this->getCustomerData($customerId);
    -        $this->assertEquals($updatedLastname, $existingCustomerDataObject->getLastname());
    +        $this->assertEquals($updatedLastName, $existingCustomerDataObject->getLastname());
             $this->assertEquals($expected, $this->isAssistanceEnabled->execute($customerId));
         }
     
    @@ -117,16 +120,19 @@ public function testUpdateCustomerWithLimitedResources(int $state): void
             ];
             $customerId = (int)$this->customerRepository->get('customer@example.com')->getId();
     
    -        $updatedLastname = 'Updated lastname';
    +        $updatedLastName = 'Updated lastname';
             $customer = $this->getCustomerData($customerId);
             $customerData = $this->dataObjectProcessor->buildOutputDataArray($customer, Customer::class);
    -        $customerData[Customer::LASTNAME] = $updatedLastname;
    +        $customerData[Customer::LASTNAME] = $updatedLastName;
             $customerData[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]['assistance_allowed'] = $state;
     
    -        $requestData['customer'] = TESTS_WEB_API_ADAPTER === self::ADAPTER_SOAP
    +        $requestData['customer'] = (TESTS_WEB_API_ADAPTER === self::ADAPTER_SOAP)
                 ? $customerData
                 : [
    -                Customer::LASTNAME => $updatedLastname,
    +                Customer::FIRSTNAME => $customer->getFirstname(),
    +                Customer::LASTNAME => $updatedLastName,
    +                Customer::EMAIL => $customer->getEmail(),
    +                Customer::ID => $customerId,
                     Customer::EXTENSION_ATTRIBUTES_KEY => ['assistance_allowed' => $state]
                 ];
     
    @@ -136,7 +142,7 @@ public function testUpdateCustomerWithLimitedResources(int $state): void
             $this->assertNotNull($response);
     
             $existingCustomerDataObject = $this->getCustomerData($customerId);
    -        $this->assertEquals($updatedLastname, $existingCustomerDataObject->getLastname());
    +        $this->assertEquals($updatedLastName, $existingCustomerDataObject->getLastname());
             $this->assertEquals(false, $this->isAssistanceEnabled->execute($customerId));
         }
     
    
  • dev/tests/integration/testsuite/Magento/CatalogInventory/Model/StockItemSave/OnProductUpdate/ByProductModel/ByQuantityAndStockStatusTest.php+2 2 modified
    @@ -44,7 +44,7 @@ protected function setUp(): void
          * model (deprecated)
          *
          * @magentoDataFixture Magento/Catalog/_files/product_simple.php
    -     * @magentoDbIsolation disabled
    +     * @magentoDbIsolation enabled
          */
         public function testSaveBySetQuantityAndStockStatus()
         {
    @@ -61,7 +61,7 @@ public function testSaveBySetQuantityAndStockStatus()
          * via product model (deprecated)
          *
          * @magentoDataFixture Magento/Catalog/_files/product_simple.php
    -     * @magentoDbIsolation disabled
    +     * @magentoDbIsolation enabled
          */
         public function testSaveBySetData()
         {
    
  • dev/tests/integration/testsuite/Magento/CatalogInventory/Model/StockItemSave/OnProductUpdate/ByProductModel/ByStockDataTest.php+2 2 modified
    @@ -44,7 +44,7 @@ protected function setUp(): void
          * model (deprecated)
          *
          * @magentoDataFixture Magento/Catalog/_files/product_simple.php
    -     * @magentoDbIsolation disabled
    +     * @magentoDbIsolation enabled
          */
         public function testSaveBySetStockData()
         {
    @@ -61,7 +61,7 @@ public function testSaveBySetStockData()
          * via product model (deprecated)
          *
          * @magentoDataFixture Magento/Catalog/_files/product_simple.php
    -     * @magentoDbIsolation disabled
    +     * @magentoDbIsolation enabled
          */
         public function testSaveBySetData()
         {
    
  • dev/tests/integration/testsuite/Magento/CatalogInventory/Model/StockItemSave/OnProductUpdate/ByProductModel/ByStockItemTest.php+4 4 modified
    @@ -58,7 +58,7 @@ protected function setUp(): void
          * Test saving of stock item by product data via product model (deprecated)
          *
          * @magentoDataFixture Magento/Catalog/_files/product_simple.php
    -     * @magentoDbIsolation disabled
    +     * @magentoDbIsolation enabled
          */
         public function testSave()
         {
    @@ -76,7 +76,7 @@ public function testSave()
          * product model (deprecated)
          *
          * @magentoDataFixture Magento/Catalog/_files/product_simple.php
    -     * @magentoDbIsolation disabled
    +     * @magentoDbIsolation enabled
          */
         public function testSaveManuallyCreatedStockItem()
         {
    @@ -97,7 +97,7 @@ public function testSaveManuallyCreatedStockItem()
          * product repository (deprecated)
          *
          * @magentoDataFixture Magento/Catalog/_files/product_simple.php
    -     * @magentoDbIsolation disabled
    +     * @magentoDbIsolation enabled
          */
         public function testSaveManuallyUpdatedStockItem()
         {
    @@ -116,7 +116,7 @@ public function testSaveManuallyUpdatedStockItem()
     
         /**
          * @magentoDataFixture Magento/Catalog/_files/product_simple.php
    -     * @magentoDbIsolation disabled
    +     * @magentoDbIsolation enabled
          */
         public function testAutomaticIsInStockUpdate(): void
         {
    
  • dev/tests/integration/testsuite/Magento/CatalogInventory/Model/StockItemSave/OnProductUpdate/ByProductRepository/ByQuantityAndStockStatusTest.php+2 2 modified
    @@ -44,7 +44,7 @@ protected function setUp(): void
          * repository
          *
          * @magentoDataFixture Magento/Catalog/_files/product_simple.php
    -     * @magentoDbIsolation disabled
    +     * @magentoDbIsolation enabled
          */
         public function testSaveBySetQuantityAndStockStatus()
         {
    @@ -61,7 +61,7 @@ public function testSaveBySetQuantityAndStockStatus()
          * via product repository
          *
          * @magentoDataFixture Magento/Catalog/_files/product_simple.php
    -     * @magentoDbIsolation disabled
    +     * @magentoDbIsolation enabled
          */
         public function testSaveBySetData()
         {
    
  • dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/DeleteFilesTest.php+2 0 modified
    @@ -161,6 +161,8 @@ public function executeDataProvider(): array
                 ['name with[ bracket.jpg'],
                 ['magento_small_image.jpg'],
                 ['_.jpg'],
    +            [' - .jpg'],
    +            ['-.jpg'],
             ];
         }
     
    
  • dev/tests/integration/testsuite/Magento/Sales/Helper/AdminTest.php+2 2 modified
    @@ -76,7 +76,7 @@ public function escapeHtmlWithLinksDataProvider(): array
                 ],
                 [
                     '<a href=\"#\">Foo</a>',
    -                '<a href="#">Foo</a>',
    +                '<a href="%5C&quot;#%5C&quot;">Foo</a>',
                     'allowedTags' => ['a'],
                 ],
                 [
    @@ -86,7 +86,7 @@ public function escapeHtmlWithLinksDataProvider(): array
                 ],
                 [
                     "<a href=\"javascript&colon;alert(59)\">Foo</a>",
    -                '<a href="#">Foo</a>',
    +                '<a href="javascript&amp;colon;alert(59)">Foo</a>',
                     'allowedTags' => ['a'],
                 ],
                 [
    
  • lib/internal/Magento/Framework/Amqp/composer.json+10 8 modified
    @@ -1,25 +1,27 @@
     {
         "name": "magento/framework-amqp",
         "description": "N/A",
    -    "config": {
    -        "sort-packages": true
    -    },
         "type": "magento2-library",
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "config": {
    +        "sort-packages": true
    +    },
    +    "version": "100.4.5",
         "require": {
    -        "magento/framework": "*",
    +        "magento/framework": "103.0.*",
             "php": "~8.1.0||~8.2.0||~8.3.0",
             "php-amqplib/php-amqplib": "~3.2.0"
         },
         "autoload": {
    -        "psr-4": {
    -            "Magento\\Framework\\Amqp\\": ""
    -        },
             "files": [
                 "registration.php"
    -        ]
    +        ],
    +        "psr-4": {
    +            "Magento\\Framework\\Amqp\\": ""
    +        }
         }
     }
    +
    
  • lib/internal/Magento/Framework/Bulk/composer.json+10 8 modified
    @@ -1,24 +1,26 @@
     {
         "name": "magento/framework-bulk",
         "description": "N/A",
    -    "config": {
    -        "sort-packages": true
    -    },
         "type": "magento2-library",
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "config": {
    +        "sort-packages": true
    +    },
    +    "version": "101.0.3",
         "require": {
    -        "magento/framework": "*",
    +        "magento/framework": "103.0.*",
             "php": "~8.1.0||~8.2.0||~8.3.0"
         },
         "autoload": {
    -        "psr-4": {
    -            "Magento\\Framework\\Bulk\\": ""
    -        },
             "files": [
                 "registration.php"
    -        ]
    +        ],
    +        "psr-4": {
    +            "Magento\\Framework\\Bulk\\": ""
    +        }
         }
     }
    +
    
  • lib/internal/Magento/Framework/composer.json+7 5 modified
    @@ -9,6 +9,7 @@
         "config": {
             "sort-packages": true
         },
    +    "version": "103.0.7-p1",
         "require": {
             "php": "~8.1.0||~8.2.0||~8.3.0",
             "ext-bcmath": "*",
    @@ -23,7 +24,7 @@
             "ext-sodium": "*",
             "ext-xsl": "*",
             "lib-libxml": "*",
    -        "colinmollenhour/php-redis-session-abstract": "^1.5",
    +        "colinmollenhour/php-redis-session-abstract": "~1.5.3",
             "composer/composer": "^2.0, !=2.2.16",
             "ezyang/htmlpurifier": "^4.17",
             "guzzlehttp/guzzle": "^7.5",
    @@ -65,11 +66,12 @@
             "ext-imagick": "Use Image Magick >=3.0.0 as an optional alternative image processing library"
         },
         "autoload": {
    -        "psr-4": {
    -            "Magento\\Framework\\": ""
    -        },
             "files": [
                 "registration.php"
    -        ]
    +        ],
    +        "psr-4": {
    +            "Magento\\Framework\\": ""
    +        }
         }
     }
    +
    
  • lib/internal/Magento/Framework/Filesystem/Directory/PathValidator.php+1 1 modified
    @@ -54,7 +54,7 @@ public function validate(
                 $actualPath = $this->driver->getRealPathSafety($path);
             }
     
    -        if (preg_match('/(?:^-|\s-)/', $path)
    +        if (preg_match('/(?:^-|\s-\S)/', $path)
                 || (
                     mb_strpos($actualPath, $realDirectoryPath) !== 0
                     && rtrim($path, DIRECTORY_SEPARATOR) !== $realDirectoryPath
    
  • lib/internal/Magento/Framework/Filesystem/Test/Unit/Directory/PathValidatorTest.php+4 2 modified
    @@ -1,7 +1,5 @@
     <?php declare(strict_types=1);
     /**
    - * Unit Test for \Magento\Framework\Filesystem\Directory\PathValidator
    - *
      * Copyright © Magento, Inc. All rights reserved.
      * See COPYING.txt for license details.
      */
    @@ -12,6 +10,9 @@
     use PHPUnit\Framework\MockObject\MockObject;
     use PHPUnit\Framework\TestCase;
     
    +/**
    + * Unit Test for \Magento\Framework\Filesystem\Directory\PathValidator
    + */
     class PathValidatorTest extends TestCase
     {
         /**
    @@ -77,6 +78,7 @@ public function validateDataProvider()
             return [
                 ['/directory/path/', '/directory/path/', '/', false, '/://'],
                 ['/directory/path/', '/var/.regenerate', null, false, ''],
    +            ['/directory/path/', '/var/image - 1.jpg', null, false, ''],
             ];
         }
     }
    
  • lib/internal/Magento/Framework/MessageQueue/composer.json+10 8 modified
    @@ -1,24 +1,26 @@
     {
         "name": "magento/framework-message-queue",
         "description": "N/A",
    -    "config": {
    -        "sort-packages": true
    -    },
         "type": "magento2-library",
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "config": {
    +        "sort-packages": true
    +    },
    +    "version": "100.4.7",
         "require": {
    -        "magento/framework": "*",
    +        "magento/framework": "103.0.*",
             "php": "~8.1.0||~8.2.0||~8.3.0"
         },
         "autoload": {
    -        "psr-4": {
    -            "Magento\\Framework\\MessageQueue\\": ""
    -        },
             "files": [
                 "registration.php"
    -        ]
    +        ],
    +        "psr-4": {
    +            "Magento\\Framework\\MessageQueue\\": ""
    +        }
         }
     }
    +
    
  • lib/internal/Magento/Framework/Webapi/ServiceInputProcessor.php+6 0 modified
    @@ -278,6 +278,12 @@ protected function _createFromArray($className, $data)
             // convert to string directly to avoid situations when $className is object
             // which implements __toString method like \ReflectionObject
             $className = (string) $className;
    +        if (is_subclass_of($className, \SimpleXMLElement::class)
    +            || is_subclass_of($className, \DOMElement::class)) {
    +            throw new SerializationException(
    +                new Phrase('Invalid data type')
    +            );
    +        }
             $class = new ClassReflection($className);
             if (is_subclass_of($className, self::EXTENSION_ATTRIBUTES_TYPE)) {
                 $className = substr($className, 0, -strlen('Interface'));
    
c5c538810b87

Magento Release 2.4.6-p6

https://github.com/magento/magento2magento packaging serviceJun 6, 2024via ghsa
300 files changed · +1896 888
  • app/code/Magento/AdminAnalytics/composer.json+14 11 modified
    @@ -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.5-p6",
         "require": {
             "php": "~8.1.0||~8.2.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 @@
             }
         }
     }
    +
    
  • app/code/Magento/AdminAnalytics/view/adminhtml/templates/tracking.phtml+17 9 modified
    @@ -6,6 +6,7 @@
     
     /**
      * @var \Magento\Framework\View\Helper\SecureHtmlRenderer $secureRenderer
    + * @var \Magento\Framework\Escaper $escaper
      */
     ?>
     
    @@ -22,18 +23,25 @@
     <?php
     /** @var \Magento\AdminAnalytics\ViewModel\Metadata $metadata */
     $metadata = $block->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 . '";
     ';
     ?>
     <?= /* @noEscape */ $secureRenderer->renderTag('script', [], $scriptString, false); ?>
    
  • app/code/Magento/AdminAnalytics/ViewModel/Metadata.php+29 1 modified
    @@ -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;
    +    }
     }
    
  • app/code/Magento/AdminNotification/composer.json+13 11 modified
    @@ -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.5",
         "require": {
             "php": "~8.1.0||~8.2.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 @@
             }
         }
     }
    +
    
  • app/code/Magento/AdvancedPricingImportExport/composer.json+16 14 modified
    @@ -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.6",
         "require": {
             "php": "~8.1.0||~8.2.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 @@
             }
         }
     }
    +
    
  • app/code/Magento/AdvancedSearch/composer.json+15 13 modified
    @@ -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.4",
         "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": "~8.1.0||~8.2.0"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -29,3 +30,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Amqp/composer.json+10 8 modified
    @@ -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.3",
         "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": "~8.1.0||~8.2.0"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -24,3 +25,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Analytics/composer.json+10 8 modified
    @@ -1,19 +1,20 @@
     {
         "name": "magento/module-analytics",
         "description": "N/A",
    -    "require": {
    -        "php": "~8.1.0||~8.2.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.6-p4",
    +    "require": {
    +        "php": "~8.1.0||~8.2.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 @@
             }
         }
     }
    +
    
  • app/code/Magento/Analytics/Test/Mftf/Test/AdminConfigurationPermissionTest.xml+1 1 modified
    @@ -35,7 +35,7 @@
             <click selector="{{AdminUserGridSection.searchResultFirstRow}}" stepKey="clickFoundUsername"/>
             <waitForPageLoad time="30" stepKey="wait2"/>
             <seeInField selector="{{AdminEditUserSection.usernameTextField}}" userInput="$$noReportUser.username$$" stepKey="seeUsernameInField"/>
    -        <fillField selector="{{AdminEditUserSection.currentPasswordField}}" userInput="{{_ENV.MAGENTO_ADMIN_PASSWORD}}" stepKey="fillCurrentPassword"/>
    +        <fillField selector="{{AdminEditUserSection.currentPasswordField}}" userInput="{{_CREDS.magento/MAGENTO_ADMIN_PASSWORD}}" stepKey="fillCurrentPassword"/>
             <scrollToTopOfPage stepKey="scrollToTopOfPage"/>
     
             <click selector="{{AdminEditUserSection.userRoleTab}}" stepKey="clickUserRoleTab"/>
    
  • app/code/Magento/AsynchronousOperations/composer.json+14 12 modified
    @@ -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.6",
         "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": "~8.1.0||~8.2.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 @@
             }
         }
     }
    +
    
  • app/code/Magento/Authorization/composer.json+9 7 modified
    @@ -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.6",
         "require": {
             "php": "~8.1.0||~8.2.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 @@
             }
         }
     }
    +
    
  • app/code/Magento/AwsS3/composer.json+8 6 modified
    @@ -1,18 +1,19 @@
     {
         "name": "magento/module-aws-s3",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "proprietary"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.4-p4",
         "require": {
             "php": "~8.1.0||~8.2.0",
    -        "magento/framework": "*",
    -        "magento/module-remote-storage": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-remote-storage": "100.4.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "proprietary"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -22,3 +23,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/AwsS3/Test/Mftf/Test/AwsS3AdminCreateDownloadableProductWithLinkTest.xml+4 1 modified
    @@ -35,6 +35,7 @@
                 <comment userInput="BIC workaround" stepKey="disableRemoteStorage"/>
                 <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com"/>
                 <!-- Delete customer -->
    +            <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="logoutCustomer" />
                 <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/>
     
                 <!-- Delete category -->
    @@ -81,7 +82,9 @@
     
             <!-- Save product -->
             <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/>
    -        <magentoCron stepKey="runIndexCronJobs" groups="index"/>
    +        <actionGroup ref="CliIndexerReindexActionGroup" stepKey="runIndexCronJobs">
    +            <argument name="indices" value=""/>
    +        </actionGroup>
     
             <!-- Login to frontend -->
             <actionGroup ref="LoginToStorefrontActionGroup" stepKey="signIn">
    
  • app/code/Magento/Backend/composer.json+26 24 modified
    @@ -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.6-p6",
         "require": {
             "php": "~8.1.0||~8.2.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 @@
             }
         }
     }
    +
    
  • app/code/Magento/Backend/etc/adminhtml/system.xml+3 0 modified
    @@ -10,6 +10,9 @@
             <tab id="general" translate="label" sortOrder="100">
                 <label>General</label>
             </tab>
    +        <tab id="security" translate="label" sortOrder="200">
    +            <label>Security</label>
    +        </tab>
             <tab id="service" translate="label" sortOrder="99999">
                 <label>Services</label>
             </tab>
    
  • app/code/Magento/Backend/Test/Mftf/ActionGroup/AdminLoginActionGroup.xml+2 1 modified
    @@ -14,7 +14,8 @@
             </annotations>
             <arguments>
                 <argument name="username" type="string" defaultValue="{{_ENV.MAGENTO_ADMIN_USERNAME}}"/>
    -            <argument name="password" type="string" defaultValue="{{_CREDS.magento/MAGENTO_ADMIN_PASSWORD}}"/></arguments>
    +            <argument name="password" type="string" defaultValue="{{_CREDS.magento/MAGENTO_ADMIN_PASSWORD}}"/>
    +        </arguments>
     
             <amOnPage url="{{AdminLoginPage.url}}" stepKey="navigateToAdmin"/>
             <fillField selector="{{AdminLoginFormSection.username}}" userInput="{{username}}" stepKey="fillUsername"/>
    
  • app/code/Magento/Backend/Test/Mftf/ActionGroup/SetAdminAccountActionGroup.xml+1 1 modified
    @@ -20,7 +20,7 @@
             <waitForElementVisible selector="{{AdminSystemAccountSection.interfaceLocale}}" stepKey="waitForInterfaceLocale"/>
             <!-- Change Admin locale to Français (France) / French (France) -->
             <selectOption userInput="{{InterfaceLocaleByValue}}" selector="{{AdminSystemAccountSection.interfaceLocale}}" stepKey="setInterfaceLocate"/>
    -        <fillField selector="{{AdminSystemAccountSection.currentPassword}}" userInput="{{_ENV.MAGENTO_ADMIN_PASSWORD}}" stepKey="fillPassword"/>
    +        <fillField selector="{{AdminSystemAccountSection.currentPassword}}" userInput="{{_CREDS.magento/MAGENTO_ADMIN_PASSWORD}}" stepKey="fillPassword"/>
             <click selector="{{AdminMainActionsSection.save}}" stepKey="clickSave"/>
             <waitForElementVisible selector="{{AdminMessagesSection.success}}" stepKey="waitSuccessMessage"/>
             <see selector="{{AdminMessagesSection.success}}" userInput="You saved the account." stepKey="seeSuccessMessage"/>
    
  • app/code/Magento/Backend/Test/Mftf/Section/AdminMessagesSection.xml+1 0 modified
    @@ -23,5 +23,6 @@
             <element name="errorMessage" type="text" selector=".message.message-error.error"/>
             <element name="warningMessage" type="text" selector=".message-warning"/>
             <element name="noticeMessage" type="text" selector=".message-notice"/>
    +        <element name="warningMessageNotice" type="text" selector="div.message.message-warning.notice"/>
         </section>
     </sections>
    
  • app/code/Magento/Backend/Test/Mftf/Test/AdminAttributeTextSwatchesCanBeFiledTest.xml+6 2 modified
    @@ -57,7 +57,9 @@
                 <actionGroup ref="AdminDeleteStoreViewActionGroup" stepKey="deleteStoreView10">
                     <argument name="customStore" value="storeViewData7"/>
                 </actionGroup>
    -            <magentoCron groups="index" stepKey="reindex"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
     
                 <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/>
             </after>
    @@ -93,7 +95,9 @@
             <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createStoreView10">
                 <argument name="customStore" value="storeViewData7"/>
             </actionGroup>
    -        <magentoCron groups="index" stepKey="reindex"/>
    +        <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +            <argument name="indices" value=""/>
    +        </actionGroup>
     
             <!--Navigate to Product attribute page-->
             <actionGroup ref="AdminNavigateToNewProductAttributePageActionGroup" stepKey="navigateToNewProductAttributePage"/>
    
  • app/code/Magento/Backend/Test/Mftf/Test/AdminCheckDashboardWithChartsTest.xml+1 0 modified
    @@ -51,6 +51,7 @@
                     <argument name="tags" value="config full_page"/>
                 </actionGroup>
                 <deleteData createDataKey="createProduct" stepKey="deleteProduct"/>
    +            <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="logoutCustomer" />
                 <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/>
                 <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/>
             </after>
    
  • app/code/Magento/Backend/Test/Mftf/Test/AdminDashboardWithChartsTest.xml+5 2 modified
    @@ -20,7 +20,7 @@
                 <group value="backend"/>
                 <skip>
                     <issueId value="DEPRECATED">Use AdminCheckDashboardWithChartsTest instead</issueId>
    -            </skip> 
    +            </skip>
                 <group value="pr_exclude"/>
             </annotations>
             <before>
    @@ -32,14 +32,17 @@
                     <field key="firstname">John1</field>
                     <field key="lastname">Doe1</field>
                 </createData>
    -            <magentoCron stepKey="runCronIndex" groups="index"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="runCronIndex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </before>
             <after>
                 <!-- Reset admin order filter -->
                 <comment userInput="Reset admin order filter" stepKey="resetAdminOrderFilter"/>
                 <actionGroup ref="AdminOrdersGridClearFiltersActionGroup" stepKey="clearOrderFilters"/>
                 <magentoCLI command="config:set admin/dashboard/enable_charts 0" stepKey="setDisableChartsAsDefault"/>
                 <deleteData createDataKey="createProduct" stepKey="deleteProduct"/>
    +            <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="logoutCustomer" />
                 <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/>
                 <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/>
             </after>
    
  • app/code/Magento/Backend/Test/Mftf/Test/AdminLoginFailedTest.xml+1 1 modified
    @@ -22,7 +22,7 @@
             </annotations>
     
             <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin">
    -            <argument name="password" value="INVALID!{{_ENV.MAGENTO_ADMIN_PASSWORD}}"/>
    +            <argument name="password" value="INVALID!"/>
             </actionGroup>
             <actionGroup ref="AssertMessageOnAdminLoginActionGroup" stepKey="assertErrorMessage"/>
         </test>
    
  • app/code/Magento/Backup/composer.json+11 9 modified
    @@ -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.6",
         "require": {
             "php": "~8.1.0||~8.2.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 @@
             }
         }
     }
    +
    
  • app/code/Magento/Bundle/composer.json+27 25 modified
    @@ -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.6-p6",
         "require": {
             "php": "~8.1.0||~8.2.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 @@
             }
         }
     }
    +
    
  • app/code/Magento/BundleGraphQl/composer.json+15 13 modified
    @@ -2,23 +2,24 @@
         "name": "magento/module-bundle-graph-ql",
         "description": "N/A",
         "type": "magento2-module",
    -    "require": {
    -        "php": "~8.1.0||~8.2.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.6",
    +    "require": {
    +        "php": "~8.1.0||~8.2.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 @@
             }
         }
     }
    +
    
  • app/code/Magento/BundleImportExport/composer.json+14 12 modified
    @@ -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.5-p4",
         "require": {
             "php": "~8.1.0||~8.2.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 @@
             }
         }
     }
    +
    
  • app/code/Magento/BundleImportExport/Test/Mftf/Test/AdminImportBundleProductTest.xml+1 0 modified
    @@ -50,6 +50,7 @@
             <after>
                 <!-- Delete Data -->
                 <deleteData createDataKey="createImportCategory" stepKey="deleteImportCategory"/>
    +            <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="logoutCustomer" />
                 <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/>
                 <helper class="Magento\Catalog\Test\Mftf\Helper\LocalFileAssertions" method="deleteDirectory" stepKey="deleteProductImageDirectory">
                     <argument name="path">var/import/images/{{ImportProduct_Bundle.name}}</argument>
    
  • app/code/Magento/Bundle/Test/Mftf/ActionGroup/AdminCreateApiDynamicBundleProductActionGroup.xml+1 1 modified
    @@ -62,6 +62,6 @@
                 <requiredEntity createDataKey="simpleProduct4"/>
             </createData>
     
    -        <magentoCron stepKey="runCronIndex" groups="index"/>
    +        <magentoCLI command="indexer:reindex" stepKey="runCronIndex"/>
         </actionGroup>
     </actionGroups>
    
  • app/code/Magento/Bundle/Test/Mftf/ActionGroup/AdminCreateApiDynamicBundleProductAllOptionTypesActionGroup.xml+1 1 modified
    @@ -70,6 +70,6 @@
                 <requiredEntity createDataKey="createBundleRadioButtonsOption"/>
                 <requiredEntity createDataKey="simpleProduct2"/>
             </createData>
    -        <magentoCron stepKey="runCronIndex" groups="index"/>
    +        <magentoCLI command="indexer:reindex" stepKey="runCronIndex"/>
         </actionGroup>
     </actionGroups>
    
  • app/code/Magento/Bundle/Test/Mftf/ActionGroup/AdminCreateApiFixedBundleProductActionGroup.xml+1 1 modified
    @@ -61,6 +61,6 @@
                 <requiredEntity createDataKey="createBundleOption1_2"/>
                 <requiredEntity createDataKey="simpleProduct4"/>
             </createData>
    -        <magentoCron stepKey="runCronIndex" groups="index"/>
    +        <magentoCLI command="indexer:reindex" stepKey="runCronIndex"/>
         </actionGroup>
     </actionGroups>
    
  • app/code/Magento/Bundle/Test/Mftf/Test/AdminAddBundleItemsTest.xml+3 1 modified
    @@ -24,7 +24,9 @@
                 <createData entity="SimpleProduct2" stepKey="simpleProduct1"/>
                 <createData entity="SimpleProduct2" stepKey="simpleProduct2"/>
                 <createData entity="SimpleProduct2" stepKey="simpleProduct3"/>
    -            <magentoCron stepKey="runCronIndex" groups="index"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="runCronIndex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
                 <actionGroup stepKey="loginToAdminPanel" ref="AdminLoginActionGroup"/>
             </before>
             <after>
    
  • app/code/Magento/Bundle/Test/Mftf/Test/AdminAddBundleProductToCartFromWishListPageTest.xml+3 1 modified
    @@ -50,7 +50,9 @@
                     <requiredEntity createDataKey="bundleOption"/>
                     <requiredEntity createDataKey="createSimpleProduct2"/>
                 </createData>
    -            <magentoCron stepKey="runCronIndex" groups="index"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="runCronIndex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
     
                 <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToProductEditPage">
                     <argument name="productId" value="$$createProduct.id$$"/>
    
  • app/code/Magento/Bundle/Test/Mftf/Test/AdminAddDefaultImageBundleProductTest.xml+4 2 modified
    @@ -22,7 +22,9 @@
                 <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/>
                 <createData entity="SimpleProduct2" stepKey="simpleProduct1"/>
                 <createData entity="SimpleProduct2" stepKey="simpleProduct2"/>
    -            <magentoCron stepKey="runCronIndex" groups="index"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="runCronIndex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </before>
             <after>
                 <actionGroup ref="AdminLogoutActionGroup" stepKey="amOnLogoutPage"/>
    @@ -58,7 +60,7 @@
             </actionGroup>
             <actionGroup ref="AdminCheckFirstCheckboxInAddProductsToOptionPanelGridActionGroup" stepKey="selectFirstGridRow2"/>
             <click selector="{{AdminAddProductsToOptionPanel.addSelectedProducts}}" stepKey="clickAddSelectedBundleProducts"/>
    -        
    +
             <actionGroup ref="AdminFillBundleItemQtyActionGroup" stepKey="fillProductDefaultQty1">
                 <argument name="optionIndex" value="0"/>
                 <argument name="productIndex" value="0"/>
    
  • app/code/Magento/Bundle/Test/Mftf/Test/AdminAssociateBundleProductToWebsitesTest.xml+6 2 modified
    @@ -63,7 +63,9 @@
                     <argument name="StoreGroup" value="SecondStoreGroupUnique"/>
                     <argument name="customStore" value="SecondStoreUnique"/>
                 </actionGroup>
    -            <magentoCron groups="index" stepKey="reindex2"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex2">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </before>
             <after>
                 <!-- Disabled Store URLs -->
    @@ -78,7 +80,9 @@
                 <actionGroup ref="AdminDeleteWebsiteActionGroup" stepKey="deleteWebsite">
                     <argument name="websiteName" value="{{secondCustomWebsite.name}}"/>
                 </actionGroup>
    -            <magentoCron groups="index" stepKey="reindex"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
     
                 <actionGroup ref="NavigateToAndResetProductGridToDefaultViewActionGroup" stepKey="resetProductGridFilter"/>
     
    
  • app/code/Magento/Bundle/Test/Mftf/Test/AdminBundleDynamicAttributesAfterMassUpdateTest.xml+1 4 modified
    @@ -31,23 +31,20 @@
             <actionGroup ref="FilterProductGridByNameActionGroup" stepKey="filterBundleProductByName">
                 <argument name="product" value="ApiFixedBundleProduct"/>
             </actionGroup>
    -
             <checkOption selector="{{AdminProductGridSection.productGridCheckboxOnRow('1')}}" stepKey="selectFirstProductFromGrid"/>
             <actionGroup ref="AdminClickMassUpdateProductAttributesActionGroup" stepKey="goToUpdateProductAttributesPage"/>
    -
             <checkOption selector="{{AdminEditProductAttributesSection.ChangeAttributeDynamicPriceToggle}}" stepKey="toggleToChangeDynamicPrice"/>
             <selectOption selector="{{AdminEditProductAttributesSection.AttributeDynamicPrice}}" userInput="No" stepKey="disableDynamicPrice"/>
             <checkOption selector="{{AdminEditProductAttributesSection.ChangeAttributeDynamicSKUToggle}}" stepKey="toggleToChangeDynamicSKU"/>
             <checkOption selector="{{AdminEditProductAttributesSection.ChangeAttributeDynamicWeightToggle}}" stepKey="toggleToChangeDynamicWeight"/>
             <selectOption selector="{{AdminEditProductAttributesSection.AttributeDynamicSKU}}" userInput="No" stepKey="disableDynamicSKU"/>
             <selectOption selector="{{AdminEditProductAttributesSection.AttributeDynamicWeight}}" userInput="No" stepKey="disableDynamicWeight"/>
             <actionGroup ref="AdminSaveProductsMassAttributesUpdateActionGroup" stepKey="saveMassAttributeUpdate"/>
    -
             <actionGroup ref="CliConsumerStartActionGroup" stepKey="startMessageQueueConsumer">
                 <argument name="consumerName" value="{{AdminProductAttributeUpdateConsumerData.consumerName}}"/>
                 <argument name="maxMessages" value="{{AdminProductAttributeUpdateConsumerData.messageLimit}}"/>
             </actionGroup>
    -        <magentoCLI command="cron:run" stepKey="runCron"/>
    +        <magentoCron stepKey="runCron"/>
     
             <actionGroup ref="OpenProductForEditByClickingRowXColumnYInProductGridActionGroup" stepKey="openProductForEdit"/>
     
    
  • app/code/Magento/Bundle/Test/Mftf/Test/AdminBundleProductPriceCalculationOnProductPageTest.xml+3 1 modified
    @@ -32,7 +32,9 @@
                 <!-- deleting category, simple products -->
                 <deleteData createDataKey="simpleProduct" stepKey="deleteProduct"/>
                 <deleteData createDataKey="createCategory" stepKey="deleteCategory"/>
    -            <magentoCron groups="index" stepKey="reindex"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
                 <actionGroup ref="CliCacheCleanActionGroup" stepKey="flushCache">
                     <argument name="tags" value="full_page"/>
                 </actionGroup>
    
  • app/code/Magento/Bundle/Test/Mftf/Test/AdminCreateAndEditBundleProductOptionsNegativeTest.xml+6 2 modified
    @@ -25,7 +25,9 @@
             <before>
                 <!-- Create a Website -->
                 <createData entity="customWebsite" stepKey="createWebsite"/>
    -            <magentoCron groups="index" stepKey="reindex"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
     
                 <!-- Create first simple product for a bundle option -->
                 <createData entity="SimpleProduct2" stepKey="createFirstSimpleProduct"/>
    @@ -47,7 +49,9 @@
                 <actionGroup ref="AdminDeleteWebsiteActionGroup" stepKey="deleteWebsite">
                     <argument name="websiteName" value="Second Website"/>
                 </actionGroup>
    -            <magentoCron groups="index" stepKey="reindex"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
     
                 <!-- Log out -->
                 <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/>
    
  • app/code/Magento/Bundle/Test/Mftf/Test/AdminCreateAndEditBundleProductSettingsTest.xml+6 2 modified
    @@ -21,7 +21,9 @@
             <before>
                 <!-- Create a Website -->
                 <createData entity="customWebsite" stepKey="createWebsite"/>
    -            <magentoCron groups="index" stepKey="reindex"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
     
                 <!-- Create a simple product for a bundle option -->
                 <createData entity="SimpleProduct2" stepKey="createSimpleProduct"/>
    @@ -37,7 +39,9 @@
                 <actionGroup ref="AdminDeleteWebsiteActionGroup" stepKey="deleteWebsite">
                     <argument name="websiteName" value="Second Website"/>
                 </actionGroup>
    -            <magentoCron groups="index" stepKey="reindex"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
     
                 <!-- Log out -->
                 <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/>
    
  • app/code/Magento/Bundle/Test/Mftf/Test/AdminDeleteABundleProductTest.xml+3 1 modified
    @@ -22,7 +22,9 @@
                 <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/>
                 <createData entity="SimpleProduct2" stepKey="simpleProduct1"/>
                 <createData entity="SimpleProduct2" stepKey="simpleProduct2"/>
    -            <magentoCron stepKey="runCronIndex" groups="index"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="runCronIndex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </before>
             <after>
                 <actionGroup ref="AdminLogoutActionGroup" stepKey="amOnLogoutPage"/>
    
  • app/code/Magento/Bundle/Test/Mftf/Test/AdminDeleteBundleDynamicPriceProductTest.xml+3 1 modified
    @@ -42,7 +42,9 @@
             <after>
                 <deleteData createDataKey="createCategory" stepKey="deleteCategory"/>
                 <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/>
    -            <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindexInvalidatedIndices">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
                 <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/>
             </after>
             <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="deleteBundleProductBySku">
    
  • app/code/Magento/Bundle/Test/Mftf/Test/AdminDeleteBundleDynamicProductTest.xml+3 1 modified
    @@ -27,7 +27,9 @@
                 <createData entity="ApiBundleProductPriceViewRange" stepKey="createDynamicBundleProduct">
                     <requiredEntity createDataKey="createCategory"/>
                 </createData>
    -            <magentoCron stepKey="runCronIndex" groups="index"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="runCronIndex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </before>
             <after>
                 <deleteData createDataKey="createCategory" stepKey="deleteCategory"/>
    
  • app/code/Magento/Bundle/Test/Mftf/Test/AdminDeleteBundleFixedProductTest.xml+3 1 modified
    @@ -23,7 +23,9 @@
                 <createData entity="FixedBundleProduct" stepKey="createFixedBundleProduct">
                     <requiredEntity createDataKey="createCategory"/>
                 </createData>
    -            <magentoCron stepKey="runCronIndex" groups="index"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="runCronIndex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </before>
             <after>
                 <deleteData createDataKey="createCategory" stepKey="deleteCategory"/>
    
  • app/code/Magento/Bundle/Test/Mftf/Test/AdminEditRelatedBundleProductTest.xml+6 2 modified
    @@ -23,7 +23,9 @@
                 <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/>
                 <createData entity="SimpleProduct2" stepKey="simpleProduct0"/>
                 <createData entity="SimpleProduct2" stepKey="simpleProduct1"/>
    -            <magentoCron stepKey="runCronIndex" groups="index"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="runCronIndex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </before>
             <after>
                 <!-- Delete the bundled product -->
    @@ -78,7 +80,9 @@
                 <argument name="expectedText" value="$$simpleProduct1.name$$"/>
             </actionGroup>
     
    -        <magentoCLI command="cron:run --group=index" stepKey="runCronIndexer"/>
    +        <actionGroup ref="CliIndexerReindexActionGroup" stepKey="runCronIndexer">
    +            <argument name="indices" value=""/>
    +        </actionGroup>
     
             <!--See related product in storefront-->
             <actionGroup ref="StorefrontOpenProductPageActionGroup" stepKey="goToStorefront">
    
  • app/code/Magento/Bundle/Test/Mftf/Test/AdminFilterProductListByBundleProductInDutchUserLanguageTest.xml+10 2 modified
    @@ -8,7 +8,7 @@
     
     <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd">
    -        <test name="AdminFilterProductListByBundleProductInDutchUserLanguageTest">
    +    <test name="AdminFilterProductListByBundleProductInDutchUserLanguageTest">
             <annotations>
                 <features value="Bundle"/>
                 <stories value="Admin list bundle products when user language is set as Dutch"/>
    @@ -26,7 +26,9 @@
                 </createData>
                 <!-- Enable Changing Locale to Dutch -->
                 <magentoCLI command="setup:static-content:deploy" arguments="-f nl_NL" stepKey="staticDeployAfterChangeLocaleToNL"/>
    -            <magentoCron groups="index" stepKey="runCronIndex"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="runCronIndex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
                 <actionGroup ref="AdminLoginActionGroup" stepKey="login"/>
             </before>
             <after>
    @@ -42,6 +44,12 @@
                     <argument name="InterfaceLocaleByValue" value="en_US" />
                 </actionGroup>
                 <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
    +            <actionGroup ref="CliCacheCleanActionGroup" stepKey="cleanInvalidatedCaches">
    +                <argument name="tags" value="config full_page"/>
    +            </actionGroup>
             </after>
     
             <!-- Change Admin locale to Nederlands (Nederland) / Nederlands (Nederland) -->
    
  • app/code/Magento/Bundle/Test/Mftf/Test/AdminFilterProductListByBundleProductTest.xml+3 1 modified
    @@ -22,7 +22,9 @@
                 <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/>
                 <createData entity="SimpleProduct2" stepKey="simpleProduct1"/>
                 <createData entity="SimpleProduct2" stepKey="simpleProduct2"/>
    -            <magentoCron stepKey="runCronIndex" groups="index"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="runCronIndex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </before>
             <after>
                 <actionGroup ref="AdminLogoutActionGroup" stepKey="amOnLogoutPage"/>
    
  • app/code/Magento/Bundle/Test/Mftf/Test/AdminMassDeleteBundleProductsTest.xml+3 1 modified
    @@ -24,7 +24,9 @@
                 <createData entity="SimpleProduct2" stepKey="simpleProduct2"/>
                 <createData entity="SimpleProduct2" stepKey="simpleProduct3"/>
                 <createData entity="SimpleProduct2" stepKey="simpleProduct4"/>
    -            <magentoCron stepKey="runCronIndex" groups="index"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="runCronIndex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </before>
             <after>
                 <!--Clear Filters-->
    
  • app/code/Magento/Bundle/Test/Mftf/Test/AdminProductBundleCreationTest.xml+3 1 modified
    @@ -23,7 +23,9 @@
                 <createData entity="_defaultCategory" stepKey="createPreReqCategory"/>
                 <createData entity="SimpleProduct2" stepKey="simpleProduct1"/>
                 <createData entity="SimpleProduct2" stepKey="simpleProduct2"/>
    -            <magentoCron stepKey="runCronIndex" groups="index"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="runCronIndex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
                 <!-- Admin Login-->
                 <actionGroup stepKey="loginToAdminPanel" ref="AdminLoginActionGroup"/>
             </before>
    
  • app/code/Magento/Bundle/Test/Mftf/Test/AdminRemoveDefaultImageBundleProductTest.xml+3 1 modified
    @@ -22,7 +22,9 @@
                 <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/>
                 <createData entity="SimpleProduct2" stepKey="simpleProduct1"/>
                 <createData entity="SimpleProduct2" stepKey="simpleProduct2"/>
    -            <magentoCron stepKey="runCronIndex" groups="index"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="runCronIndex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </before>
             <after>
                 <!-- Delete the bundled product we created in the test body -->
    
  • app/code/Magento/Bundle/Test/Mftf/Test/AdminShouldBeAbleToMassUpdateAttributesForBundleProductsTest.xml+3 1 modified
    @@ -34,7 +34,9 @@
                     <requiredEntity createDataKey="createBundleOption"/>
                     <requiredEntity createDataKey="createSimpleProduct"/>
                 </createData>
    -            <magentoCLI stepKey="runCronIndex" command="cron:run --group=index"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="runCronIndex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </before>
             <after>
                 <!-- Delete Simple Product -->
    
  • app/code/Magento/Bundle/Test/Mftf/Test/AdminValidateBundleProductWithBundleItemsOptionPerPageTest.xml+3 1 modified
    @@ -22,7 +22,9 @@
                 <actionGroup ref="AdminLoginActionGroup" stepKey="login"/>
                 <createData entity="SimpleProduct2" stepKey="simpleProduct1"/>
                 <createData entity="SimpleProduct2" stepKey="simpleProduct2"/>
    -            <magentoCron stepKey="runCronIndex" groups="index"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="runCronIndex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </before>
             <after>
                 <!--Delete custom added per page-->
    
  • app/code/Magento/Bundle/Test/Mftf/Test/AdvanceCatalogSearchBundleByDescriptionTest.xml+5 1 modified
    @@ -38,7 +38,11 @@
                     <requiredEntity createDataKey="simple2"/>
                 </createData>
     
    -            <magentoCron stepKey="runCronReindex" groups="index"/>
    +            <comment userInput="BIC workaround" stepKey="runCronReindex"/>
    +
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="runCronIndex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </before>
             <after>
                 <deleteData createDataKey="simple1" stepKey="deleteSimple1" before="deleteSimple2"/>
    
  • app/code/Magento/Bundle/Test/Mftf/Test/AdvanceCatalogSearchBundleByPriceTest.xml+5 1 modified
    @@ -46,7 +46,11 @@
                     <requiredEntity createDataKey="simple2"/>
                 </getData>
     
    -            <magentoCron stepKey="runCronReindex" groups="index"/>
    +            <comment userInput="BIC workaround" stepKey="runCronReindex"/>
    +
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="runCronIndex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </before>
             <after>
                 <deleteData createDataKey="simple1" stepKey="deleteSimple1" before="deleteSimple2"/>
    
  • app/code/Magento/Bundle/Test/Mftf/Test/AdvanceCatalogSearchBundleByShortDescriptionTest.xml+4 1 modified
    @@ -37,7 +37,10 @@
                     <requiredEntity createDataKey="simple2"/>
                 </createData>
     
    -            <magentoCron stepKey="runCronReindex" groups="index"/>
    +            <comment userInput="BIC workaround" stepKey="runCronReindex"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="runCronIndex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </before>
             <after>
                 <deleteData createDataKey="simple1" stepKey="deleteSimple1" before="deleteSimple2"/>
    
  • app/code/Magento/Bundle/Test/Mftf/Test/AdvanceCatalogSearchBundleBySkuTest.xml+4 1 modified
    @@ -36,7 +36,10 @@
                     <requiredEntity createDataKey="simple2"/>
                 </createData>
     
    -            <magentoCron stepKey="runCronReindex" groups="index"/>
    +            <comment userInput="BIC workaround" stepKey="runCronReindex"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="runCronIndex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </before>
             <after>
                 <deleteData createDataKey="simple1" stepKey="deleteSimple1" before="deleteSimple2"/>
    
  • app/code/Magento/Bundle/Test/Mftf/Test/AdvanceCatalogSearchBundleProductTest.xml+3 1 modified
    @@ -37,7 +37,9 @@
                     <requiredEntity createDataKey="simple2"/>
                 </createData>
     
    -            <magentoCron stepKey="runCronReindex" groups="index"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="runCronReindex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </before>
             <after>
                 <deleteData createDataKey="simple1" stepKey="deleteSimple1" before="deleteSimple2"/>
    
  • app/code/Magento/Bundle/Test/Mftf/Test/BundleProductFixedPricingTest.xml+3 1 modified
    @@ -23,7 +23,9 @@
                 <createData entity="_defaultCategory" stepKey="createPreReqCategory"/>
                 <createData entity="SimpleProduct2" stepKey="simpleProduct1"/>
                 <createData entity="SimpleProduct2" stepKey="simpleProduct2"/>
    -            <magentoCron stepKey="runCronIndex" groups="index"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="runCronIndex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
                 <!--Admin login-->
                 <actionGroup stepKey="loginToAdminPanel" ref="AdminLoginActionGroup"/>
             </before>
    
  • app/code/Magento/Bundle/Test/Mftf/Test/BundleProductWithTierPriceInCartTest.xml+3 1 modified
    @@ -20,7 +20,9 @@
             <before>
                 <createData entity="SimpleProduct2" stepKey="simpleProduct1"/>
                 <createData entity="SimpleProduct2" stepKey="simpleProduct2"/>
    -            <magentoCron stepKey="runCronIndex" groups="index"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="runCronIndex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
                 <actionGroup ref="AdminLoginActionGroup" stepKey="login"/>
             </before>
             <after>
    
  • app/code/Magento/Bundle/Test/Mftf/Test/BundleProductWithTierPriceWithFixedAndPercentOptionsInCartTest.xml+3 1 modified
    @@ -26,7 +26,9 @@
                 <createData entity="SimpleProduct2" stepKey="createProductForBundleItem2">
                     <field key="price">100.00</field>
                 </createData>
    -            <magentoCron groups="index" stepKey="runCronIndex"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="runCronIndex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
                 <actionGroup ref="AdminLoginActionGroup" stepKey="login"/>
             </before>
     
    
  • app/code/Magento/Bundle/Test/Mftf/Test/CurrencyChangingBundleProductInCartTest.xml+7 1 modified
    @@ -16,12 +16,15 @@
                     <severity value="BLOCKER"/>
                     <testCaseId value="MAGETWO-94467"/>
                     <group value="Bundle"/>
    +                <group value="cloud"/>
                 </annotations>
                 <before>
                     <actionGroup ref="AdminLoginActionGroup" stepKey="login"/>
                     <createData entity="SimpleProduct2" stepKey="simpleProduct1"/>
                     <createData entity="SimpleProduct2" stepKey="simpleProduct2"/>
    -                <magentoCron stepKey="runCronIndex" groups="index"/>
    +                <actionGroup ref="CliIndexerReindexActionGroup" stepKey="runCronIndex">
    +                    <argument name="indices" value=""/>
    +                </actionGroup>
                 </before>
                 <after>
                     <!-- Delete the bundled product -->
    @@ -76,6 +79,9 @@
                     <argument name="product" value="$$simpleProduct1$$"/>
                     <argument name="currency" value="EUR - Euro"/>
                 </actionGroup>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="runCronIndex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
                 <actionGroup ref="StoreFrontAddProductToCartFromBundleWithCurrencyActionGroup" stepKey="addProduct2ToCartAndChangeCurrencyToUSD">
                     <argument name="product" value="$$simpleProduct2$$"/>
                     <argument name="currency" value="USD - US Dollar"/>
    
  • app/code/Magento/Bundle/Test/Mftf/Test/EnableDisableBundleProductStatusTest.xml+3 1 modified
    @@ -23,7 +23,9 @@
                 <createData entity="_defaultCategory" stepKey="createPreReqCategory"/>
                 <createData entity="SimpleProduct2" stepKey="simpleProduct1"/>
                 <createData entity="SimpleProduct2" stepKey="simpleProduct2"/>
    -            <magentoCron stepKey="runCronIndex" groups="index"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="runCronIndex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
     
                 <!--Admin login-->
                 <actionGroup stepKey="loginToAdminPanel" ref="AdminLoginActionGroup"/>
    
  • app/code/Magento/Bundle/Test/Mftf/Test/MassEnableDisableBundleProductsTest.xml+6 2 modified
    @@ -24,7 +24,9 @@
                 <createData entity="SimpleProduct2" stepKey="simpleProduct2"/>
                 <createData entity="SimpleProduct2" stepKey="simpleProduct3"/>
                 <createData entity="SimpleProduct2" stepKey="simpleProduct4"/>
    -            <magentoCron stepKey="runCronIndex" groups="index"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="runCronIndex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </before>
             <after>
                 <!--Clear Filters-->
    @@ -141,7 +143,9 @@
             <click selector="{{AdminProductFiltersSection.disable}}" stepKey="ClickOnDisable"/>
             <waitForPageLoad stepKey="waitForPageloadToExecute"/>
     
    -        <magentoCron stepKey="runCronReindex" groups="index"/>
    +        <actionGroup ref="CliIndexerReindexActionGroup" stepKey="runCronReindex">
    +            <argument name="indices" value=""/>
    +        </actionGroup>
             <actionGroup ref="ClearPageCacheActionGroup" stepKey="clearing"/>
     
             <!--Confirm bundle products have been disabled-->
    
  • app/code/Magento/Bundle/Test/Mftf/Test/NewProductsListWidgetBundleProductTest.xml+4 2 modified
    @@ -23,7 +23,9 @@
             <before>
                 <createData entity="SimpleProduct2" stepKey="simpleProduct1"/>
                 <createData entity="SimpleProduct2" stepKey="simpleProduct2"/>
    -            <magentoCron stepKey="runCronIndex" groups="index"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="runCronIndex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </before>
     
             <after>
    @@ -77,7 +79,7 @@
                 <argument name="productIndex" value="1"/>
                 <argument name="qty" value="{{BundleProduct.defaultQuantity}}"/>
             </actionGroup>
    -        
    +
             <actionGroup ref="AdminProductFormSaveActionGroup" stepKey="clickSaveProduct"/>
     
             <!-- If PageCache is enabled, Cache clearing happens here, via merge -->
    
  • app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAddBundleOptionsToCartTest.xml+3 1 modified
    @@ -30,7 +30,9 @@
                 <createData entity="SimpleProduct2" stepKey="simpleProduct8"/>
                 <createData entity="SimpleProduct2" stepKey="simpleProduct9"/>
                 <createData entity="SimpleProduct2" stepKey="simpleProduct10"/>
    -            <magentoCron stepKey="runCronIndex" groups="index"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="runCronIndex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </before>
             <after>
                 <deleteData createDataKey="simpleProduct1" stepKey="deleteSimpleProduct1"/>
    
  • app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAddBundleProductWithZeroPriceToShoppingCartTest.xml+1 0 modified
    @@ -17,6 +17,7 @@
                 <severity value="CRITICAL"/>
                 <testCaseId value="MAGETWO-95167"/>
                 <group value="bundle"/>
    +            <group value="guest_checkout"/>
             </annotations>
             <before>
                 <!--Enable freeShipping-->
    
  • app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAdminEditDataTest.xml+3 1 modified
    @@ -22,7 +22,9 @@
                 <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/>
                 <createData entity="SimpleProduct2" stepKey="simpleProduct1"/>
                 <createData entity="SimpleProduct2" stepKey="simpleProduct2"/>
    -            <magentoCron stepKey="runCronIndex" groups="index"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="runCronIndex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </before>
             <after>
                 <actionGroup ref="AdminLogoutActionGroup" stepKey="amOnLogoutPage"/>
    
  • app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAdvanceCatalogSearchBundleBySkuWithHyphenTest.xml+3 1 modified
    @@ -37,7 +37,9 @@
                     <requiredEntity createDataKey="bundleOption"/>
                     <requiredEntity createDataKey="simple2"/>
                 </createData>
    -            <magentoCron stepKey="runCronReindex" groups="index"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="runCronReindex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </before>
             <after>
                 <deleteData createDataKey="simple1" before="deleteSimple2" stepKey="deleteSimple1"/>
    
  • app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleCartTest.xml+3 1 modified
    @@ -22,7 +22,9 @@
                 <actionGroup ref="AdminLoginActionGroup" stepKey="login"/>
                 <createData entity="SimpleProduct2" stepKey="simpleProduct1"/>
                 <createData entity="SimpleProduct2" stepKey="simpleProduct2"/>
    -            <magentoCron stepKey="runCronIndex" groups="index"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="runCronIndex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </before>
             <after>
                 <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/>
    
  • app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleCheckBoxOptionValidationTest.xml+3 1 modified
    @@ -38,7 +38,9 @@
                     <requiredEntity createDataKey="simpleProduct2"/>
                     <field key="qty">4</field>
                 </createData>
    -            <magentoCron stepKey="runCronIndex" groups="index"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="runCronIndex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </before>
             <after>
                 <deleteData createDataKey="bundleProduct" stepKey="deleteBundleProduct"/>
    
  • app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundlePlaceOrderWithMultipleOptionsSuccessTest.xml+1 0 modified
    @@ -28,6 +28,7 @@
                 <deleteData createDataKey="createPreReqCategory" stepKey="deletePreReqCategory"/>
                 <deleteData createDataKey="firstSimpleProduct" stepKey="deleteFirstSimpleProduct"/>
                 <deleteData createDataKey="secondSimpleProduct" stepKey="deleteSecondSimpleProduct"/>
    +            <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="logoutCustomer" />
                 <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/>
                 <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/>
             </after>
    
  • app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundlePlaceOrderWithVirtualAndSimpleChildrenTest.xml+1 0 modified
    @@ -55,6 +55,7 @@
                 <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProductForBundleItem"/>
                 <deleteData createDataKey="createVirtualProduct" stepKey="deleteVirtualProductForBundleItem"/>
                 <deleteData createDataKey="createFixedBundleProduct" stepKey="deleteBundleProduct"/>
    +            <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="logoutCustomer" />
                 <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/>
                 <actionGroup ref="AdminClearFiltersActionGroup" stepKey="clearProductsGridFilters"/>
                 <waitForPageLoad stepKey="waitForClearProductsGridFilters"/>
    
  • app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleProductDetailsTest.xml+3 1 modified
    @@ -23,7 +23,9 @@
                 <createData entity="_defaultCategory" stepKey="createPreReqCategory"/>
                 <createData entity="SimpleProduct2" stepKey="simpleProduct1"/>
                 <createData entity="SimpleProduct2" stepKey="simpleProduct2"/>
    -            <magentoCron stepKey="runCronIndex" groups="index"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="runCronIndex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
                 <!-- Admin Login-->
                 <actionGroup stepKey="loginToAdminPanel" ref="AdminLoginActionGroup"/>
                 <actionGroup ref="DisabledWYSIWYGActionGroup" stepKey="disableWYSIWYG"/>
    
  • app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleProductShownInCategoryListAndGridTest.xml+6 2 modified
    @@ -25,7 +25,9 @@
                 <createData entity="SimpleProduct2" stepKey="simpleProduct2"/>
                 <createData entity="SimpleProduct2" stepKey="simpleProduct3"/>
                 <createData entity="SimpleProduct2" stepKey="simpleProduct4"/>
    -            <magentoCron stepKey="runCronIndex" groups="index"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="runCronIndex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </before>
             <after>
                 <!--Logging out-->
    @@ -89,7 +91,9 @@
             <actionGroup ref="AdminProductFormSaveActionGroup" stepKey="clickSaveButton"/>
             <seeElement selector="{{AdminCategoryMessagesSection.SuccessMessage}}" stepKey="messageYouSavedTheProductIsShown"/>
     
    -        <magentoCron stepKey="runCronReindex" groups="index"/>
    +        <actionGroup ref="CliIndexerReindexActionGroup" stepKey="runCronReindex">
    +            <argument name="indices" value=""/>
    +        </actionGroup>
     
             <!--Go to category page-->
             <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="goToHomePage"/>
    
  • app/code/Magento/Bundle/Test/Mftf/Test/StorefrontCheckBundleProductOptionTierPricesTest.xml+3 1 modified
    @@ -48,7 +48,9 @@
                 </actionGroup>
                 <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutAsAdmin"/>
     
    -            <magentoCLI stepKey="runCronIndex" command="cron:run --group=index"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="runCronIndex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </before>
             <after>
                 <deleteData createDataKey="createBundleProductCreateBundleProduct" stepKey="deleteDynamicBundleProduct"/>
    
  • app/code/Magento/Bundle/Test/Mftf/Test/StorefrontCheckBundleProductTwoWebsiteDifferentPriceOptionTest.xml+6 2 modified
    @@ -34,7 +34,9 @@
                     <argument name="StoreGroup" value="customStoreGroup"/>
                     <argument name="customStore" value="customStore"/>
                 </actionGroup>
    -            <magentoCron groups="index" stepKey="reindex"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
     
                 <createData entity="SimpleProduct2" stepKey="simpleProduct"/>
                 <createData entity="ApiFixedBundleProduct" stepKey="createBundleProduct"/>
    @@ -58,7 +60,9 @@
                 </actionGroup>
                 <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/>
     
    -            <magentoCron groups="index" stepKey="reindex"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
                 <actionGroup ref="CliCacheCleanActionGroup" stepKey="cleanFullPageCache">
                     <argument name="tags" value="config full_page"/>
                 </actionGroup>
    
  • app/code/Magento/Bundle/Test/Mftf/Test/StorefrontCustomerSelectAndSetBundleOptionsTest.xml+7 4 modified
    @@ -22,8 +22,9 @@
                 <actionGroup ref="AdminLoginActionGroup" stepKey="login"/>
                 <createData entity="SimpleProduct2" stepKey="simpleProduct1"/>
                 <createData entity="SimpleProduct2" stepKey="simpleProduct2"/>
    -
    -            <magentoCron stepKey="runCronIndex" groups="index"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="runCronIndex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </before>
             <after>
                 <!-- Delete the bundled product -->
    @@ -92,8 +93,10 @@
             <!-- Save product and go to storefront -->
             <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/>
     
    -        <wait stepKey="waitBeforeIndexerAfterBundle" time="60"/>
    -        <magentoCLI command="cron:run --group=index" stepKey="runCronIndexerAfterBundle"/>
    +        <comment userInput="BIC workaround" stepKey="waitBeforeIndexerAfterBundle"/>
    +        <actionGroup ref="CliIndexerReindexActionGroup" stepKey="runCronIndexerAfterBundle">
    +            <argument name="indices" value=""/>
    +        </actionGroup>
     
             <amOnPage url="{{BundleProduct.sku}}.html" stepKey="goToStorefront"/>
             <waitForPageLoad stepKey="waitForStorefront"/>
    
  • app/code/Magento/Bundle/Test/Mftf/Test/StorefrontEditBundleProductTest.xml+6 2 modified
    @@ -22,7 +22,9 @@
                 <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/>
                 <createData entity="SimpleProduct2" stepKey="simpleProduct1"/>
                 <createData entity="SimpleProduct2" stepKey="simpleProduct2"/>
    -            <magentoCron stepKey="runCronIndex" groups="index"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="runCronIndex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </before>
             <after>
                 <actionGroup ref="AdminLogoutActionGroup" stepKey="amOnLogoutPage"/>
    @@ -72,7 +74,9 @@
             <click stepKey="saveProductBundle" selector="{{AdminProductFormActionSection.saveButton}}"/>
             <see stepKey="assertSuccess" selector="{{AdminProductMessagesSection.successMessage}}" userInput="You saved the product."/>
     
    -        <magentoCLI command="cron:run --group=index" stepKey="runCronIndexer"/>
    +        <actionGroup ref="CliIndexerReindexActionGroup" stepKey="runCronIndexer">
    +            <argument name="indices" value=""/>
    +        </actionGroup>
     
             <!-- Go to the storefront bundled product page -->
             <amOnPage url="/{{BundleProduct.urlKey}}.html" stepKey="visitStoreFrontBundle"/>
    
  • app/code/Magento/Bundle/Test/Mftf/Test/StorefrontGoToDetailsPageWhenAddingToCartTest.xml+3 1 modified
    @@ -23,7 +23,9 @@
                 <createData entity="SimpleProduct2" stepKey="simpleProduct1"/>
                 <createData entity="SimpleProduct2" stepKey="simpleProduct2"/>
                 <createData entity="_defaultCategory" stepKey="createCategory"/>
    -            <magentoCron stepKey="runCronIndex" groups="index"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="runCronIndex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </before>
             <after>
                 <deleteData createDataKey="simpleProduct1" stepKey="deleteSimpleProduct1"/>
    
  • app/code/Magento/Bundle/Test/Mftf/Test/StorefrontPlaceOrderBundleProductFixedPriceWithUpdatedPriceTest.xml+1 0 modified
    @@ -59,6 +59,7 @@
                 <deleteData createDataKey="createFirstProduct" stepKey="deleteSimpleProductForBundleItem"/>
                 <deleteData createDataKey="createSecondProduct" stepKey="deleteVirtualProductForBundleItem"/>
                 <deleteData createDataKey="createFixedBundleProduct" stepKey="deleteBundleProduct"/>
    +            <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="logoutCustomer" />
                 <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/>
                 <actionGroup ref="AdminClearFiltersActionGroup" stepKey="clearProductsGridFilters"/>
                 <waitForPageLoad stepKey="waitForClearProductsGridFilters"/>
    
  • app/code/Magento/Bundle/Test/Mftf/Test/StorefrontSortBundleProductsByPriceTest.xml+3 1 modified
    @@ -114,7 +114,9 @@
                 <deleteData createDataKey="createThirdBundleProduct" stepKey="deleteThirdBundleProduct"/>
     
                 <!-- Reindex invalidated indices after product attribute has been created/deleted -->
    -            <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindexInvalidatedIndices">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </after>
     
             <!-- Open created category on Storefront -->
    
  • app/code/Magento/Bundle/Test/Mftf/Test/StorefrontValidateQuantityBundleProductsTest.xml+7 3 modified
    @@ -21,7 +21,9 @@
                 <actionGroup ref="AdminLoginActionGroup" stepKey="login"/>
                 <createData entity="SimpleProduct2" stepKey="createProduct1"/>
                 <createData entity="SimpleProduct2" stepKey="createProduct2"/>
    -            <magentoCron stepKey="runCronIndex" groups="index"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="runCronIndex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </before>
             <after>
                 <!-- Delete the bundled product -->
    @@ -70,8 +72,10 @@
             <!-- Save product and go to storefront -->
             <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/>
     
    -        <wait stepKey="waitBeforeIndexerAfterBundle" time="60"/>
    -        <magentoCLI command="cron:run --group=index" stepKey="runCronIndexerAfterBundle"/>
    +        <comment userInput="BIC workaround" stepKey="waitBeforeIndexerAfterBundle"/>
    +        <actionGroup ref="CliIndexerReindexActionGroup" stepKey="runCronIndexerAfterBundle">
    +            <argument name="indices" value=""/>
    +        </actionGroup>
     
             <amOnPage url="{{BundleProduct.sku}}.html" stepKey="goToStorefront"/>
             <waitForPageLoad stepKey="waitForStorefront"/>
    
  • app/code/Magento/Bundle/view/frontend/templates/catalog/product/view/type/bundle/option/radio.phtml+28 22 modified
    @@ -4,6 +4,8 @@
      * See COPYING.txt for license details.
      */
     use Magento\Bundle\ViewModel\ValidateQuantity;
    +
    +// phpcs:disable Generic.Files.LineLength.TooLong
     ?>
     <?php /* @var $block \Magento\Bundle\Block\Catalog\Product\View\Type\Bundle\Option\Radio */ ?>
     <?php $_option = $block->getOption(); ?>
    @@ -20,42 +22,45 @@ $viewModel = $block->getData('validateQuantityViewModel');
         </label>
         <div class="control">
             <div class="nested options-list">
    -            <?php if ($block->showSingle()) : ?>
    +            <?php if ($block->showSingle()): ?>
                     <?= /* @noEscape */ $block->getSelectionTitlePrice($_selections[0]) ?>
                     <?= /* @noEscape */ $block->getTierPriceRenderer()->renderTierPrice($_selections[0]) ?>
                     <input type="hidden"
    -                    class="bundle-option-<?= (int)$_option->getId() ?>  product bundle option"
    -                    name="bundle_option[<?= (int)$_option->getId() ?>]"
    -                    value="<?= (int)$_selections[0]->getSelectionId() ?>"
    -                    id="bundle-option-<?= (int)$_option->getId() ?>-<?= (int)$_selections[0]->getSelectionId() ?>"
    -                    checked="checked"
    +                       class="bundle-option-<?= (int)$_option->getId() ?>  product bundle option"
    +                       name="bundle_option[<?= (int)$_option->getId() ?>]"
    +                       value="<?= (int)$_selections[0]->getSelectionId() ?>"
    +                       id="bundle-option-<?= (int)$_option->getId() ?>-<?= (int)$_selections[0]->getSelectionId() ?>"
    +                       checked="checked"
                     />
    -            <?php else :?>
    -                <?php if (!$_option->getRequired()) : ?>
    +            <?php else: ?>
    +                <?php if (!$_option->getRequired()): ?>
                         <div class="field choice">
                             <input type="radio"
                                    class="radio product bundle option"
                                    id="bundle-option-<?= $block->escapeHtmlAttr($_option->getId()) ?>"
                                    name="bundle_option[<?= $block->escapeHtmlAttr($_option->getId()) ?>]"
                                    data-selector="bundle_option[<?= $block->escapeHtmlAttr($_option->getId()) ?>]"
    -                               <?= ($_default && $_default->isSalable())?'':' checked="checked" ' ?>
    +                            <?= ($_default && $_default->isSalable())?'':' checked="checked" ' ?>
                                    value=""/>
                             <label class="label" for="bundle-option-<?= $block->escapeHtmlAttr($_option->getId()) ?>">
                                 <span><?= $block->escapeHtml(__('None')) ?></span>
                             </label>
                         </div>
                     <?php endif; ?>
    -                <?php foreach ($_selections as $_selection) : ?>
    +                <?php foreach ($_selections as $_selection): ?>
                         <div class="field choice">
                             <input type="radio"
                                    class="radio product bundle option change-container-classname"
                                    id="bundle-option-<?= $block->escapeHtmlAttr($_option->getId()) ?>-<?= $block->escapeHtmlAttr($_selection->getSelectionId()) ?>"
    -                               <?php if ($_option->getRequired()) { echo 'data-validate="{\'validate-one-required-by-name\':true}"'; }?>
    +                            <?php if ($_option->getRequired()) {
    +                                echo 'data-validate="{\'validate-one-required-by-name\':true}"';
    +                            } ?>
                                    name="bundle_option[<?= $block->escapeHtmlAttr($_option->getId()) ?>]"
                                    data-selector="bundle_option[<?= $block->escapeHtmlAttr($_option->getId()) ?>]"
    -                               <?php if ($block->isSelected($_selection)) { echo ' checked="checked"'; } ?>
    -                               <?php if (!$_selection->isSaleable()) { echo ' disabled="disabled"'; } ?>
    -                               value="<?= $block->escapeHtmlAttr($_selection->getSelectionId()) ?>"/>
    +                            <?php if ($block->isSelected($_selection)) { echo ' checked="checked"'; } ?>
    +                            <?php if (!$_selection->isSaleable()) { echo ' disabled="disabled"'; } ?>
    +                               value="<?= $block->escapeHtmlAttr($_selection->getSelectionId()) ?>"
    +                               data-errors-message-box="#validation-message-box-radio"/>
                             <label class="label"
                                    for="bundle-option-<?= $block->escapeHtmlAttr($_option->getId()) ?>-<?= $block->escapeHtmlAttr($_selection->getSelectionId()) ?>">
                                 <span><?= /* @noEscape */ $block->getSelectionTitlePrice($_selection) ?></span>
    @@ -65,21 +70,22 @@ $viewModel = $block->getData('validateQuantityViewModel');
                         </div>
                     <?php endforeach; ?>
                     <div id="bundle-option-<?= $block->escapeHtmlAttr($_option->getId()) ?>-container"></div>
    +                <div id="validation-message-box-radio"></div>
                 <?php endif; ?>
                 <div class="field qty qty-holder">
                     <label class="label" for="bundle-option-<?= $block->escapeHtmlAttr($_option->getId()) ?>-qty-input">
                         <span><?= $block->escapeHtml(__('Quantity')) ?></span>
                     </label>
                     <div class="control">
                         <input <?php if (!$_canChangeQty) { echo ' disabled="disabled"'; } ?>
    -                           id="bundle-option-<?= $block->escapeHtmlAttr($_option->getId()) ?>-qty-input"
    -                           class="input-text qty<?php if (!$_canChangeQty) { echo ' qty-disabled'; } ?>"
    -                           type="number"
    -                           min="0"
    -                           data-validate="<?= $block->escapeHtmlAttr($viewModel->getQuantityValidators()) ?>"
    -                           name="bundle_option_qty[<?= $block->escapeHtmlAttr($_option->getId()) ?>]"
    -                           data-selector="bundle_option_qty[<?= $block->escapeHtmlAttr($_option->getId()) ?>]"
    -                           value="<?= $block->escapeHtmlAttr($_defaultQty) ?>"/>
    +                        id="bundle-option-<?= $block->escapeHtmlAttr($_option->getId()) ?>-qty-input"
    +                        class="input-text qty<?php if (!$_canChangeQty) { echo ' qty-disabled'; } ?>"
    +                        type="number"
    +                        min="0"
    +                        data-validate="<?= $block->escapeHtmlAttr($viewModel->getQuantityValidators()) ?>"
    +                        name="bundle_option_qty[<?= $block->escapeHtmlAttr($_option->getId()) ?>]"
    +                        data-selector="bundle_option_qty[<?= $block->escapeHtmlAttr($_option->getId()) ?>]"
    +                        value="<?= $block->escapeHtmlAttr($_defaultQty) ?>"/>
                     </div>
                 </div>
             </div>
    
  • app/code/Magento/CacheInvalidate/composer.json+9 7 modified
    @@ -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.4",
         "require": {
             "php": "~8.1.0||~8.2.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 @@
             }
         }
     }
    +
    
  • app/code/Magento/Captcha/composer.json+14 12 modified
    @@ -1,26 +1,27 @@
     {
         "name": "magento/module-captcha",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.6-p4",
         "require": {
             "php": "~8.1.0||~8.2.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.12",
             "laminas/laminas-db": "^2.13.4"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -30,3 +31,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Captcha/Test/Mftf/Test/CaptchaFormsDisplayingTest/CaptchaWithDisabledGuestCheckoutTest.xml+4 2 modified
    @@ -5,7 +5,7 @@
       * See COPYING.txt for license details.
       */
     -->
    -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd">
         <test name="CaptchaWithDisabledGuestCheckoutTest">
             <annotations>
    @@ -25,7 +25,9 @@
                 <createData entity="ApiSimpleProduct" stepKey="createSimpleProduct">
                     <requiredEntity createDataKey="createCategory"/>
                 </createData>
    -            <magentoCron stepKey="runCronIndex" groups="index"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="runCronIndex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </before>
             <after>
                 <magentoCLI command="config:set checkout/options/guest_checkout 1" stepKey="enableGuestCheckout"/>
    
  • app/code/Magento/Captcha/Test/Mftf/Test/StorefrontCaptchaChangeCustomerPasswordTest.xml+1 0 modified
    @@ -39,6 +39,7 @@
                 <actionGroup ref="CliCacheCleanActionGroup" stepKey="cleanInvalidatedCaches">
                     <argument name="tags" value="config full_page"/>
                 </actionGroup>
    +            <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="logoutCustomer" />
                 <deleteData createDataKey="customer" stepKey="deleteCustomer"/>
             </after>
     
    
  • app/code/Magento/Captcha/Test/Mftf/Test/StorefrontCaptchaCheckoutWithEnabledCaptchaTest.xml+1 1 modified
    @@ -34,7 +34,7 @@
             <after>
                 <deleteData createDataKey="createCategory" stepKey="deleteCategory"/>
                 <deleteData createDataKey="createProduct" stepKey="deleteProduct"/>
    -            <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="logoutCustomer"/>
    +            <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="logoutCustomer" />
                 <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/>
                 <magentoCLI command="config:set {{StorefrontCustomerCaptchaDefaultLengthConfigData.path}} {{StorefrontCustomerCaptchaDefaultLengthConfigData.value}}" stepKey="setDefaultCaptchaLength"/>
                 <magentoCLI command="config:set {{StorefrontCustomerCaptchaDefaultSymbolsConfigData.path}} {{StorefrontCustomerCaptchaDefaultSymbolsConfigData.value}}" stepKey="setDefaultCaptchaSymbols"/>
    
  • app/code/Magento/Captcha/Test/Mftf/Test/StorefrontCaptchaEditCustomerEmailTest.xml+1 0 modified
    @@ -44,6 +44,7 @@
                     <argument name="tags" value="config full_page"/>
                 </actionGroup>
     
    +            <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="logoutCustomer" />
                 <deleteData createDataKey="customer" stepKey="deleteCustomer"/>
             </after>
     
    
  • app/code/Magento/Captcha/Test/Mftf/Test/StorefrontCaptchaLoginOnCheckoutWithEnabledCaptchaTest.xml+2 1 modified
    @@ -16,6 +16,7 @@
                 <description value="Customer should be able to login using Sign In link on checkout with enabled captcha"/>
                 <severity value="MAJOR"/>
                 <group value="captcha"/>
    +            <group value="guest_checkout"/>
             </annotations>
             <before>
                 <createData entity="SimpleSubCategory" stepKey="createCategory"/>
    @@ -34,7 +35,7 @@
             <after>
                 <deleteData createDataKey="createCategory" stepKey="deleteCategory"/>
                 <deleteData createDataKey="createProduct" stepKey="deleteProduct"/>
    -            <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="logoutCustomer"/>
    +            <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="logoutCustomer" />
                 <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/>
                 <magentoCLI command="config:set {{StorefrontCustomerCaptchaDefaultLengthConfigData.path}} {{StorefrontCustomerCaptchaDefaultLengthConfigData.value}}" stepKey="setDefaultCaptchaLength"/>
                 <magentoCLI command="config:set {{StorefrontCustomerCaptchaDefaultSymbolsConfigData.path}} {{StorefrontCustomerCaptchaDefaultSymbolsConfigData.value}}" stepKey="setDefaultCaptchaSymbols"/>
    
  • app/code/Magento/Captcha/Test/Mftf/Test/StorefrontCaptchaOnApplyingCouponCodesFormsTest.xml+1 1 modified
    @@ -42,7 +42,7 @@
                 <deleteData createDataKey="createCategory" stepKey="deleteCategory"/>
                 <deleteData createDataKey="createProduct" stepKey="deleteProduct"/>
                 <deleteData createDataKey="createCartPriceRule" stepKey="deleteCartPriceRule"/>
    -            <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="logoutCustomer"/>
    +            <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="logoutCustomer" />
                 <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/>
                 <magentoCLI command="config:set {{DisablePaymentBankTransferConfigData.path}} {{DisablePaymentBankTransferConfigData.value}}" stepKey="disableBankTransferPayment"/>
                 <magentoCLI command="config:set {{StorefrontCustomerCaptchaDefaultLengthConfigData.path}} {{StorefrontCustomerCaptchaDefaultLengthConfigData.value}}" stepKey="setDefaultCaptchaLength"/>
    
  • app/code/Magento/Captcha/Test/Mftf/Test/StorefrontCaptchaOnCustomerLoginTest.xml+1 0 modified
    @@ -37,6 +37,7 @@
                 <actionGroup ref="CliCacheCleanActionGroup" stepKey="cleanInvalidatedCaches">
                     <argument name="tags" value="config full_page"/>
                 </actionGroup>
    +            <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="logoutCustomer" />
                 <deleteData createDataKey="customer" stepKey="deleteCustomer"/>
             </after>
     
    
  • app/code/Magento/Captcha/Test/Mftf/Test/StorefrontCaptchaOnOnepageCheckoutPyamentTest.xml+2 0 modified
    @@ -21,6 +21,7 @@
                 <group value="storefront_captcha_enabled"/>
             </annotations>
             <before>
    +            <magentoCLI command="config:set checkout/options/enable_guest_checkout_login 1" stepKey="EnablingGuestCheckoutLogin"/>
                 <!-- Create Simple Product -->
                 <createData entity="SimpleProduct2" stepKey="createSimpleProduct">
                     <field key="price">20</field>
    @@ -62,6 +63,7 @@
     
                 <!-- Delete customer -->
                 <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/>
    +            <magentoCLI command="config:set checkout/options/enable_guest_checkout_login 0" stepKey="DisablingGuestCheckoutLogin"/>
             </after>
     
             <!-- Reindex and flush cache -->
    
  • app/code/Magento/CardinalCommerce/composer.json+11 9 modified
    @@ -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.4",
         "require": {
             "php": "~8.1.0||~8.2.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 @@
             }
         }
     }
    +
    
  • app/code/Magento/CatalogAnalytics/composer.json+8 6 modified
    @@ -1,17 +1,18 @@
     {
         "name": "magento/module-catalog-analytics",
         "description": "N/A",
    -    "require": {
    -        "php": "~8.1.0||~8.2.0",
    -        "magento/framework": "*",
    -        "magento/module-catalog": "*",
    -        "magento/module-analytics": "*"
    -    },
         "type": "magento2-module",
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "100.4.3",
    +    "require": {
    +        "php": "~8.1.0||~8.2.0",
    +        "magento/framework": "103.0.*",
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-analytics": "100.4.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -21,3 +22,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/CatalogCmsGraphQl/composer.json+12 10 modified
    @@ -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.2",
         "require": {
             "php": "~8.1.0||~8.2.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 @@
             }
         }
     }
    +
    
  • app/code/Magento/Catalog/composer.json+36 34 modified
    @@ -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.6-p4",
         "require": {
             "php": "~8.1.0||~8.2.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 @@
             }
         }
     }
    +
    
  • app/code/Magento/Catalog/Controller/Adminhtml/Product/NewAction.php+21 4 modified
    @@ -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');
             }
    
  • app/code/Magento/CatalogCustomerGraphQl/composer.json+9 7 modified
    @@ -2,17 +2,18 @@
         "name": "magento/module-catalog-customer-graph-ql",
         "description": "N/A",
         "type": "magento2-module",
    -    "require": {
    -        "php": "~8.1.0||~8.2.0",
    -        "magento/framework": "*",
    -        "magento/module-catalog": "*",
    -        "magento/module-customer": "*",
    -        "magento/module-catalog-graph-ql": "*"
    -    },
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "100.4.5",
    +    "require": {
    +        "php": "~8.1.0||~8.2.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 @@
             }
         }
     }
    +
    
  • app/code/Magento/CatalogGraphQl/composer.json+19 17 modified
    @@ -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.6",
         "require": {
             "php": "~8.1.0||~8.2.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 @@
             }
         }
     }
    +
    
  • app/code/Magento/Catalog/i18n/en_US.csv+1 0 modified
    @@ -819,4 +819,5 @@ Details,Details
     "Failed to retrieve product links for ""%1""","Failed to retrieve product links for ""%1"""
     "The linked product SKU is invalid. Verify the data and try again.","The linked product SKU is invalid. Verify the data and try again."
     "The linked products data is invalid. Verify the data and try again.","The linked products data is invalid. Verify the data and try again."
    +"The url has invalid characters. Please correct and try again.","The url has invalid characters. Please correct and try again."
     
    
  • app/code/Magento/CatalogImportExport/composer.json+18 16 modified
    @@ -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.6-p4",
         "require": {
             "php": "~8.1.0||~8.2.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 @@
             }
         }
     }
    +
    
  • app/code/Magento/CatalogImportExport/Model/Import/Product.php+8 1 modified
    @@ -48,6 +48,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';
     
    @@ -1624,6 +1625,12 @@ protected function _saveProducts()
                             // the bunch of products will pass for the event with url_key column.
                             $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];
                         if (null === $rowSku) {
                             $this->getErrorAggregator()->addRowToSkip($rowNum);
    @@ -1660,7 +1667,7 @@ protected function _saveProducts()
                             $prevAttributeSet,
                             $attributes
                         );
    -                // phpcs:ignore Magento2.CodeAnalysis.EmptyBlock.DetectedCatch
    +                    // phpcs:ignore Magento2.CodeAnalysis.EmptyBlock.DetectedCatch
                     } catch (Skip $skip) {
                         // Product is skipped.  Go on to the next one.
                     }
    
  • app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportGroupedProductWithSpecialPriceTest.xml+3 1 modified
    @@ -67,7 +67,9 @@
                 <helper class="\Magento\Catalog\Test\Mftf\Helper\LocalFileAssertions" method="deleteDirectory" stepKey="deleteExportFileDirectory">
                     <argument name="path">var/export</argument>
                 </helper>
    -            <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindexInvalidatedIndices">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
                 <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/>
             </after>
     
    
  • app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportImportConfigurableProductWithImagesTest.xml+7 3 modified
    @@ -126,10 +126,12 @@
                     <requiredEntity createDataKey="createConfigChildProduct"/>
                 </createData>
     
    -            <magentoCron stepKey="runCronIndex" groups="index"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="runCronIndex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
     
                 <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/>
    -    </before>
    +        </before>
             <after>
                 <!-- Remove downloadable domains -->
                 <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove example.com static.magento.com"/>
    @@ -150,7 +152,9 @@
                 <deleteData createDataKey="createCategory" stepKey="deleteCategory"/>
                 <actionGroup ref="AdminOpenProductIndexPageActionGroup" stepKey="navigateToProductIndex"/>
                 <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetProductGridColumnsInitial"/>
    -            <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindexInvalidatedIndices">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
                 <!-- Admin logout-->
                 <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/>
             </after>
    
  • app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleAndConfigurableProductsWithCustomOptionsTest.xml+6 2 modified
    @@ -81,7 +81,9 @@
                     <requiredEntity createDataKey="createConfigSecondChildProduct"/>
                 </createData>
     
    -            <magentoCron stepKey="runCronIndex" groups="index"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="runCronIndex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
                 <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/>
             </before>
     
    @@ -95,7 +97,9 @@
                 <helper class="\Magento\Catalog\Test\Mftf\Helper\LocalFileAssertions" method="deleteDirectory" stepKey="deleteExportFileDirectory">
                     <argument name="path">var/export</argument>
                 </helper>
    -            <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindexInvalidatedIndices">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
                 <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/>
             </after>
     
    
  • app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleProductAndConfigurableProductsWithAssignedImagesTest.xml+6 2 modified
    @@ -98,7 +98,9 @@
                     <requiredEntity createDataKey="createConfigProduct"/>
                 </createData>
     
    -            <magentoCron groups="index" stepKey="runCronIndex"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="runCronIndex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
                 <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/>
             </before>
     
    @@ -112,7 +114,9 @@
                 <helper class="\Magento\Catalog\Test\Mftf\Helper\LocalFileAssertions" method="deleteDirectory" stepKey="deleteExportFileDirectory">
                     <argument name="path">var/export</argument>
                 </helper>
    -            <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindexInvalidatedIndices">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
                 <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/>
             </after>
     
    
  • app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleProductAssignedToMainWebsiteAndConfigurableProductAssignedToCustomWebsiteTest.xml+6 2 modified
    @@ -71,7 +71,9 @@
                     <requiredEntity createDataKey="createConfigSecondChildProduct"/>
                 </createData>
     
    -            <magentoCron stepKey="runCronIndex" groups="index"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="runCronIndex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
                 <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/>
             </before>
             <after>
    @@ -85,7 +87,9 @@
                 <deleteData createDataKey="createConfigSecondChildProduct" stepKey="deleteConfigSecondChildProduct"/>
                 <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/>
                 <deleteData createDataKey="createCategory" stepKey="deleteCategory"/>
    -            <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindexInvalidatedIndices">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
                 <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/>
             </after>
     
    
  • app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleProductWithCustomAttributeTest.xml+7 3 modified
    @@ -29,16 +29,20 @@
                     <requiredEntity createDataKey="createAttributeSet"/>
                 </createData>
     
    -            <magentoCLI command="cron:run" arguments="--group index" stepKey="cronRun"/>
    -            <magentoCLI command="cron:run" arguments="--group index" stepKey="cronRunToStartReindex"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="cronRun">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
    +            <comment userInput="BIC workaround" stepKey="cronRunToStartReindex"/>
                 <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/>
             </before>
             <after>
                 <!-- Delete product creations -->
                 <deleteData createDataKey="createSimpleProductWithCustomAttributeSet" stepKey="deleteSimpleProductWithCustomAttributeSet"/>
                 <deleteData createDataKey="createAttributeSet" stepKey="deleteAttributeSet"/>
                 <deleteData createDataKey="createCategory" stepKey="deleteCategory"/>
    -            <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindexInvalidatedIndices">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
                 <!-- Log out -->
                 <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/>
             </after>
    
  • app/code/Magento/CatalogInventory/composer.json+15 13 modified
    @@ -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.6-p4",
         "require": {
             "php": "~8.1.0||~8.2.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-metapackage"
     }
    +
    
  • app/code/Magento/CatalogInventoryGraphQl/composer.json+10 8 modified
    @@ -2,18 +2,19 @@
         "name": "magento/module-catalog-inventory-graph-ql",
         "description": "N/A",
         "type": "magento2-module",
    -    "require": {
    -        "php": "~8.1.0||~8.2.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.3",
    +    "require": {
    +        "php": "~8.1.0||~8.2.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 @@
             }
         }
     }
    +
    
  • app/code/Magento/CatalogInventory/Test/Mftf/Test/AdminCreateProductWithZeroMaximumQtyAllowedInShoppingCartTest.xml+3 1 modified
    @@ -23,7 +23,9 @@
             <before>
                 <createData entity="DefaultValueForMaxSaleQty" stepKey="setDefaultValueForMaxSaleQty"/>
                 <createData entity="SimpleProduct2" stepKey="createdProduct"/>
    -            <magentoCron stepKey="runCronIndex" groups="index"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="runCronIndex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
                 <actionGroup ref="AdminLoginActionGroup" stepKey="login"/>
             </before>
             <after>
    
  • app/code/Magento/CatalogInventory/Test/Mftf/Test/AssociatedProductToConfigurableOutOfStockTest.xml+11 6 modified
    @@ -77,19 +77,24 @@
                 <createData entity="Simple_US_Customer" stepKey="createSimpleUsCustomer">
                     <field key="group_id">1</field>
                 </createData>
    -            <magentoCron stepKey="runCronIndex" groups="index"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="runCronIndex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </before>
     
             <after>
                 <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/>
                 <deleteData createDataKey="simplecategory" stepKey="deleteSimpleCategory"/>
    +            <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="logoutCustomer" />
                 <deleteData createDataKey="createSimpleUsCustomer" stepKey="deleteCustomer"/>
                 <deleteData createDataKey="createConfigChildProduct1" stepKey="deleteConfigChildProduct1"/>
                 <deleteData createDataKey="createConfigChildProduct2" stepKey="deleteConfigChildProduct2"/>
                 <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/>
     
                 <!-- Reindex invalidated indices after product attribute has been created/deleted -->
    -            <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindexInvalidatedIndices">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </after>
     
             <!-- Login as a customer -->
    @@ -131,10 +136,10 @@
             <click selector="{{AdminShipmentMainActionsSection.submitShipment}}" stepKey="submitShipment"/>
             <waitForPageLoad stepKey="waitShipmentCreated"/>
             <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/>
    -        <magentoCLI stepKey="runCron" command="cron:run --group='index'"/>
    -
    -        <!-- Wait till cron job runs for schedule updates -->
    -        <wait time="60" stepKey="waitForUpdateStarts"/>
    +        <actionGroup ref="CliIndexerReindexActionGroup" stepKey="runCron">
    +            <argument name="indices" value=""/>
    +        </actionGroup>
    +        <comment userInput="BIC workaround" stepKey="waitForUpdateStarts"/>
     
             <!-- Assert that product with single quantity is not available for order -->
             <amOnPage url="/{{ApiConfigurableProduct.urlKey}}2.html" stepKey="goToConfigProductPage2"/>
    
  • app/code/Magento/CatalogInventory/Test/Mftf/Test/DisabledInventoryCheckOnePageCheckoutTest.xml+1 0 modified
    @@ -16,6 +16,7 @@
                 <description value="Disabling Inventory and Doing Checkout For Out Of Stock Products"/>
                 <severity value="CRITICAL"/>
                 <testCaseId value="AC-5453"/>
    +            <group value="guest_checkout"/>
             </annotations>
     
             <before>
    
  • app/code/Magento/CatalogInventory/Test/Mftf/Test/StoreFrontAddOutOfStockProductToShoppingCartTest.xml+2 2 modified
    @@ -61,8 +61,8 @@
             <!-- Mouse Hover Product On Category Page-->
             <actionGroup ref="StorefrontHoverProductOnCategoryPageActionGroup" stepKey="hoverProduct"/>
             <!-- Select Add to cart-->
    -        <click selector="{{StorefrontCategoryMainSection.addToCartButtonProductInfoHover}}" stepKey="toCategory"/>
    -        <waitForPageLoad stepKey="wait"/>
    +        <click selector="{{StorefrontCategoryMainSection.addToCartProductBySku($$simpleProductOne.sku$$)}}" stepKey="toCategory"/>
    +        <waitForElementVisible selector="{{StorefrontProductPageSection.errorMsg}}" stepKey="wait"/>
             <!-- Assert the Error Message-->
             <see selector="{{StorefrontProductPageSection.errorMsg}}" userInput="Product that you are trying to add is not available." stepKey="seeErrorMessage"/>
         </test>
    
  • app/code/Magento/CatalogInventory/Test/Mftf/Test/StorefrontSelectionOfOutOfStockChildProductsOfConfigurableProductDisabledTest.xml+6 2 modified
    @@ -66,7 +66,9 @@
                     <requiredEntity createDataKey="createConfigProduct"/>
                     <requiredEntity createDataKey="createConfigChildProduct2"/>
                 </createData>
    -            <magentoCron groups="index" stepKey="reindex"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </before>
     
             <after>
    @@ -76,7 +78,9 @@
                 <deleteData createDataKey="createConfigChildProduct1" stepKey="deleteConfigChildProduct1"/>
                 <deleteData createDataKey="createConfigChildProduct2" stepKey="deleteConfigChildProduct2"/>
                 <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/>
    -            <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindexInvalidatedIndices">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </after>
             <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin1"/>
             <amOnPage url="{{AdminProductEditPage.url($$createConfigChildProduct1.id$$)}}" stepKey="openProductEditPageToSetStatus"/>
    
  • app/code/Magento/CatalogRule/composer.json+17 15 modified
    @@ -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.6-p4",
         "require": {
             "php": "~8.1.0||~8.2.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 @@
             }
         }
     }
    +
    
  • app/code/Magento/CatalogRuleConfigurable/composer.json+12 10 modified
    @@ -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.5-p4",
         "require": {
             "php": "~8.1.0||~8.2.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 @@
             }
         }
     }
    +
    
  • app/code/Magento/CatalogRuleConfigurable/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithAssignedSimpleProducts2Test.xml+0 1 modified
    @@ -163,7 +163,6 @@
                 <!-- Customer log out -->
                 <!-- Must logout before delete customer otherwise magento fails during logout -->
                 <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="logoutFromStorefront"/>
    -
                 <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/>
                 <deleteData createDataKey="customerGroup" stepKey="deleteCustomerGroup"/>
     
    
  • app/code/Magento/CatalogRuleConfigurable/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithOptions2Test.xml+3 1 modified
    @@ -116,7 +116,9 @@
                 <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/>
     
                 <!-- Reindex invalidated indices after product attribute has been created/deleted -->
    -            <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindexInvalidatedIndices">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </after>
     
             <!-- Create price rule for first configurable product option -->
    
  • app/code/Magento/CatalogRuleGraphQl/composer.json+8 6 modified
    @@ -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.3",
         "require": {
             "php": "~8.1.0||~8.2.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 @@
             }
         }
     }
    +
    
  • app/code/Magento/CatalogRule/Test/Mftf/Test/AdminApplyCatalogPriceRuleByProductAttributeTest.xml+6 2 modified
    @@ -123,7 +123,9 @@
                               userInput="$createProductAttributeOptionGreen.option[store_labels][0][label]$" stepKey="setAttributeValueForSecondChildProduct"/>
                 <actionGroup ref="SaveProductFormActionGroup" stepKey="saveSecondChildProduct"/>
                 <actionGroup ref="AdminCatalogPriceRuleDeleteAllActionGroup" stepKey="deleteAllCatalogPriceRule"/>
    -            <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindexInvalidatedIndices">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </before>
             <after>
                 <!-- Delete created data -->
    @@ -139,7 +141,9 @@
                 <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="resetCatalogRulesGridFilter"/>
                 <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/>
                 <!-- Reindex invalidated indices after product attribute has been created/deleted -->
    -            <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindexInvalidatedIndices">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </after>
             <!-- Create Catalog Price Rule -->
             <actionGroup ref="AdminOpenNewCatalogPriceRuleFormPageActionGroup" stepKey="startCreatingFirstPriceRule"/>
    
  • app/code/Magento/CatalogRule/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithSpecialPricesTest.xml+6 2 modified
    @@ -74,7 +74,9 @@
                     <requiredEntity createDataKey="createConfigProduct"/>
                     <requiredEntity createDataKey="createSecondConfigChildProduct"/>
                 </createData>
    -            <magentoCron stepKey="runCronIndex" groups="index"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="runCronIndex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </before>
             <after>
                 <!-- Delete the catalog price rule -->
    @@ -95,7 +97,9 @@
                 <deleteData createDataKey="createCategory" stepKey="deleteCategory"/>
     
                 <!-- Reindex invalidated indices after product attribute has been created/deleted -->
    -            <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindexInvalidatedIndices">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </after>
     
             <!-- Add special prices for products -->
    
  • app/code/Magento/CatalogRule/Test/Mftf/Test/AdminApplyCatalogRuleForDownloadableProductTest.xml+6 2 modified
    @@ -77,7 +77,9 @@
     
              <!-- Delete the catalog price rule -->
              <actionGroup ref="AdminCatalogPriceRuleDeleteAllActionGroup" stepKey="deleteAllCatalogRulesAfterTest"/>
    -         <magentoCron groups="index" stepKey="fixInvalidatedIndicesAfterTest"/>
    +         <actionGroup ref="CliIndexerReindexActionGroup" stepKey="fixInvalidatedIndicesAfterTest">
    +             <argument name="indices" value=""/>
    +         </actionGroup>
     
              <!-- Log out -->
              <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/>
    @@ -101,7 +103,9 @@
             <actionGroup ref="AdminCatalogPriceRuleSaveAndApplyActionGroup" stepKey="saveAndApplyCatalogPriceRule"/>
     
             <!-- Reindex and flush cache -->
    -        <magentoCron groups="index" stepKey="reindex"/>
    +        <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +            <argument name="indices" value=""/>
    +        </actionGroup>
             <actionGroup ref="CliCacheCleanActionGroup" stepKey="flushCache">
                 <argument name="tags" value="full_page"/>
             </actionGroup>
    
  • app/code/Magento/CatalogRule/Test/Mftf/Test/AdminApplyCatalogRuleForFixedBundleProductWithCustomOptionsTest.xml+6 2 modified
    @@ -71,7 +71,9 @@
              <!-- Save Catalog rule -->
              <actionGroup ref="AdminCatalogPriceRuleSaveAndApplyActionGroup" stepKey="saveAndApplyCatalogPriceRule"/>
              <!-- Reindex and flush cache -->
    -         <magentoCron groups="index" stepKey="reindex"/>
    +         <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +             <argument name="indices" value=""/>
    +         </actionGroup>
              <actionGroup ref="CliCacheCleanActionGroup" stepKey="flushCache">
                  <argument name="tags" value="full_page"/>
              </actionGroup>
    @@ -83,7 +85,9 @@
              </actionGroup>
              <!-- Delete the catalog price rule -->
              <actionGroup ref="AdminCatalogPriceRuleDeleteAllActionGroup" stepKey="deleteAllCatalogRulesAfterTest"/>
    -         <magentoCron groups="index" stepKey="fixInvalidatedIndicesAfterTest"/>
    +         <actionGroup ref="CliIndexerReindexActionGroup" stepKey="fixInvalidatedIndicesAfterTest">
    +             <argument name="indices" value=""/>
    +         </actionGroup>
              <!-- deleting category, simple products -->
              <deleteData createDataKey="simpleProduct" stepKey="deleteSimpleProduct"/>
              <deleteData createDataKey="createCategory" stepKey="deleteCategory"/>
    
  • app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest/AdminCreateCatalogPriceRuleForCustomerGroupTest.xml+4 1 modified
    @@ -35,12 +35,15 @@
                 <deleteData createDataKey="createProduct" stepKey="deleteProduct"/>
                 <deleteData createDataKey="createCategory" stepKey="deleteCategory"/>
                 <actionGroup ref="AdminCatalogPriceRuleDeleteAllActionGroup" stepKey="deleteAllCatalogPriceRule"/>
    +            <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="logoutCustomer" />
                 <actionGroup ref="DeleteCustomerByEmailActionGroup" stepKey="deleteCustomer">
                     <argument name="email" value="{{CustomerEntityOne.email}}"/>
                 </actionGroup>
                 <actionGroup ref="ResetAdminDataGridToDefaultViewActionGroup" stepKey="resetGrid"/>
                 <actionGroup ref="AdminLogoutActionGroup" stepKey="amOnLogoutPage"/>
    -            <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindexInvalidatedIndices">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </after>
     
             <!-- Create a catalog rule for the NOT LOGGED IN customer group -->
    
  • app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest/AdminCreateCatalogPriceRuleTest.xml+6 2 modified
    @@ -35,7 +35,9 @@
                     <field key="price">100.00</field>
                 </createData>
     
    -            <magentoCron groups="index" stepKey="fixInvalidatedIndicesBeforeTest"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="fixInvalidatedIndicesBeforeTest">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </before>
             <after>
                 <!-- Delete products and category -->
    @@ -49,7 +51,9 @@
                 <actionGroup ref="AdminCatalogPriceRuleDeleteAllActionGroup" stepKey="deleteAllCatalogPriceRule"/>
                 <!-- Logout -->
                 <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/>
    -            <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindexInvalidatedIndices">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </after>
     
             <!-- Goto Marketing > Catalog Price Rule -->
    
  • app/code/Magento/CatalogRule/Test/Mftf/Test/AdminDeleteCatalogPriceRuleEntityTest/AdminDeleteCatalogPriceRuleEntityFromConfigurableProductTest.xml+4 1 modified
    @@ -92,6 +92,7 @@
             <after>
                 <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutOfAdmin1"/>
     
    +            <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="logoutCustomer" />
                 <deleteData createDataKey="createCustomer1" stepKey="deleteCustomer"/>
                 <deleteData createDataKey="createCategory1" stepKey="deleteCategory1"/>
                 <deleteData createDataKey="createConfigProduct1" stepKey="deleteConfigProduct1"/>
    @@ -100,7 +101,9 @@
                 <deleteData createDataKey="createConfigProductAttribute1" stepKey="deleteConfigProductAttribute1"/>
     
                 <!-- Reindex invalidated indices after product attribute has been created/deleted -->
    -            <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindexInvalidatedIndices">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </after>
     
             <!-- Delete the simple product and catalog price rule -->
    
  • app/code/Magento/CatalogRule/Test/Mftf/Test/AdminDeleteCatalogPriceRuleEntityTest/AdminDeleteCatalogPriceRuleEntityFromSimpleProductTest.xml+1 0 modified
    @@ -16,6 +16,7 @@
                 <severity value="CRITICAL"/>
                 <group value="CatalogRule"/>
                 <group value="mtf_migrated"/>
    +            <group value="guest_checkout"/>
             </annotations>
     
             <before>
    
  • app/code/Magento/CatalogRule/Test/Mftf/Test/AdminDeleteCatalogPriceRuleTest.xml+3 1 modified
    @@ -23,7 +23,9 @@
                 <createData entity="ApiSimpleProduct" stepKey="createSimpleProduct">
                     <requiredEntity createDataKey="createCategory"/>
                 </createData>
    -            <magentoCron stepKey="runCronIndex" groups="index"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="runCronIndex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
                 <!-- Login to Admin page -->
                 <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/>
                 <!-- Create a configurable product -->
    
  • app/code/Magento/CatalogRule/Test/Mftf/Test/AdminEnableAttributeIsUndefinedCatalogPriceRuleTest.xml+6 2 modified
    @@ -44,7 +44,9 @@
                 <createData entity="productDropDownAttribute" stepKey="createSecondProductAttribute">
                     <field key="scope">website</field>
                 </createData>
    -            <magentoCLI command="cron:run --group=index" stepKey="runCron"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="runCron">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </before>
             <after>
     
    @@ -64,7 +66,9 @@
                 <deleteData createDataKey="createSecondProductAttribute" stepKey="deleteSecondProductAttribute"/>
     
                 <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/>
    -            <magentoCLI command="cron:run --group=index" stepKey="runCron"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="runCron">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </after>
     
             <!--Create catalog price rule-->
    
  • app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogPriceRuleByProductAttributeTest.xml+6 2 modified
    @@ -84,7 +84,9 @@
                     <requiredEntity createDataKey="createConfigProduct"/>
                     <requiredEntity createDataKey="createConfigChildProduct2"/>
                 </createData>
    -            <magentoCron stepKey="runCronIndex" groups="index"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="runCronIndex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </before>
             <after>
     
    @@ -108,7 +110,9 @@
                 <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/>
     
                 <!-- Reindex invalidated indices after product attribute has been created/deleted -->
    -            <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindexInvalidatedIndices">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </after>
     
             <!-- Add values to your attribute ( ex: red , green) -->
    
  • app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogRuleForSimpleAndConfigurableProductTest.xml+6 2 modified
    @@ -77,7 +77,9 @@
                     <requiredEntity createDataKey="createConfigProduct"/>
                     <requiredEntity createDataKey="createConfigChildProduct2"/>
                 </createData>
    -            <magentoCron stepKey="runCronIndex" groups="index"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="runCronIndex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </before>
             <after>
                 <!-- Delete the catalog price rule -->
    @@ -99,7 +101,9 @@
                 <deleteData createDataKey="createCategory" stepKey="deleteApiCategory"/>
     
                 <!-- Reindex invalidated indices after product attribute has been created/deleted -->
    -            <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindexInvalidatedIndices">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </after>
             <!-- Begin creating a new catalog price rule -->
             <actionGroup ref="NewCatalogPriceRuleByUIWithConditionIsCategoryActionGroup" stepKey="newCatalogPriceRuleByUIWithConditionIsCategory">
    
  • app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogRuleForSimpleProductAndFixedMethodTest.xml+3 1 modified
    @@ -37,7 +37,9 @@
     
                 <!-- Update all products to have custom options -->
                 <updateData createDataKey="createProduct1" entity="productWithFixedOptions" stepKey="updateProductWithOptions1"/>
    -            <magentoCron stepKey="runCronIndex" groups="index"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="runCronIndex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </before>
             <after>
                 <!-- Delete products and category -->
    
  • app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogRuleForSimpleProductForNewCustomerGroupTest.xml+3 1 modified
    @@ -32,7 +32,9 @@
                     <requiredEntity createDataKey="createCategory"/>
                     <field key="price">56.78</field>
                 </createData>
    -            <magentoCron stepKey="runCronIndex" groups="index"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="runCronIndex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </before>
             <after>
                 <!-- Delete products and category -->
    
  • app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogRuleForSimpleProductWithCustomOptionsTest.xml+3 1 modified
    @@ -43,7 +43,9 @@
                 <updateData createDataKey="createProduct1" entity="productWithCustomOptions" stepKey="updateProductWithOptions1"/>
                 <updateData createDataKey="createProduct2" entity="productWithCustomOptions" stepKey="updateProductWithOptions2"/>
                 <updateData createDataKey="createProduct3" entity="productWithCustomOptions" stepKey="updateProductWithOptions3"/>
    -            <magentoCron stepKey="runCronIndex" groups="index"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="runCronIndex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </before>
             <after>
                 <!-- Delete products and category -->
    
  • app/code/Magento/CatalogRule/Test/Mftf/Test/CatalogPriceRuleAndCustomerGroupMembershipArePersistedUnderLongTermCookieTest.xml+1 0 modified
    @@ -47,6 +47,7 @@
                 <createData entity="PersistentLogoutClearEnabled" stepKey="persistentLogoutClearEnabled"/>
                 <deleteData createDataKey="createCategory" stepKey="deleteCategory"/>
                 <deleteData createDataKey="createProduct" stepKey="deleteProduct"/>
    +            <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="logoutCustomer" />
                 <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/>
                 <!-- Delete the rule -->
                 <actionGroup ref="RemoveCatalogPriceRuleActionGroup" stepKey="deleteCatalogPriceRule">
    
  • app/code/Magento/CatalogRule/Test/Mftf/Test/StorefrontApplyCatalogRuleForSimpleProductsWithCustomOptionsMultiCurrencyStoreTest.xml+6 2 modified
    @@ -71,7 +71,9 @@
     
                 <!-- Clear all catalog price rules before test -->
                 <actionGroup ref="AdminCatalogPriceRuleDeleteAllActionGroup" stepKey="deleteAllCatalogRulesBeforeTest"/>
    -            <magentoCron groups="index" stepKey="fixInvalidatedIndicesBeforeTest"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="fixInvalidatedIndicesBeforeTest">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </before>
             <after>
                 <magentoCLI command="config:set {{StorefrontDisableAddStoreCodeToUrls.path}} {{StorefrontDisableAddStoreCodeToUrls.value}}" stepKey="addStoreCodeToUrlDisable"/>
    @@ -95,7 +97,9 @@
     
                 <!-- Delete the catalog price rule -->
                 <actionGroup ref="AdminCatalogPriceRuleDeleteAllActionGroup" stepKey="deleteAllCatalogRulesAfterTest"/>
    -            <magentoCron groups="index" stepKey="fixInvalidatedIndicesAfterTest"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="fixInvalidatedIndicesAfterTest">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
     
                 <!-- Logout -->
                 <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/>
    
  • app/code/Magento/CatalogRule/Test/Mftf/Test/StorefrontApplyCatalogRuleForSimpleProductsWithCustomOptionsTest.xml+7 2 modified
    @@ -18,6 +18,7 @@
                 <group value="catalogRule"/>
                 <group value="mtf_migrated"/>
                 <group value="catalog"/>
    +            <group value="guest_checkout"/>
             </annotations>
             <before>
                 <createData entity="_defaultCategory" stepKey="createCategory"/>
    @@ -44,7 +45,9 @@
     
                 <!-- Clear all catalog price rules before test -->
                 <actionGroup ref="AdminCatalogPriceRuleDeleteAllActionGroup" stepKey="deleteAllCatalogRulesBeforeTest"/>
    -            <magentoCron groups="index" stepKey="fixInvalidatedIndicesBeforeTest"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="fixInvalidatedIndicesBeforeTest">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </before>
             <after>
                 <!-- Delete products and category -->
    @@ -55,7 +58,9 @@
     
                 <!-- Delete the catalog price rule -->
                 <actionGroup ref="AdminCatalogPriceRuleDeleteAllActionGroup" stepKey="deleteAllCatalogRulesAfterTest"/>
    -            <magentoCron groups="index" stepKey="fixInvalidatedIndicesAfterTest"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="fixInvalidatedIndicesAfterTest">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
     
                 <!-- Logout -->
                 <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/>
    
  • app/code/Magento/CatalogRule/Test/Mftf/Test/StorefrontApplyCatalogRuleForSimpleProductWithSelectFixedMethodTest.xml+6 2 modified
    @@ -37,7 +37,9 @@
     
                 <!-- Clear all catalog price rules and reindex before test -->
                 <actionGroup ref="AdminCatalogPriceRuleDeleteAllActionGroup" stepKey="deleteAllCatalogRulesBeforeTest"/>
    -            <magentoCron groups="index" stepKey="fixInvalidatedIndicesBeforeTest"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="fixInvalidatedIndicesBeforeTest">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </before>
             <after>
                 <!-- Delete products and category -->
    @@ -46,7 +48,9 @@
     
                 <!-- Delete the catalog price rule -->
                 <actionGroup ref="AdminCatalogPriceRuleDeleteAllActionGroup" stepKey="deleteAllCatalogRulesAfterTest"/>
    -            <magentoCron groups="index" stepKey="fixInvalidatedIndicesAfter"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="fixInvalidatedIndicesAfter">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
                 <!-- Logout -->
                 <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/>
             </after>
    
  • app/code/Magento/CatalogRule/Test/Mftf/Test/StorefrontApplyCatalogRuleToSimpleProductNotCustomOptionsTest.xml+6 2 modified
    @@ -37,7 +37,9 @@
     
                 <!-- Clear all catalog price rules and reindex before test -->
                 <actionGroup ref="AdminCatalogPriceRuleDeleteAllActionGroup" stepKey="deleteAllCatalogRulesBeforeTest"/>
    -            <magentoCron groups="index" stepKey="fixInvalidatedIndicesBeforeTest"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="fixInvalidatedIndicesBeforeTest">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </before>
             <after>
                 <!-- Delete products and category -->
    @@ -46,7 +48,9 @@
     
                 <!-- Delete the catalog price rule -->
                 <actionGroup ref="AdminCatalogPriceRuleDeleteAllActionGroup" stepKey="deleteAllCatalogRulesAfterTest"/>
    -            <magentoCron groups="index" stepKey="fixInvalidatedIndicesAfter"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="fixInvalidatedIndicesAfter">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
                 <!-- Logout -->
                 <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/>
             </after>
    
  • app/code/Magento/CatalogSearch/composer.json+20 18 modified
    @@ -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.6-p4",
         "require": {
             "php": "~8.1.0||~8.2.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 @@
             }
         }
     }
    +
    
  • app/code/Magento/CatalogSearch/Test/Mftf/Test/MinimalQueryLengthForCatalogSearchTest.xml+3 1 modified
    @@ -27,7 +27,9 @@
                     <requiredEntity createDataKey="createCategory"/>
                 </createData>
                 <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/>
    -            <magentoCron groups="index" stepKey="reindex"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </before>
             <after>
                 <deleteData createDataKey="createCategory" stepKey="deleteCategory"/>
    
  • app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchAndAddToCartBundleDynamicTest.xml+3 1 modified
    @@ -43,7 +43,9 @@
                 </actionGroup>
                 <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/>
     
    -            <magentoCron groups="index" stepKey="reindex"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
                 <comment userInput="Adding the comment to replace CliCacheFlushActionGroup action group ('cache:flush' command) for preserving Backward Compatibility" stepKey="flushCache"/>
             </before>
             <after>
    
  • app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchAndAddToCartBundleFixedTest.xml+3 1 modified
    @@ -54,7 +54,9 @@
                 </actionGroup>
                 <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/>
     
    -            <magentoCron groups="index" stepKey="reindex"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
                 <comment userInput="Adding the comment to replace CliCacheFlushActionGroup action group ('cache:flush' command) for preserving Backward Compatibility" stepKey="flushCache"/>
             </before>
             <after>
    
  • app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchAndAddToCartDownloadableTest.xml+3 1 modified
    @@ -27,7 +27,9 @@
                     <requiredEntity createDataKey="createProduct"/>
                 </createData>
     
    -            <magentoCron groups="index" stepKey="reindex"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
                 <comment userInput="Adding the comment to replace CliCacheFlushActionGroup action group ('cache:flush' command) for preserving Backward Compatibility" stepKey="flushCache"/>
             </before>
             <after>
    
  • app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchAndAddToCartGroupedTest.xml+3 1 modified
    @@ -27,7 +27,9 @@
                     <requiredEntity createDataKey="simple1"/>
                 </createData>
     
    -            <magentoCron groups="index" stepKey="reindex"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
                 <comment userInput="Adding the comment to replace CliCacheFlushActionGroup action group ('cache:flush' command) for preserving Backward Compatibility" stepKey="flushCache"/>
             </before>
             <after>
    
  • app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchAndAddToCartTest.xml+3 1 modified
    @@ -23,7 +23,9 @@
                     <requiredEntity createDataKey="createCategory"/>
                 </createData>
     
    -            <magentoCron groups="index" stepKey="reindex"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
                 <comment userInput="Adding the comment to replace CliCacheFlushActionGroup action group ('cache:flush' command) for preserving Backward Compatibility" stepKey="flushCache"/>
             </before>
             <after>
    
  • app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchAndAddToCartVirtualTest.xml+3 1 modified
    @@ -23,7 +23,9 @@
                     <requiredEntity createDataKey="createCategory"/>
                 </createData>
     
    -            <magentoCron groups="index" stepKey="reindex"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
                 <comment userInput="Adding the comment to replace CliCacheFlushActionGroup action group ('cache:flush' command) for preserving Backward Compatibility" stepKey="flushCache"/>
             </before>
             <after>
    
  • app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchEmptyResultsTest.xml+3 1 modified
    @@ -25,7 +25,9 @@
                     <requiredEntity createDataKey="createCategory"/>
                 </createData>
     
    -            <magentoCron groups="index" stepKey="reindex"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
                 <comment userInput="Adding the comment to replace CliCacheFlushActionGroup action group ('cache:flush' command) for preserving Backward Compatibility" stepKey="flushCache"/>
             </before>
             <after>
    
  • app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchProductBySkuTest.xml+3 1 modified
    @@ -23,7 +23,9 @@
                     <requiredEntity createDataKey="createCategory"/>
                 </createData>
     
    -            <magentoCron groups="index" stepKey="reindex"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
                 <comment userInput="Adding the comment to replace CliCacheFlushActionGroup action group ('cache:flush' command) for preserving Backward Compatibility" stepKey="flushCache"/>
             </before>
             <after>
    
  • app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest/QuickSearchWithTwoCharsEmptyResultsTest.xml+3 1 modified
    @@ -21,7 +21,9 @@
     
             <before>
                 <magentoCLI command="config:set {{MinimalQueryLengthFourConfigData.path}} {{MinimalQueryLengthFourConfigData.value}}" after="createSimpleProduct" stepKey="setMinimalQueryLengthToFour"/>
    -            <magentoCLI command="indexer:reindex" stepKey="reindex"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
                 <magentoCLI command="cache:flush" stepKey="flushCache"/>
             </before>
     
    
  • app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontPartialWordQuickSearchStemmingTest.xml+3 1 modified
    @@ -53,7 +53,9 @@
                     <field key="sku">5127AB-BRASS</field>
                     <requiredEntity createDataKey="category1"/>
                 </createData>
    -            <magentoCron groups="index" stepKey="reindex"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </before>
             <after>
                 <!--Delete category-->
    
  • app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontPartialWordQuickSearchUsingElasticSearchTest.xml+3 1 modified
    @@ -31,7 +31,9 @@
                 <createData entity="ApiSimpleProductWithNoSpace" stepKey="product3">
                     <requiredEntity createDataKey="newCategory"/>
                 </createData>
    -            <magentoCron groups="index" stepKey="reindex"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </before>
             <after>
                 <deleteData createDataKey="product1" stepKey="deleteProduct1"/>
    
  • app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontUpdateSearchTermEntityTest.xml+3 1 modified
    @@ -25,7 +25,9 @@
                     <requiredEntity createDataKey="createCategory1"/>
                 </createData>
     
    -            <magentoCron groups="index" stepKey="reindex"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
                 <comment userInput="Adding the comment to replace CliCacheFlushActionGroup action group ('cache:flush' command) for preserving Backward Compatibility" stepKey="flushCache"/>
                 <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="amOnStorefrontPage1"/>
             </before>
    
  • app/code/Magento/Catalog/Test/Mftf/ActionGroup/AddCrossSellProductBySkuActionGroup.xml+5 0 modified
    @@ -19,13 +19,18 @@
             <!--Scroll up to avoid error-->
             <scrollTo selector="{{AdminProductFormRelatedUpSellCrossSellSection.relatedDropdown}}" x="0" y="-100" stepKey="scrollTo"/>
             <conditionalClick selector="{{AdminProductFormRelatedUpSellCrossSellSection.relatedDropdown}}" dependentSelector="{{AdminProductFormRelatedUpSellCrossSellSection.relatedDependent}}" visible="false" stepKey="openDropDownIfClosedRelatedUpSellCrossSell"/>
    +        <waitForElementClickable selector="{{AdminProductFormRelatedUpSellCrossSellSection.AddCrossSellProductsButton}}" stepKey="waitForAddCrossSellButtonClickable" />
             <click selector="{{AdminProductFormRelatedUpSellCrossSellSection.AddCrossSellProductsButton}}" stepKey="clickAddCrossSellButton"/>
             <conditionalClick selector="{{AdminProductCrossSellModalSection.Modal}} {{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductCrossSellModalSection.Modal}} {{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFilters"/>
    +        <waitForElementClickable selector="{{AdminProductCrossSellModalSection.Modal}} {{AdminProductGridFilterSection.filters}}" stepKey="waitForProductFiltersClickable" />
             <click selector="{{AdminProductCrossSellModalSection.Modal}} {{AdminProductGridFilterSection.filters}}" stepKey="openProductFilters"/>
    +        <waitForElementVisible selector="{{AdminProductCrossSellModalSection.Modal}} {{AdminProductGridFilterSection.skuFilter}}" stepKey="waitForSkuFilterVisible" />
             <fillField selector="{{AdminProductCrossSellModalSection.Modal}} {{AdminProductGridFilterSection.skuFilter}}" userInput="{{sku}}" stepKey="fillProductSkuFilter"/>
             <click selector="{{AdminProductCrossSellModalSection.Modal}} {{AdminProductGridFilterSection.applyFilters}}" stepKey="clickApplyFilters"/>
             <waitForPageLoad stepKey="waitForPageToLoad"/>
    +        <waitForElementClickable selector="{{AdminProductCrossSellModalSection.Modal}}{{AdminProductModalSlideGridSection.productGridXRowYColumnButton('1', '1')}}" stepKey="waitForProductClickable" />
             <click selector="{{AdminProductCrossSellModalSection.Modal}}{{AdminProductModalSlideGridSection.productGridXRowYColumnButton('1', '1')}}" stepKey="selectProduct"/>
    +        <waitForElementClickable selector="{{AdminProductCrossSellModalSection.addSelectedProducts}}" stepKey="waitForAddRelatedProductClickable" />
             <click selector="{{AdminProductCrossSellModalSection.addSelectedProducts}}" stepKey="addRelatedProductSelected"/>
             <waitForPageLoad stepKey="waitForModalDisappear"/>
         </actionGroup>
    
  • app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCheckProductByIdOnProductGridActionGroup.xml+22 0 added
    @@ -0,0 +1,22 @@
    +<?xml version="1.0" encoding="UTF-8"?>
    +<!--
    + /**
    +  * Copyright © Magento, Inc. All rights reserved.
    +  * See COPYING.txt for license details.
    +  */
    +-->
    +
    +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    +              xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd">
    +    <actionGroup name="AdminCheckProductByIdOnProductGridActionGroup">
    +        <annotations>
    +            <description>Check the checkbox for the product on the Product Grid using Product ID</description>
    +        </annotations>
    +        <arguments>
    +            <argument name="productId" type="string"/>
    +        </arguments>
    +
    +        <waitForElementClickable selector="{{AdminProductGridSection.productRowCheckboxById(productId)}}" stepKey="waitForElementClickable" />
    +        <checkOption selector="{{AdminProductGridSection.productRowCheckboxById(productId)}}" stepKey="selectProduct"/>
    +    </actionGroup>
    +</actionGroups>
    
  • app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminClickMassUpdateProductAttributesActionGroup.xml+2 0 modified
    @@ -12,7 +12,9 @@
             <annotations>
                 <description>Clicks on 'Update attributes' from dropdown actions list on product grid page. Products should be selected via mass action before</description>
             </annotations>
    +        <waitForElementClickable selector="{{AdminProductGridSection.bulkActionDropdown}}" stepKey="waitForDropdownClickable" />
             <click selector="{{AdminProductGridSection.bulkActionDropdown}}" stepKey="clickDropdown"/>
    +        <waitForElementClickable selector="{{AdminProductGridSection.bulkActionOption('Update attributes')}}" stepKey="waitForOptionClickable" />
             <click selector="{{AdminProductGridSection.bulkActionOption('Update attributes')}}" stepKey="clickOption"/>
             <waitForPageLoad stepKey="waitForBulkUpdatePage"/>
             <seeInCurrentUrl url="{{ProductAttributesEditPage.url}}" stepKey="seeInUrl"/>
    
  • app/code/Magento/Catalog/Test/Mftf/ActionGroup/DeleteCategoryActionGroup.xml+1 0 modified
    @@ -19,6 +19,7 @@
             <amOnPage url="{{AdminCategoryPage.url}}" stepKey="goToCategoryPage"/>
             <waitForPageLoad time="60" stepKey="waitForCategoryPageLoad"/>
             <click selector="{{AdminCategorySidebarTreeSection.categoryInTree(categoryEntity.name)}}" stepKey="clickCategoryLink"/>
    +        <waitForElementClickable selector="{{AdminCategoryMainActionsSection.DeleteButton}}" stepKey="waitForDeleteButtonClickable" />
             <click selector="{{AdminCategoryMainActionsSection.DeleteButton}}" stepKey="clickDelete"/>
             <waitForElementVisible selector="{{AdminCategoryModalSection.message}}" stepKey="waitForConfirmationModal"/>
             <see selector="{{AdminCategoryModalSection.message}}" userInput="Are you sure you want to delete this category?" stepKey="seeDeleteConfirmationMessage"/>
    
  • app/code/Magento/Catalog/Test/Mftf/ActionGroup/DeleteProductAttributeByCodeActionGroup.xml+28 0 added
    @@ -0,0 +1,28 @@
    +<?xml version="1.0" encoding="UTF-8"?>
    +<!--
    + /**
    +  * Copyright © Magento, Inc. All rights reserved.
    +  * See COPYING.txt for license details.
    +  */
    +-->
    +
    +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    +              xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd">
    +    <actionGroup name="DeleteProductAttributeByCodeActionGroup">
    +        <annotations>
    +            <description>Delete a Product Attribute from the Product Attribute creation/edit page by code.</description>
    +        </annotations>
    +        <arguments>
    +            <argument name="attribute_code" type="string"/>
    +        </arguments>
    +        <amOnPage url="{{AdminProductAttributeGridPage.url}}" stepKey="navigateToProductAttributeGrid"/>
    +        <conditionalClick selector="{{AdminDataGridHeaderSection.clearFilters}}" dependentSelector="{{AdminDataGridHeaderSection.clearFilters}}" visible="true" stepKey="clearExistingFilters"/>
    +        <fillField selector="{{AdminProductAttributeGridSection.FilterByAttributeCode}}" userInput="{{attribute_code}}" stepKey="setAttributeCode"/>
    +        <click selector="{{AdminProductAttributeGridSection.Search}}" stepKey="searchForAttributeFromTheGrid"/>
    +        <click selector="{{AdminProductAttributeGridSection.FirstRow}}" stepKey="clickOnAttributeRow"/>
    +        <waitForPageLoad stepKey="waitForViewAdminProductAttributeLoad" time="30"/>
    +        <click selector="{{AttributePropertiesSection.DeleteAttribute}}" stepKey="deleteAttribute"/>
    +        <click selector="{{ModalConfirmationSection.OkButton}}" stepKey="clickOnConfirmOk"/>
    +        <waitForPageLoad stepKey="waitForViewProductAttributePageLoad"/>
    +    </actionGroup>
    +</actionGroups>
    
  • app/code/Magento/Catalog/Test/Mftf/ActionGroup/SortProductsByIdDescendingActionGroup.xml+1 0 modified
    @@ -15,5 +15,6 @@
     
             <conditionalClick selector="{{AdminProductGridTableHeaderSection.id('ascend')}}" dependentSelector="{{AdminProductGridTableHeaderSection.id('descend')}}" visible="false" stepKey="sortById"/>
             <waitForPageLoad stepKey="waitForPageLoad"/>
    +        <wait time="5" stepKey="simpleWait" />
         </actionGroup>
     </actionGroups>
    
  • app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAddProductToCompareActionGroup.xml+3 0 modified
    @@ -16,8 +16,11 @@
                 <argument name="productVar"/>
             </arguments>
     
    +        <waitForPageLoad stepKey="waitForProductPageOpenedAndLoaded" />
    +        <waitForElementClickable selector="{{StorefrontProductInfoMainSection.productAddToCompare}}" stepKey="waitForAddToCompareButtonClickable" />
             <click selector="{{StorefrontProductInfoMainSection.productAddToCompare}}" stepKey="clickAddToCompare"/>
             <waitForElement selector="{{StorefrontMessagesSection.success}}" time="30" stepKey="waitForAddProductToCompareSuccessMessage"/>
             <see selector="{{StorefrontMessagesSection.success}}" userInput="You added product {{productVar.name}} to the comparison list." stepKey="assertAddProductToCompareSuccessMessage"/>
    +        <waitForPageLoad stepKey="waitForAdditionToFinish" />
         </actionGroup>
     </actionGroups>
    
  • app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeData.xml+1 1 modified
    @@ -134,7 +134,7 @@
             <requiredEntity type="FrontendLabel">ProductAttributeFrontendLabel</requiredEntity>
         </entity>
         <entity name="productAttributeWithDropdownTwoOptions" type="ProductAttribute">
    -        <data key="attribute_code">testattribute</data>
    +        <data key="attribute_code" unique="suffix">testattribute</data>
             <data key="frontend_input">select</data>
             <data key="scope">global</data>
             <data key="is_required">false</data>
    
  • app/code/Magento/Catalog/Test/Mftf/Section/AdminProductGridSection.xml+1 0 modified
    @@ -11,6 +11,7 @@
             <element name="productRowBySku" type="block" selector="//td[count(../../..//th[./*[.='SKU']]/preceding-sibling::th) + 1][./*[.='{{sku}}']]" parameterized="true" />
             <element name="productRowByName" type="block" selector="//td[count(../../..//th[./*[.='Name']]/preceding-sibling::th) + 1][./*[.='{{sku}}']]" parameterized="true" />
             <element name="productRowCheckboxBySku" type="block" selector="//td[count(../../..//th[./*[.='SKU']]/preceding-sibling::th) + 1][./*[.='{{sku}}']]/../td//input[@data-action='select-row']" parameterized="true" />
    +        <element name="productRowCheckboxById" type="block" selector="#idscheck{{id}}" parameterized="true" />
             <element name="loadingMask" type="text" selector=".admin__data-grid-loading-mask[data-component*='product_listing']"/>
             <element name="columnHeader" type="button" selector="//div[@data-role='grid-wrapper']//table[contains(@class, 'data-grid')]/thead/tr/th[contains(@class, 'data-grid-th')]/span[text() = '{{label}}']" parameterized="true" timeout="30"/>
             <element name="column" type="text" selector="//tr//td[count(//div[@data-role='grid-wrapper']//tr//th[contains(., '{{column}}')]/preceding-sibling::th) +1 ]" parameterized="true"/>
    
  • app/code/Magento/Catalog/Test/Mftf/Section/StorefrontHeaderSection.xml+1 1 modified
    @@ -9,6 +9,6 @@
     <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd">
         <section name="StorefrontHeaderSection">
    -        <element name="NavigationCategoryByName" type="button" selector="//nav//a[span[contains(., '{{var1}}')]]" parameterized="true" timeout="30"/>
    +        <element name="NavigationCategoryByName" type="button" selector="//nav//li[a[span[contains(., '{{var1}}')]]]" parameterized="true" timeout="30"/>
         </section>
     </sections>
    
  • app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductPageSection.xml+1 0 modified
    @@ -26,5 +26,6 @@
             <element name="qtyInputWithProduct" type="input" selector="//tr//strong[contains(.,'{{productName}}')]/../../td[@class='col qty']//input" parameterized="true"/>
             <element name="customOptionRadio" type="input" selector="//span[contains(text(),'{{customOption}}')]/../../input" parameterized="true"/>
             <element name="onlyProductsLeft" type="block" selector="//div[@class='product-info-price']//div[@class='product-info-stock-sku']//div[@class='availability only']"/>
    +        <element name="qtyErr" type="text" selector="//*[@data-ui-id='message-error']//div"/>
         </section>
     </sections>
    
  • app/code/Magento/Catalog/Test/Mftf/Test/AdminApplyCatalogStorefrontConfigurationSettingsTest.xml+3 1 modified
    @@ -62,7 +62,9 @@
                     <magentoCLI command="config:set {{CustomStoreFrontListPerPageConfigData.path}} {{CustomStoreFrontListPerPageConfigData.value}}" stepKey="setCustomListPerPage"/>
                     <magentoCLI command="config:set {{CustomStoreFrontProductsSortBy.path}} {{CustomStoreFrontProductsSortBy.value}}" stepKey="setProductSortBy"/>
                     <magentoCLI command="config:set {{CustomStoreFrontAllProductsPerPage.path}} {{CustomStoreFrontAllProductsPerPage.value}}" stepKey="setAllProductsPerPage"/>
    -                <magentoCron groups="index" stepKey="reindex"/>
    +                <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +                    <argument name="indices" value=""/>
    +                </actionGroup>
                     <actionGroup ref="CliCacheCleanActionGroup" stepKey="flushCache">
                         <argument name="tags" value="full_page"/>
                     </actionGroup>
    
  • app/code/Magento/Catalog/Test/Mftf/Test/AdminApplyChangePriceForConfigurableProductWithAssignedSimpleProductsTest.xml+87 71 modified
    @@ -15,102 +15,118 @@
                 <severity value="MAJOR"/>
                 <testCaseId value="AC-2031"/>
                 <group value="Catalog"/>
    +            <group value="AllIndexerBySchedule" />
             </annotations>
     
             <before>
                 <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/>
    -            <actionGroup ref="UpdateAllIndexerByScheduleActionGroup" stepKey="updateAnIndexerBySchedule"/>
    -            <magentoCLI command="config:set {{EnableFlatRateConfigData.path}} {{EnableFlatRateConfigData.value}}" stepKey="enableFlatRate"/>
    -            <magentoCLI command="indexer:reindex" stepKey="performReindex"/>
    -            <magentoCLI command="cache:flush" stepKey="cleanCache"/>
    +            <comment userInput="BIC workaround" stepKey="updateAnIndexerBySchedule"/>
    +            <comment userInput="BIC workaround" stepKey="enableFlatRate"/>
    +
    +            <!-- Create category for  configurable product -->
    +            <createData entity="SimpleSubCategory" stepKey="firstSimpleCategory"/>
    +
    +            <!-- Create  configurable product with two options -->
    +            <createData entity="ApiConfigurableProduct" stepKey="createFirstConfigProduct">
    +                <requiredEntity createDataKey="firstSimpleCategory"/>
    +            </createData>
    +            <createData entity="productAttributeWithTwoOptions" stepKey="createFirstConfigProductAttribute"/>
    +            <createData entity="productAttributeOption1" stepKey="createFirstConfigProductAttributeFirstOption">
    +                <requiredEntity createDataKey="createFirstConfigProductAttribute"/>
    +            </createData>
    +            <createData entity="productAttributeOption2" stepKey="createFirstConfigProductAttributeSecondOption">
    +                <requiredEntity createDataKey="createFirstConfigProductAttribute"/>
    +            </createData>
    +            <createData entity="AddToDefaultSet" stepKey="addFirstProductToAttributeSet">
    +                <requiredEntity createDataKey="createFirstConfigProductAttribute"/>
    +            </createData>
    +            <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getFirstConfigAttributeFirstOption">
    +                <requiredEntity createDataKey="createFirstConfigProductAttribute"/>
    +            </getData>
    +
    +            <!-- Create one child product for configurable product -->
    +            <createData entity="ApiSimpleOne" stepKey="createFirstConfigFirstChildProduct">
    +                <requiredEntity createDataKey="createFirstConfigProductAttribute"/>
    +                <requiredEntity createDataKey="getFirstConfigAttributeFirstOption"/>
    +            </createData>
    +            <createData entity="ConfigurableProductOneOption" stepKey="createFirstConfigProductOption">
    +                <requiredEntity createDataKey="createFirstConfigProduct"/>
    +                <requiredEntity createDataKey="createFirstConfigProductAttribute"/>
    +                <requiredEntity createDataKey="getFirstConfigAttributeFirstOption"/>
    +            </createData>
    +            <createData entity="ConfigurableProductAddChild" stepKey="createFirstConfigProductAddFirstChild">
    +                <requiredEntity createDataKey="createFirstConfigProduct"/>
    +                <requiredEntity createDataKey="createFirstConfigFirstChildProduct"/>
    +            </createData>
    +
    +            <!-- Reindex -->
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="performReindex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
    +            <actionGroup ref="CliCacheCleanActionGroup" stepKey="cleanCache">
    +                <argument name="tags" value="full_page"/>
    +            </actionGroup>
             </before>
     
             <after>
                 <deleteData createDataKey="createFirstConfigProduct" stepKey="deleteConfigProduct"/>
                 <deleteData createDataKey="createFirstConfigFirstChildProduct" stepKey="deleteFirstConfigFirstChildProduct"/>
                 <deleteData createDataKey="firstSimpleCategory" stepKey="deleteCategory"/>
                 <deleteData createDataKey="createFirstConfigProductAttribute" stepKey="deleteFirstConfigProductAttribute"/>
    -            <comment userInput="The test was moved to elasticsearch suite" stepKey="resetCatalogSearchConfiguration"/>
    -            <actionGroup ref="AdminAllIndexerSetUpdateOnSaveActionGroup" stepKey="resetIndexerBackToOriginalState"/>
    -            <magentoCLI command="indexer:reindex" stepKey="performReindex"/>
    -            <magentoCLI command="cache:flush" stepKey="cleanCache"/>
    +            <comment userInput="BIC workaround" stepKey="resetCatalogSearchConfiguration"/>
    +            <comment userInput="BIC workaround" stepKey="resetIndexerBackToOriginalState"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="performReindex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
    +            <actionGroup ref="CliCacheCleanActionGroup" stepKey="cleanCache">
    +                <argument name="tags" value="full_page"/>
    +            </actionGroup>
                 <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutOfAdmin"/>
             </after>
     
    -        <!-- Create category for  configurable product -->
    -        <createData entity="SimpleSubCategory" stepKey="firstSimpleCategory"/>
    -
    -        <!-- Create  configurable product with two options -->
    -        <createData entity="ApiConfigurableProduct" stepKey="createFirstConfigProduct">
    -            <requiredEntity createDataKey="firstSimpleCategory"/>
    -        </createData>
    -
    -        <createData entity="productAttributeWithTwoOptions" stepKey="createFirstConfigProductAttribute"/>
    -
    -        <createData entity="productAttributeOption1" stepKey="createFirstConfigProductAttributeFirstOption">
    -            <requiredEntity createDataKey="createFirstConfigProductAttribute"/>
    -        </createData>
    -        <createData entity="productAttributeOption2" stepKey="createFirstConfigProductAttributeSecondOption">
    -            <requiredEntity createDataKey="createFirstConfigProductAttribute"/>
    -        </createData>
    -
    -        <createData entity="AddToDefaultSet" stepKey="addFirstProductToAttributeSet">
    -            <requiredEntity createDataKey="createFirstConfigProductAttribute"/>
    -        </createData>
    -
    -        <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getFirstConfigAttributeFirstOption">
    -            <requiredEntity createDataKey="createFirstConfigProductAttribute"/>
    -        </getData>
    -
    -        <!-- Create one child product for configurable product -->
    -        <createData entity="ApiSimpleOne" stepKey="createFirstConfigFirstChildProduct">
    -            <requiredEntity createDataKey="createFirstConfigProductAttribute"/>
    -            <requiredEntity createDataKey="getFirstConfigAttributeFirstOption"/>
    -        </createData>
    -
    -        <createData entity="ConfigurableProductOneOption" stepKey="createFirstConfigProductOption">
    -            <requiredEntity createDataKey="createFirstConfigProduct"/>
    -            <requiredEntity createDataKey="createFirstConfigProductAttribute"/>
    -            <requiredEntity createDataKey="getFirstConfigAttributeFirstOption"/>
    -        </createData>
    -
    -        <createData entity="ConfigurableProductAddChild" stepKey="createFirstConfigProductAddFirstChild">
    -            <requiredEntity createDataKey="createFirstConfigProduct"/>
    -            <requiredEntity createDataKey="createFirstConfigFirstChildProduct"/>
    -        </createData>
    +        <comment userInput="BIC workaround" stepKey="firstSimpleCategory"/>
    +        <comment userInput="BIC workaround" stepKey="createFirstConfigProduct"/>
    +        <comment userInput="BIC workaround" stepKey="createFirstConfigProductAttribute"/>
    +        <comment userInput="BIC workaround" stepKey="createFirstConfigProductAttributeFirstOption"/>
    +        <comment userInput="BIC workaround" stepKey="createFirstConfigProductAttributeSecondOption"/>
    +        <comment userInput="BIC workaround" stepKey="addFirstProductToAttributeSet"/>
    +        <comment userInput="BIC workaround" stepKey="getFirstConfigAttributeFirstOption"/>
    +        <comment userInput="BIC workaround" stepKey="createFirstConfigFirstChildProduct"/>
    +        <comment userInput="BIC workaround" stepKey="createFirstConfigProductOption"/>
    +        <comment userInput="BIC workaround" stepKey="createFirstConfigProductAddFirstChild"/>
     
             <!-- Assert first product in category -->
    -        <magentoCLI command="cron:run" stepKey="runCron"/>
    -        <amOnPage url="{{StorefrontCategoryPage.url($$firstSimpleCategory.custom_attributes[url_key]$$)}}" stepKey="goToFirstCategoryPageStorefront"/>
    -        <waitForPageLoad stepKey="waitForFirstCategoryPageLoad"/>
    -
    +        <comment userInput="BIC workaround" stepKey="runCron"/>
    +        <actionGroup ref="StorefrontNavigateToCategoryUrlActionGroup" stepKey="goToFirstCategoryPageStorefront">
    +            <argument name="categoryUrl" value="$firstSimpleCategory.custom_attributes[url_key]$"/>
    +        </actionGroup>
    +        <comment userInput="BIC workaround" stepKey="waitForFirstCategoryPageLoad"/>
             <actionGroup ref="StorefrontCheckCategoryConfigurableProductWithUpdatedPriceActionGroup" stepKey="checkFirstProductPriceInCategory">
                 <argument name="productName" value="$$createFirstConfigProduct.name$$"/>
                 <argument name="expectedPrice" value="$$createFirstConfigFirstChildProduct.price$$"/>
             </actionGroup>
     
    -        <!-- Search default simple product in grid -->
    -        <actionGroup ref="AdminClearFiltersActionGroup" stepKey="openProductCatalogPage"/>
    -        <comment userInput="Comment is added to preserve the step key for backward compatibility" stepKey="filterProductGrid"/>
    -        <actionGroup ref="OpenEditProductOnBackendActionGroup" stepKey="clickFirstRowToOpenDefaultSimpleProduct">
    -            <argument name="product" value="$$createFirstConfigFirstChildProduct$$"/>
    +        <!-- Update simple product price -->
    +        <comment userInput="BIC workaround" stepKey="openProductCatalogPage"/>
    +        <comment userInput="BIC workaround" stepKey="filterProductGrid"/>
    +        <comment userInput="BIC workaround" stepKey="clickFirstRowToOpenDefaultSimpleProduct"/>
    +        <comment userInput="BIC workaround" stepKey="waitUntilProductIsOpened"/>
    +        <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="openProductEditPage">
    +            <argument name="productId" value="$createFirstConfigFirstChildProduct.id$"/>
             </actionGroup>
    -        <comment userInput="Comment is added to preserve the step key for backward compatibility" stepKey="waitUntilProductIsOpened"/>
    -
    -        <!-- Update default simple product with price -->
    +        <waitForElementVisible selector="{{AdminProductFormSection.productPrice}}" stepKey="waitForProductPriceField"/>
             <fillField selector="{{AdminProductFormSection.productPrice}}" userInput="150" stepKey="fillSimpleProductPrice"/>
    -        <actionGroup ref="AdminProductFormSaveButtonClickActionGroup" stepKey="clickButtonSave"/>
    -
    -        <!-- Verify customer see success message -->
    -        <see selector="{{AdminProductFormSection.successMessage}}" userInput="You saved the product." stepKey="seeAssertSimpleProductSaveSuccessMessage"/>
    +        <actionGroup ref="AdminProductFormSaveActionGroup" stepKey="clickButtonSave"/>
    +        <waitForText selector="{{AdminProductFormSection.successMessage}}" userInput="You saved the product." stepKey="seeAssertSimpleProductSaveSuccessMessage"/>
     
             <!-- Assert first product in category -->
    -        <magentoCLI command="cron:run" stepKey="runCron1"/>
    -        <wait time="60" stepKey="waitForUpdateStarts"/>
    -
    -        <amOnPage url="{{StorefrontCategoryPage.url($$firstSimpleCategory.custom_attributes[url_key]$$)}}" stepKey="goToFirstCategoryPageStorefront1"/>
    -        <waitForPageLoad stepKey="waitForFirstCategoryPageLoad1"/>
    -
    +        <comment userInput="BIC workaround" stepKey="runCron1"/>
    +        <comment userInput="BIC workaround" stepKey="runCron2"/>
    +        <comment userInput="BIC workaround" stepKey="waitForUpdateStarts"/>
    +        <actionGroup ref="StorefrontNavigateToCategoryUrlActionGroup" stepKey="goToFirstCategoryPageStorefront1">
    +            <argument name="categoryUrl" value="$firstSimpleCategory.custom_attributes[url_key]$"/>
    +        </actionGroup>
    +        <comment userInput="BIC workaround" stepKey="waitForFirstCategoryPageLoad1"/>
             <actionGroup ref="StorefrontCheckCategoryConfigurableProductWithUpdatedPriceActionGroup" stepKey="checkFirstProductPriceInCategory1">
                 <argument name="productName" value="$$createFirstConfigProduct.name$$"/>
                 <argument name="expectedPrice" value="150"/>
    
  • app/code/Magento/Catalog/Test/Mftf/Test/AdminApplyTierPriceToProductTest/AdminApplyTierPriceToProductTest.xml+1 0 modified
    @@ -28,6 +28,7 @@
                 </createData>
             </before>
             <after>
    +            <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="logoutCustomer" />
                 <deleteData createDataKey="createSimpleUSCustomer" stepKey="deleteCustomer"/>
                 <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/>
                 <deleteData createDataKey="createCategory" stepKey="deleteCategory"/>
    
  • app/code/Magento/Catalog/Test/Mftf/Test/AdminApplyTierPriceToProductTest/StoreFrontDeleteProductImagesAssignedDifferentRolesTest.xml+1 0 modified
    @@ -84,6 +84,7 @@
             </before>
             <after>
                 <deleteData createDataKey="testCategory" stepKey="deleteCategory"/>
    +            <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="logoutCustomer" />
                 <deleteData createDataKey="simpleProductOne" stepKey="deleteCustomer"/>
             </after>
     
    
  • app/code/Magento/Catalog/Test/Mftf/Test/AdminAssignProductAttributeToAttributeSetTest.xml+3 1 modified
    @@ -35,7 +35,9 @@
                 <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/>
     
                 <!-- Reindex invalidated indices after product attribute has been created/deleted -->
    -            <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindexInvalidatedIndices">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </after>
     
             <!-- Go to default attribute set edit page -->
    
  • app/code/Magento/Catalog/Test/Mftf/Test/AdminChangeArrangementOfAttributesInAnAttributeSetTest.xml+3 1 modified
    @@ -33,7 +33,9 @@
             <actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin"/>
     
             <!-- Reindex invalidated indices after product attribute has been created/deleted -->
    -        <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/>
    +        <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindexInvalidatedIndices">
    +            <argument name="indices" value=""/>
    +        </actionGroup>
     
             <!-- Navigate to Stores > Attributes > Attribute Set -->
             <amOnPage url="{{AdminProductAttributeSetGridPage.url}}" stepKey="goToAttributeSetPage"/>
    
  • app/code/Magento/Catalog/Test/Mftf/Test/AdminChangeProductAttributeGroupTest.xml+3 1 modified
    @@ -52,7 +52,9 @@
                 <actionGroup ref="ClearProductsFilterActionGroup" stepKey="clearProductsFilter"/>
     
                 <!-- Reindex invalidated indices after product attribute has been created/deleted -->
    -            <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindexInvalidatedIndices">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
                 <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/>
             </after>
     
    
  • app/code/Magento/Catalog/Test/Mftf/Test/AdminChangeProductAttributeSetTest.xml+3 1 modified
    @@ -49,7 +49,9 @@
                 <actionGroup ref="ClearProductsFilterActionGroup" stepKey="clearProductsFilter"/>
     
                 <!-- Reindex invalidated indices after product attribute has been created/deleted -->
    -            <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindexInvalidatedIndices">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </after>
     
             <actionGroup ref="SearchForProductOnBackendActionGroup" stepKey="searchForSimpleProduct">
    
  • app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckConfigurableProductPriceWhenChildProductPriceUpdatedTest.xml+9 7 modified
    @@ -87,9 +87,9 @@
     
                 <!--Open Index Management Page and Select Index mode "Update by Schedule" -->
                 <magentoCLI stepKey="setIndexerMode" command="indexer:set-mode" arguments="schedule" />
    -
    -            <!-- Run cron -->
    -            <magentoCron stepKey="runIndexCronJobs" groups="index"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="runIndexCronJobs">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
                 <comment userInput="Adding the comment to replace CliCacheFlushActionGroup action group ('cache:flush' command) for preserving Backward Compatibility" stepKey="flushCache"/>
     
                 <!-- Wait till cron job runs for schedule updates -->
    @@ -108,7 +108,9 @@
                 <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/>
     
                 <!-- Reindex invalidated indices after product attribute has been created/deleted -->
    -            <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindexInvalidatedIndices">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </after>
     
             <!-- Open Product in Store Front Page -->
    @@ -164,9 +166,9 @@
             <waitForPageLoad stepKey="waitForProductPageToLoad"/>
     
             <updateData entity="SimpleProductUpdatePrice90" createDataKey="createConfigChildProduct1" stepKey="updateSimpleProductOne"/>
    -
    -        <!-- Run cron -->
    -        <magentoCron stepKey="runIndexCronJobs" groups="index"/>
    +        <actionGroup ref="CliIndexerReindexActionGroup" stepKey="runIndexCronJobs">
    +            <argument name="indices" value=""/>
    +        </actionGroup>
             <comment userInput="Adding the comment to replace CliCacheFlushActionGroup action group ('cache:flush' command) for preserving Backward Compatibility" stepKey="flushCache"/>
     
             <!-- Wait till cron job runs for schedule updates -->
    
  • app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckConfigurableProductPriceWithDisabledChildProductTest.xml+3 1 modified
    @@ -120,7 +120,9 @@
                 <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/>
     
                 <!-- Reindex invalidated indices after product attribute has been created/deleted -->
    -            <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindexInvalidatedIndices">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </after>
     
             <!-- Open Product in Store Front Page -->
    
  • app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckCustomAttributeValuesAfterProductSaveTest.xml+3 1 modified
    @@ -45,7 +45,9 @@
                 <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/>
     
                 <!-- Reindex invalidated indices after product attribute has been created/deleted -->
    -            <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindexInvalidatedIndices">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </after>
     
             <!-- Open created product for edit -->
    
  • app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckMediaRolesForFirstAddedImageViaApiTest.xml+3 1 modified
    @@ -29,7 +29,9 @@
                 <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/>
                 <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/>
                 <!-- Reindex invalidated indices after product attribute has been created/deleted -->
    -           <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindexInvalidatedIndices">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </after>
     
             <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToSimpleProduct">
    
  • app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckProductListPriceAttributesTest.xml+3 1 modified
    @@ -34,7 +34,9 @@
                 <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/>
                 <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/>
                 <!-- Reindex invalidated indices after product attribute has been created/deleted -->
    -           <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindexInvalidatedIndices">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </after>
     
             <actionGroup ref="ToggleAdminProductGridColumnsDropdownActionGroup" stepKey="openColumnsDropdown"/>
    
  • app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateAndEditVirtualProductSettingsTest.xml+6 2 modified
    @@ -27,7 +27,9 @@
     
                 <!-- Create website -->
                 <createData entity="secondCustomWebsite" stepKey="createWebsite"/>
    -            <magentoCron groups="index" stepKey="reindex"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
     
                 <!-- Login as admin -->
                 <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/>
    @@ -43,7 +45,9 @@
                 <actionGroup ref="AdminDeleteWebsiteActionGroup" stepKey="deleteWebsite">
                     <argument name="websiteName" value="$createWebsite.website[name]$"/>
                 </actionGroup>
    -            <magentoCron groups="index" stepKey="reindex"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
     
                 <!-- Delete related products -->
                 <deleteData createDataKey="createFirstRelatedProduct" stepKey="deleteFirstRelatedProduct"/>
    
  • app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithCustomRootCategoryTest.xml+6 2 modified
    @@ -24,7 +24,9 @@
                 <actionGroup ref="DeleteCustomStoreActionGroup" stepKey="deleteCustomStore">
                     <argument name="storeGroupName" value="customStoreGroup.name"/>
                 </actionGroup>
    -            <magentoCron groups="index" stepKey="reindex"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
                 <actionGroup ref="DeleteCategoryActionGroup" stepKey="deleteCreatedNewRootCategory">
                     <argument name="categoryEntity" value="NewRootCategory"/>
                 </actionGroup>
    @@ -54,7 +56,9 @@
                 <argument name="StoreGroup" value="customStoreGroup"/>
                 <argument name="customStore" value="customStore"/>
             </actionGroup>
    -        <magentoCron groups="index" stepKey="reindex"/>
    +        <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +            <argument name="indices" value=""/>
    +        </actionGroup>
             <!--Go to store front page-->
             <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="openHomepage"/>
             <!--Verify subcategory displayed in store front page-->
    
  • app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDropdownProductAttributeVisibleInStorefrontAdvancedSearchFormTest.xml+3 1 modified
    @@ -39,7 +39,9 @@
                 <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/>
     
                 <!-- Reindex invalidated indices after product attribute has been created/deleted -->
    -            <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindexInvalidatedIndices">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </after>
     
             <!-- Filter product attribute set by attribute set name -->
    
  • app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateInactiveFlatCategoryAndUpdateAsInactiveTest.xml+3 2 modified
    @@ -36,8 +36,9 @@
                 <magentoCLI stepKey="setFlatCatalogCategory" command="config:set catalog/frontend/flat_catalog_category 1"/>
                 <!--Open Index Management Page and Select Index mode "Update by Schedule" -->
                 <magentoCLI stepKey="setIndexerMode" command="indexer:set-mode" arguments="schedule" />
    -            <!-- Run cron -->
    -            <magentoCron stepKey="runIndexCronJobs" groups="index"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="runIndexCronJobs">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </before>
             <after>
                 <magentoCLI stepKey="setFlatCatalogCategory" command="config:set catalog/frontend/flat_catalog_category 0 "/>
    
  • app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateInactiveFlatCategoryTest.xml+3 2 modified
    @@ -36,8 +36,9 @@
                 <magentoCLI stepKey="setFlatCatalogCategory" command="config:set catalog/frontend/flat_catalog_category 1"/>
                 <!--Open Index Management Page and Select Index mode "Update by Schedule" -->
                 <magentoCLI stepKey="setIndexerMode" command="indexer:set-mode" arguments="schedule" />
    -            <!-- Run cron -->
    -            <magentoCron stepKey="runIndexCronJobs" groups="index"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="runIndexCronJobs">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </before>
             <after>
                 <magentoCLI stepKey="setFlatCatalogCategory" command="config:set catalog/frontend/flat_catalog_category 0 "/>
    
  • app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateInactiveInMenuFlatCategoryTest.xml+3 1 modified
    @@ -37,7 +37,9 @@
                 <!--Open Index Management Page and Select Index mode "Update by Schedule" -->
                 <magentoCLI stepKey="setIndexerMode" command="indexer:set-mode" arguments="schedule" />
                 <!-- Run cron -->
    -            <magentoCron stepKey="runIndexCronJobs" groups="index"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="runIndexCronJobs">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </before>
             <after>
                 <magentoCLI stepKey="setFlatCatalogCategory" command="config:set catalog/frontend/flat_catalog_category 0 "/>
    
  • app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateMultipleSelectProductAttributeVisibleInStorefrontAdvancedSearchFormTest.xml+3 1 modified
    @@ -43,7 +43,9 @@
                 <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/>
     
                 <!-- Reindex invalidated indices after product attribute has been created/deleted -->
    -            <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindexInvalidatedIndices">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </after>
     
             <!-- Filter product attribute set by attribute set name -->
    
  • app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateNewAttributeFromProductTest.xml+3 1 modified
    @@ -55,7 +55,9 @@
             <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createSecondStoreView">
                 <argument name="customStore" value="customStoreFR"/>
             </actionGroup>
    -        <magentoCron groups="index" stepKey="reindex"/>
    +        <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +            <argument name="indices" value=""/>
    +        </actionGroup>
     
             <!--Go to created product page and create new attribute-->
             <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="openAdminEditPage">
    
  • app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateNewGroupForAttributeSetTest.xml+3 1 modified
    @@ -31,7 +31,9 @@
                 <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/>
     
                 <!-- Reindex invalidated indices after product attribute has been created/deleted -->
    -            <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindexInvalidatedIndices">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </after>
     
             <!-- Navigate to Stores > Attributes > Attribute Set -->
    
  • app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductTest/AdminCreateSimpleProductTest.xml+3 1 modified
    @@ -34,7 +34,9 @@
                 <argument name="category" value="$$createPreReqCategory$$"/>
                 <argument name="simpleProduct" value="_defaultProduct"/>
             </actionGroup>
    -        <magentoCLI stepKey="runCronIndex" command="cron:run --group=index"/>
    +        <actionGroup ref="CliIndexerReindexActionGroup" stepKey="runCronIndex">
    +            <argument name="indices" value=""/>
    +        </actionGroup>
             <actionGroup ref="AssertProductInStorefrontCategoryPage" stepKey="assertProductInStorefront1">
                 <argument name="category" value="$$createPreReqCategory$$"/>
                 <argument name="product" value="_defaultProduct"/>
    
  • app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductTest/AdminCreateTwoSimpleProductTest.xml+3 1 modified
    @@ -15,6 +15,7 @@
                 <testCaseId value="MC-36852"/>
                 <severity value="MAJOR"/>
                 <group value="product"/>
    +            <group value="guest_checkout"/>
             </annotations>
             <before>
                 <createData entity="ApiCategory" stepKey="createCategory"/>
    @@ -32,7 +33,8 @@
                 <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="deleteProduct">
                     <argument name="sku" value="{{_defaultProduct.sku}}"/>
                 </actionGroup>
    -           <actionGroup ref="AdminDeleteCustomerActionGroup" stepKey="deleteCustomer">
    +           <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="logoutCustomer" />
    +            <actionGroup ref="AdminDeleteCustomerActionGroup" stepKey="deleteCustomer">
                    <argument name="customerEmail" value="Simple_US_Customer.email"/>
                </actionGroup>
                 <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearFilter"/>
    
  • app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductWithCustomOptionsSuiteAndImportOptionsTest.xml+3 1 modified
    @@ -119,7 +119,9 @@
     
             <!-- Verify we see success message -->
             <see selector="{{AdminProductFormSection.successMessage}}" userInput="You saved the product." stepKey="seeAssertVirtualProductSuccessMessage"/>
    -        <magentoCron groups="index" stepKey="reindex"/>
    +        <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +            <argument name="indices" value=""/>
    +        </actionGroup>
             <comment userInput="Adding the comment to replace CliCacheFlushActionGroup action group ('cache:flush' command) for preserving Backward Compatibility" stepKey="flushCache"/>
     
             <!-- Verify customer see created virtual product with custom options suite and import options(from above step) on storefront page and is searchable by sku -->
    
  • app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductWithoutManageStockTest.xml+3 0 modified
    @@ -24,6 +24,9 @@
             </before>
             <after>
                 <deleteData stepKey="deleteSimpleSubCategory" createDataKey="categoryEntity"/>
    +            <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="deleteVirtualProductWithoutManageStock">
    +                <argument name="sku" value="{{virtualProductWithoutManageStock.sku}}"/>
    +            </actionGroup>
                 <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/>
             </after>
     
    
  • app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductWithTierPriceForGeneralGroupTest.xml+1 0 modified
    @@ -26,6 +26,7 @@
             </before>
             <after>
                 <deleteData createDataKey="categoryEntity" stepKey="deleteSimpleSubCategory"/>
    +            <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="logoutCustomer" />
                 <deleteData createDataKey="customer" stepKey="deleteCustomer"/>
                 <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteVirtualProduct">
                     <argument name="product" value="virtualProductGeneralGroup"/>
    
  • app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteConfigurableChildProductsTest.xml+3 1 modified
    @@ -84,7 +84,9 @@
                 <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/>
     
                 <!-- Reindex invalidated indices after product attribute has been created/deleted -->
    -            <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindexInvalidatedIndices">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </after>
             <!--Open Product in Store Front Page -->
             <actionGroup ref="StorefrontOpenProductEntityPageActionGroup" stepKey="openProductInStoreFront">
    
  • app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteDropdownProductAttributeFromAttributeSetTest.xml+3 1 modified
    @@ -31,7 +31,9 @@
                 <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/>
     
                 <!-- Reindex invalidated indices after product attribute has been created/deleted -->
    -            <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindexInvalidatedIndices">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </after>
             <!-- Open Product Attribute Set Page -->
             <amOnPage url="{{AdminProductAttributeSetGridPage.url}}" stepKey="goToAttributeSets"/>
    
  • app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteProductAttributeTest.xml+3 1 modified
    @@ -25,7 +25,9 @@
                 <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/>
     
                 <!-- Reindex invalidated indices after product attribute has been created/deleted -->
    -            <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindexInvalidatedIndices">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </after>
             <actionGroup ref="OpenProductAttributeFromSearchResultInGridActionGroup" stepKey="openProductAttributeFromSearchResultInGrid">
                 <argument name="productAttributeCode" value="$$createProductAttribute.attribute_code$$"/>
    
  • app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteProductsImageInCaseOfMultipleStoresTest.xml+6 2 modified
    @@ -36,7 +36,9 @@
                     <argument name="StoreGroup" value="NewStoreData"/>
                     <argument name="customStore" value="NewStoreViewData"/>
                 </actionGroup>
    -            <magentoCron groups="index" stepKey="reindex"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
                 <!--Create Product-->
                 <createData entity="SimpleProduct2" stepKey="createProduct"/>
                 <createData entity="SubCategory" stepKey="createSubCategory"/>
    @@ -66,7 +68,9 @@
                 <actionGroup ref="AdminDeleteWebsiteActionGroup" stepKey="deleteWebsite">
                     <argument name="websiteName" value="{{NewWebSiteData.name}}"/>
                 </actionGroup>
    -            <magentoCron groups="index" stepKey="reindex"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
                 <comment userInput="Adding the comment to replace CliCacheFlushActionGroup action group ('cache:flush' command) for preserving Backward Compatibility" stepKey="flushCache"/>
                 <deleteData createDataKey="createSubCategory" stepKey="deleteSubCategory"/>
                 <deleteData createDataKey="createRootCategory" stepKey="deleteRootCategory"/>
    
  • app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteRootCategoryAssignedToStoreTest.xml+3 1 modified
    @@ -25,7 +25,9 @@
                 <actionGroup ref="DeleteCustomStoreActionGroup" stepKey="deleteCreatedStore">
                     <argument name="storeGroupName" value="customStore.code"/>
                 </actionGroup>
    -            <magentoCron groups="index" stepKey="reindex"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
                 <deleteData createDataKey="rootCategory" stepKey="deleteRootCategory"/>
                 <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/>
             </after>
    
  • app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteRootSubCategoryTest.xml+6 2 modified
    @@ -28,7 +28,9 @@
                 <actionGroup ref="DeleteCustomStoreActionGroup" stepKey="deleteCreatedStore">
                     <argument name="storeGroupName" value="customStore.code"/>
                 </actionGroup>
    -            <magentoCron groups="index" stepKey="reindex"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
                 <deleteData createDataKey="rootCategory" stepKey="deleteRootCategory"/>
                 <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/>
             </after>
    @@ -45,7 +47,9 @@
                 <argument name="StoreGroup" value="customStore"/>
                 <argument name="customStore" value="customStore"/>
             </actionGroup>
    -        <magentoCron groups="index" stepKey="reindex"/>
    +        <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +            <argument name="indices" value=""/>
    +        </actionGroup>
     
             <!--Go To store front page-->
             <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="openHomepage"/>
    
  • app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteTextFieldProductAttributeFromAttributeSetTest.xml+3 1 modified
    @@ -34,7 +34,9 @@
                 <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/>
     
                 <!-- Reindex invalidated indices after product attribute has been created/deleted -->
    -            <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindexInvalidatedIndices">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </after>
             <!-- Open Product Attribute Set Page -->
             <amOnPage url="{{AdminProductAttributeSetGridPage.url}}" stepKey="goToAttributeSets"/>
    
  • app/code/Magento/Catalog/Test/Mftf/Test/AdminFilteringCategoryProductsUsingScopeSelectorTest.xml+6 2 modified
    @@ -33,7 +33,9 @@
                     <argument name="StoreGroup" value="SecondStoreGroupUnique"/>
                     <argument name="customStore" value="SecondStoreUnique"/>
                 </actionGroup>
    -            <magentoCron groups="index" stepKey="reindex"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
     
                 <!--Create Simple Product and Category -->
                 <createData entity="_defaultCategory" stepKey="createCategory"/>
    @@ -92,7 +94,9 @@
                 <actionGroup ref="AdminDeleteWebsiteActionGroup" stepKey="deleteWebsite">
                     <argument name="websiteName" value="{{secondCustomWebsite.name}}"/>
                 </actionGroup>
    -            <magentoCron groups="index" stepKey="reindex"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
                 <actionGroup ref="ClearProductsFilterActionGroup" stepKey="clearProductsFilter"/>
                 <deleteData createDataKey="createProduct0" stepKey="deleteProduct"/>
                 <deleteData createDataKey="createProduct1" stepKey="deleteProduct1"/>
    
  • app/code/Magento/Catalog/Test/Mftf/Test/AdminMassChangeProductsStatusTest.xml+3 1 modified
    @@ -28,7 +28,9 @@
                 <createData entity="ApiSimpleProduct" stepKey="createProductTwo">
                     <requiredEntity createDataKey="createCategory"/>
                 </createData>
    -            <magentoCron groups="index" stepKey="reindex"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </before>
             <after>
                 <actionGroup ref="AdminLogoutActionGroup" stepKey="amOnLogoutPage"/>
    
  • app/code/Magento/Catalog/Test/Mftf/Test/AdminMassProductAttributeUpdateAddedToQueueTest.xml+4 0 modified
    @@ -19,6 +19,7 @@
                 <useCaseId value="MC-29179"/>
                 <group value="catalog"/>
                 <group value="asynchronousOperations"/>
    +            <group value="cloud"/>
             </annotations>
             <before>
                 <createData entity="ApiProductWithDescription" stepKey="createFirstProduct"/>
    @@ -39,15 +40,18 @@
                 <argument name="keyword" value="api-simple-product"/>
             </actionGroup>
             <actionGroup ref="SortProductsByIdDescendingActionGroup" stepKey="sortProductsByIdDescending"/>
    +        <waitForElementClickable selector="{{AdminProductGridSection.productGridCheckboxOnRow('1')}}" stepKey="waitForSelectThirdProduct"/>
             <checkOption selector="{{AdminProductGridSection.productGridCheckboxOnRow('1')}}" stepKey="selectThirdProduct"/>
             <checkOption selector="{{AdminProductGridSection.productGridCheckboxOnRow('2')}}" stepKey="selectSecondProduct"/>
             <checkOption selector="{{AdminProductGridSection.productGridCheckboxOnRow('3')}}" stepKey="selectFirstProduct"/>
             <actionGroup ref="AdminClickMassUpdateProductAttributesActionGroup" stepKey="goToUpdateProductAttributesPage"/>
             <checkOption selector="{{AdminEditProductAttributesSection.changeAttributeShortDescriptionToggle}}" stepKey="toggleToChangeShortDescription"/>
             <fillField selector="{{AdminEditProductAttributesSection.attributeShortDescription}}" userInput="Test Update" stepKey="fillShortDescriptionField"/>
             <actionGroup ref="AdminSaveProductsMassAttributesUpdateActionGroup" stepKey="saveMassAttributeUpdate"/>
    +        <waitForElementVisible selector="{{AdminSystemMessagesSection.info}}" stepKey="waitForInfoMessage" />
             <see selector="{{AdminSystemMessagesSection.info}}" userInput="Task &quot;Update attributes for 3 selected products&quot;: 1 item(s) have been scheduled for update." stepKey="seeInfoMessage"/>
             <click selector="{{AdminSystemMessagesSection.viewDetailsLink}}" stepKey="seeDetails"/>
    +        <waitForElementVisible selector="{{AdminBulkDetailsModalSection.descriptionValue}}" stepKey="waitForDescription" />
             <see selector="{{AdminBulkDetailsModalSection.descriptionValue}}" userInput="Update attributes for 3 selected products" stepKey="seeDescription"/>
             <see selector="{{AdminBulkDetailsModalSection.summaryValue}}" userInput="Pending, in queue..." stepKey="seeSummary"/>
             <grabTextFrom selector="{{AdminBulkDetailsModalSection.startTimeValue}}" stepKey="grabStartTimeValue"/>
    
  • app/code/Magento/Catalog/Test/Mftf/Test/AdminMassProductPriceUpdateTest.xml+3 1 modified
    @@ -62,7 +62,9 @@
                 <argument name="maxMessages" value="{{AdminProductAttributeUpdateConsumerData.messageLimit}}"/>
             </actionGroup>
     
    -        <magentoCLI command="cron:run --group=index" stepKey="runCron"/>
    +        <actionGroup ref="CliIndexerReindexActionGroup" stepKey="runCron">
    +            <argument name="indices" value=""/>
    +        </actionGroup>
     
             <comment userInput="Comment is added to preserve the step key for backward compatibility" stepKey="openFirstProduct"/>
             <actionGroup ref="AssertAdminProductPriceUpdatedOnEditPageActionGroup" stepKey="waitForFirstProductToLoad">
    
  • app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesGlobalScopeTest.xml+6 2 modified
    @@ -30,7 +30,9 @@
                 <createData entity="ApiSimpleProduct" stepKey="createProductTwo">
                     <requiredEntity createDataKey="createCategory"/>
                 </createData>
    -            <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindexInvalidatedIndices">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </before>
             <after>
                 <deleteData createDataKey="createProductOne" stepKey="deleteProductOne"/>
    @@ -40,7 +42,9 @@
                 <actionGroup ref="AdminClearGridFiltersActionGroup" stepKey="resetSearchFilter"/>
                 <actionGroup ref="ClearProductsFilterActionGroup" stepKey="clearProductFilter"/>
                 <actionGroup ref="AdminLogoutActionGroup" stepKey="amOnLogoutPage"/>
    -            <magentoCron groups="index" stepKey="reindexInvalidatedIndicesAfterDelete"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindexInvalidatedIndicesAfterDelete">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </after>
     
             <!-- Search and select products -->
    
  • app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesMissingRequiredFieldTest.xml+4 4 modified
    @@ -45,12 +45,12 @@
                 <argument name="keyword" value="api-simple-product"/>
             </actionGroup>
     
    -        <actionGroup ref="AdminCheckProductOnProductGridActionGroup" stepKey="clickCheckbox1">
    -            <argument name="product" value="$$createProductOne$$"/>
    +        <actionGroup ref="AdminCheckProductByIdOnProductGridActionGroup" stepKey="clickCheckbox1">
    +            <argument name="productId" value="$$createProductOne.id$$"/>
             </actionGroup>
     
    -        <actionGroup ref="AdminCheckProductOnProductGridActionGroup" stepKey="clickCheckbox2">
    -            <argument name="product" value="$$createProductTwo$$"/>
    +        <actionGroup ref="AdminCheckProductByIdOnProductGridActionGroup" stepKey="clickCheckbox2">
    +            <argument name="productId" value="$$createProductTwo.id$$"/>
             </actionGroup>
     
             <actionGroup ref="AdminClickMassUpdateProductAttributesActionGroup" stepKey="clickDropdown"/>
    
  • app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesStoreViewScopeTest/AdminMassUpdateProductAttributesStoreViewScopeTest.xml+9 2 modified
    @@ -22,7 +22,9 @@
             <before>
                 <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/>
                 <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createStoreView"/>
    -            <magentoCron groups="index" stepKey="reindex"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
                 <createData entity="ApiProductWithDescription" stepKey="createProductOne"/>
                 <createData entity="ApiProductWithDescription" stepKey="createProductTwo"/>
                 <createData entity="ApiProductNameWithNoSpaces" stepKey="createProductThree"/>
    @@ -32,7 +34,9 @@
                 <deleteData createDataKey="createProductTwo" stepKey="deleteProductTwo"/>
                 <deleteData createDataKey="createProductThree" stepKey="deleteProductThree"/>
                 <actionGroup ref="AdminDeleteStoreViewActionGroup" stepKey="AdminDeleteStoreViewActionGroup"/>
    -            <magentoCron groups="index" stepKey="reindex"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
                 <actionGroup ref="AdminLogoutActionGroup" stepKey="amOnLogoutPage"/>
             </after>
     
    @@ -43,10 +47,13 @@
                 <argument name="keyword" value="api-simple-product"/>
             </actionGroup>
             <actionGroup ref="SortProductsByIdDescendingActionGroup" stepKey="sortProductsByIdDescending"/>
    +        <waitForElementClickable selector="{{AdminProductGridSection.productGridCheckboxOnRow('1')}}" stepKey="waitForFirstCheckboxClickable" />
             <checkOption selector="{{AdminProductGridSection.productGridCheckboxOnRow('1')}}" stepKey="clickCheckbox1"/>
             <checkOption selector="{{AdminProductGridSection.productGridCheckboxOnRow('2')}}" stepKey="clickCheckbox2"/>
             <!-- Mass update attributes -->
    +        <waitForElementClickable selector="{{AdminProductGridSection.bulkActionDropdown}}" stepKey="waitForDropdownClickable" />
             <click selector="{{AdminProductGridSection.bulkActionDropdown}}" stepKey="clickDropdown"/>
    +        <waitForElementClickable selector="{{AdminProductGridSection.bulkActionOption('Update attributes')}}" stepKey="waitForOptionClickable" />
             <click selector="{{AdminProductGridSection.bulkActionOption('Update attributes')}}" stepKey="clickOption"/>
             <waitForPageLoad stepKey="waitForBulkUpdatePage"/>
             <seeInCurrentUrl stepKey="seeInUrl" url="catalog/product_action_attribute/edit/"/>
    
  • app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductStatusStoreViewScopeTest/AdminMassUpdateProductStatusStoreViewScopeTest.xml+6 2 modified
    @@ -35,7 +35,9 @@
                     <argument name="StoreGroup" value="customStoreGroup"/>
                     <argument name="customStore" value="customStore"/>
                 </actionGroup>
    -            <magentoCron groups="index" stepKey="reindex"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
     
                 <!--Create a Simple Product 1 -->
                 <actionGroup ref="CreateSimpleProductAndAddToWebsiteActionGroup" stepKey="createSimpleProduct1">
    @@ -54,7 +56,9 @@
                 <actionGroup ref="AdminDeleteWebsiteActionGroup" stepKey="deleteSecondWebsite">
                     <argument name="websiteName" value="{{customWebsite.name}}"/>
                 </actionGroup>
    -            <magentoCron groups="index" stepKey="reindex"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
                 <actionGroup ref="AdminClearFiltersActionGroup" stepKey="clearFilters"/>
     
                 <!--Delete Products -->
    
  • app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveAnchoredCategoryTest.xml+3 1 modified
    @@ -30,7 +30,9 @@
                 <createData entity="_defaultProduct" stepKey="productTwo">
                     <requiredEntity createDataKey="simpleSubCategoryOne"/>
                 </createData>
    -            <magentoCron groups="index" stepKey="RunToScheduleJobs"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="RunToScheduleJobs">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
                 <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/>
             </before>
     
    
  • app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveCategoryAndCheckUrlRewritesTest.xml+3 1 modified
    @@ -22,7 +22,9 @@
                 <createData entity="FirstLevelSubCat" stepKey="createDefaultCategory">
                     <field key="is_active">true</field>
                 </createData>
    -            <magentoCron groups="index" stepKey="reindex"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
                 <actionGroup ref="CliCacheFlushActionGroup" stepKey="flushCache">
                     <argument name="tags" value=""/>
                 </actionGroup>
    
  • app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveProductBetweenCategoriesTest.xml+6 2 modified
    @@ -111,7 +111,9 @@
             <see userInput="You saved the product." selector="{{CatalogProductsSection.messageSuccessSavedProduct}}" stepKey="seeSuccessMessage"/>
     
             <!-- Run cron -->
    -        <magentoCLI command="cron:run --group=index" stepKey="runCron"/>
    +        <actionGroup ref="CliIndexerReindexActionGroup" stepKey="runCron">
    +            <argument name="indices" value=""/>
    +        </actionGroup>
     
             <!-- Clear invalidated cache on System>Tools>Cache Management page  -->
             <amOnPage url="{{AdminCacheManagementPage.url}}" stepKey="onCachePage"/>
    @@ -182,7 +184,9 @@
             <see userInput="You saved the product." selector="{{CatalogProductsSection.messageSuccessSavedProduct}}" stepKey="seeSaveMessage"/>
     
             <!-- Run cron -->
    -        <magentoCLI command="cron:run --group=index" stepKey="runCron2"/>
    +        <actionGroup ref="CliIndexerReindexActionGroup" stepKey="runCron2">
    +            <argument name="indices" value=""/>
    +        </actionGroup>
     
             <!-- Open frontend -->
             <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="onFrontendPage"/>
    
  • app/code/Magento/Catalog/Test/Mftf/Test/AdminMultipleWebsitesUseDefaultValuesTest.xml+6 2 modified
    @@ -35,14 +35,18 @@
                     <argument name="StoreGroup" value="customStoreGroup"/>
                     <argument name="customStore" value="customStore"/>
                 </actionGroup>
    -            <magentoCron groups="index" stepKey="reindex"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </before>
     
             <after>
                 <actionGroup ref="AdminDeleteWebsiteActionGroup" stepKey="deleteSecondWebsite">
                     <argument name="websiteName" value="{{customWebsite.name}}"/>
                 </actionGroup>
    -            <magentoCron groups="index" stepKey="reindex"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
                 <actionGroup ref="AdminClearFiltersActionGroup" stepKey="clearFilters"/>
                 <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/>
             </after>
    
  • app/code/Magento/Catalog/Test/Mftf/Test/AdminNavigateMultipleUpSellProductsTest.xml+3 1 modified
    @@ -92,7 +92,9 @@
                 <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/>
     
                 <!-- Reindex invalidated indices after product attribute has been created/deleted -->
    -            <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindexInvalidatedIndices">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </after>
     
             <!--Open Product Index Page-->
    
  • app/code/Magento/Catalog/Test/Mftf/Test/AdminProductCategoryIndexerInUpdateOnScheduleModeTest.xml+35 20 modified
    @@ -92,10 +92,13 @@
             <actionGroup ref="AssertStorefrontNoProductsFoundActionGroup" stepKey="seeEmptyNotice"/>
             <dontSee userInput="$$createProductA1.name$$" selector="{{StorefrontCategoryMainSection.productName}}" stepKey="dontseeProductA1"/>
     
    -        <!-- 4. Run cron to reindex -->
    -        <wait time="60" stepKey="waitForChanges"/>
    -        <magentoCLI command="cron:run --group index" stepKey="runCron"/>
    -        <magentoCLI command="cron:run --group index" stepKey="runCronTwice"/>
    +        <!-- 4. Reindex -->
    +        <comment userInput="BIC workaround" stepKey="waitForChanges"/>
    +        <comment userInput="BIC workaround" stepKey="runCron"/>
    +        <comment userInput="BIC workaround" stepKey="runCronTwice"/>
    +        <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex1">
    +            <argument name="indices" value=""/>
    +        </actionGroup>
     
             <!-- 5. Open category A on Storefront again -->
             <actionGroup ref="ReloadPageActionGroup" stepKey="reloadCategoryA"/>
    @@ -122,10 +125,13 @@
             <see userInput="$$createCategoryA.name$$" selector="{{StorefrontCategoryMainSection.CategoryTitle}}" stepKey="seeCategoryAOnPage"/>
             <see userInput="$$createProductA1.name$$" selector="{{StorefrontCategoryMainSection.productName}}" stepKey="seeNameProductA1"/>
     
    -        <!-- 8. Run cron reindex (Ensure that at least one minute passed since last cron run) -->
    -        <wait time="60" stepKey="waitOneMinute"/>
    -        <magentoCLI command="cron:run --group index" stepKey="runCron1"/>
    -        <magentoCLI command="cron:run --group index" stepKey="runCronTwice1"/>
    +        <!-- 8. Reindex -->
    +        <comment userInput="BIC workaround" stepKey="waitOneMinute"/>
    +        <comment userInput="BIC workaround" stepKey="runCron1"/>
    +        <comment userInput="BIC workaround" stepKey="runCronTwice1"/>
    +        <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex2">
    +            <argument name="indices" value=""/>
    +        </actionGroup>
     
             <!-- 9. Open category A on Storefront again -->
             <actionGroup ref="ReloadPageActionGroup" stepKey="refreshCategoryAPage"/>
    @@ -178,10 +184,13 @@
             <see userInput="$$createProductC1.name$$" selector="{{StorefrontCategoryMainSection.productName}}" stepKey="seeProductC1inCategoryC1"/>
             <see userInput="$$createProductC2.name$$" selector="{{StorefrontCategoryMainSection.productName}}" stepKey="seeProductC2InCategoryC2"/>
     
    -        <!-- 14. Run cron to reindex  (Ensure that at least one minute passed since last cron run) -->
    -        <wait time="60" stepKey="waitMinute"/>
    -        <magentoCLI command="cron:run --group index" stepKey="runCron2"/>
    -        <magentoCLI command="cron:run --group index" stepKey="runCronTwice2"/>
    +        <!-- 14. Reindex -->
    +        <comment userInput="BIC workaround" stepKey="waitMinute"/>
    +        <comment userInput="BIC workaround" stepKey="runCron2"/>
    +        <comment userInput="BIC workaround" stepKey="runCronTwice2"/>
    +        <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex3">
    +            <argument name="indices" value=""/>
    +        </actionGroup>
     
             <!--  15. Open category B on Storefront  -->
             <actionGroup ref="StorefrontGoToCategoryPageActionGroup" stepKey="onPageCategoryB">
    @@ -238,10 +247,13 @@
             <dontSee userInput="$$createProductC1.name$$" selector="{{StorefrontCategoryMainSection.productName}}" stepKey="dontSeeCategoryCPageProductC1"/>
             <see userInput="$$createProductC2.name$$" selector="{{StorefrontCategoryMainSection.productName}}" stepKey="seeCategoryCPageProductC2"/>
     
    -        <!-- 17.14. Run cron to reindex  (Ensure that at least one minute passed since last cron run) -->
    -        <wait time="60" stepKey="waitForOneMinute"/>
    -        <magentoCLI command="cron:run --group index" stepKey="runCron3"/>
    -        <magentoCLI command="cron:run --group index" stepKey="runCronTwice3"/>
    +        <!-- 17.14. Reindex -->
    +        <comment userInput="BIC workaround" stepKey="waitForOneMinute"/>
    +        <comment userInput="BIC workaround" stepKey="runCron3"/>
    +        <comment userInput="BIC workaround" stepKey="runCronTwice3"/>
    +        <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex4">
    +            <argument name="indices" value=""/>
    +        </actionGroup>
     
             <!--  17.15. Open category B on Storefront  -->
             <actionGroup ref="StorefrontGoToCategoryPageActionGroup" stepKey="openPageCategoryB">
    @@ -300,10 +312,13 @@
             <see userInput="$$createProductC1.name$$" selector="{{StorefrontCategoryMainSection.productName}}" stepKey="seeOnCategoryCProductC1"/>
             <see userInput="$$createProductC2.name$$" selector="{{StorefrontCategoryMainSection.productName}}" stepKey="seeOnCategoryCProductC2"/>
     
    -        <!-- 18.14. Run cron to reindex  (Ensure that at least one minute passed since last cron run) -->
    -        <wait time="60" stepKey="waitExtraMinute"/>
    -        <magentoCLI command="cron:run --group index" stepKey="runCron4"/>
    -        <magentoCLI command="cron:run --group index" stepKey="runCronTwice4"/>
    +        <!-- 18.14. Reindex -->
    +        <comment userInput="BIC workaround" stepKey="waitExtraMinute"/>
    +        <comment userInput="BIC workaround" stepKey="runCron4"/>
    +        <comment userInput="BIC workaround" stepKey="runCronTwice4"/>
    +        <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex5">
    +            <argument name="indices" value=""/>
    +        </actionGroup>
     
             <!--  18.15. Open category B on Storefront  -->
             <actionGroup ref="StorefrontGoToCategoryPageActionGroup" stepKey="navigateToPageCategoryB">
    
  • app/code/Magento/Catalog/Test/Mftf/Test/AdminProductCustomURLKeyPreservedWhenAssignedToCategoryWithoutCustomURLKeyTest.xml+6 2 modified
    @@ -34,7 +34,9 @@
                     <argument name="customStore" value="storeViewData"/>
                 </actionGroup>
     
    -            <magentoCron groups="index" stepKey="reindex"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
                 <comment userInput="Adding the comment to replace CliCacheFlushActionGroup action group ('cache:flush' command) for preserving Backward Compatibility" stepKey="flushCache"/>
             </before>
             <after>
    @@ -45,7 +47,9 @@
                 <actionGroup ref="AdminDeleteStoreViewActionGroup" stepKey="deleteStoreView">
                     <argument name="customStore" value="storeViewData"/>
                 </actionGroup>
    -            <magentoCron groups="index" stepKey="reindex"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
     
                 <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/>
             </after>
    
  • app/code/Magento/Catalog/Test/Mftf/Test/AdminProductGridFilteringByCustomAttributeTest.xml+3 1 modified
    @@ -92,7 +92,9 @@
                 <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/>
     
                 <!-- Reindex invalidated indices after product attribute has been created/deleted -->
    -            <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindexInvalidatedIndices">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </after>
             <actionGroup ref="AdminOpenProductIndexPageActionGroup" stepKey="navigateToProductIndex"/>
             <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearProductGridFilters"/>
    
  • app/code/Magento/Catalog/Test/Mftf/Test/AdminProductImageAssignmentForMultipleStoresTest.xml+6 2 modified
    @@ -30,7 +30,9 @@
                 <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createStoreViewFr">
                     <argument name="customStore" value="customStoreFR"/>
                 </actionGroup>
    -            <magentoCron groups="index" stepKey="reindex"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
                 <!-- Create Category and Simple Product -->
                 <createData entity="_defaultCategory" stepKey="createCategory"/>
                 <createData entity="_defaultProduct" stepKey="createSimpleProduct">
    @@ -49,7 +51,9 @@
                 </actionGroup>
                 <!-- Clear Filter Store -->
                 <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="resetFiltersOnStorePage"/>
    -            <magentoCron groups="index" stepKey="reindex"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
                 <!-- Delete Category and Simple Product -->
                 <deleteData createDataKey="createSimpleProduct" stepKey="deleteProduct"/>
                 <deleteData createDataKey="createCategory" stepKey="deleteCategory"/>
    
  • app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveImageAffectsAllScopesTest.xml+6 2 modified
    @@ -52,7 +52,9 @@
                     <argument name="StoreGroup" value="SecondStoreGroupUnique"/>
                     <argument name="customStore" value="SecondStoreUnique"/>
                 </actionGroup>
    -            <magentoCron groups="index" stepKey="reindex"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </before>
     
             <after>
    @@ -66,7 +68,9 @@
                 </actionGroup>
                 <deleteData createDataKey="category" stepKey="deletePreReqCategory"/>
                 <deleteData createDataKey="product" stepKey="deleteFirstProduct"/>
    -            <magentoCron groups="index" stepKey="reindex"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
                 <comment userInput="Adding the comment to replace CliCacheFlushActionGroup action group ('cache:flush' command) for preserving Backward Compatibility" stepKey="flushCache"/>
                 <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/>
             </after>
    
  • app/code/Magento/Catalog/Test/Mftf/Test/AdminShouldBeAbleToAssociateSimpleProductToWebsitesTest.xml+6 2 modified
    @@ -32,7 +32,9 @@
                     <argument name="StoreGroup" value="customStoreGroup"/>
                     <argument name="customStore" value="customStoreEN"/>
                 </actionGroup>
    -            <magentoCron groups="index" stepKey="reindex"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </before>
     
             <after>
    @@ -43,7 +45,9 @@
                     <argument name="websiteName" value="$createCustomWebsite.website[name]$"/>
                 </actionGroup>
                 <actionGroup ref="AdminGridFilterResetActionGroup" stepKey="resetFiltersOnStoresIndexPage"/>
    -            <magentoCron groups="index" stepKey="reindex"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
                 <actionGroup ref="AdminOpenProductIndexPageActionGroup" stepKey="openProductIndexPageToResetFilters"/>
                 <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearFiltersOnProductIndexPage"/>
                 <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/>
    
  • app/code/Magento/Catalog/Test/Mftf/Test/AdminShowDoubleSpacesInProductGrid.xml+4 2 modified
    @@ -24,8 +24,10 @@
                 <createData entity="ApiSimpleProductWithDoubleSpaces" stepKey="createProduct">
                     <requiredEntity createDataKey="createCategory"/>
                 </createData>
    -            <magentoCLI command="cron:run --group=index" stepKey="cronRun"/>
    -            <magentoCLI command="cron:run --group=index" stepKey="cronRunSecondTime"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="cronRun">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
    +            <comment userInput="BIC workaround" stepKey="cronRunSecondTime"/>
             </before>
     
             <after>
    
  • app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleSetEditRelatedProductsTest.xml+3 1 modified
    @@ -28,7 +28,9 @@
                 <createData entity="SimpleProduct2" stepKey="simpleProduct5"/>
                 <createData entity="SimpleProduct2" stepKey="simpleProduct6"/>
     
    -            <magentoCLI command="cron:run --group=index" stepKey="runCronIndexer"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="runCronIndexer">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </before>
             <after>
                 <!-- Delete simple product -->
    
  • app/code/Magento/Catalog/Test/Mftf/Test/AdminSortingByWebsitesTest.xml+6 2 modified
    @@ -31,7 +31,9 @@
                     <argument name="websiteCode" value="{{customWebsite.code}}"/>
                 </actionGroup>
                 <actionGroup ref="EnableWebUrlOptionsActionGroup" stepKey="addStoreCodeToUrls"/>
    -            <magentoCron groups="index" stepKey="reindex"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
                 <comment userInput="Adding the comment to replace CliCacheFlushActionGroup action group ('cache:flush' command) for preserving Backward Compatibility" stepKey="flushCacheAfterEnableWebUrlOptions"/>
             </before>
             <after>
    @@ -44,7 +46,9 @@
                 <actionGroup ref="AdminOpenCatalogProductPageActionGroup" stepKey="goToProductCatalogPage"/>
                 <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetProductGridColumnsInitial"/>
                 <actionGroup ref="ResetWebUrlOptionsActionGroup" stepKey="resetUrlOption"/>
    -            <magentoCron groups="index" stepKey="reindex"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
                 <comment userInput="Adding the comment to replace CliCacheFlushActionGroup action group ('cache:flush' command) for preserving Backward Compatibility" stepKey="flushCache"/>
                 <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/>
             </after>
    
  • app/code/Magento/Catalog/Test/Mftf/Test/AdminTierPriceNotAvailableForProductOptionsWithoutTierPriceTest.xml+3 1 modified
    @@ -85,7 +85,9 @@
                 <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutOfAdmin"/>
     
                 <!-- Reindex invalidated indices after product attribute has been created/deleted -->
    -            <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindexInvalidatedIndices">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </after>
     
             <!--Go to storefront product page an check price box css-->
    
  • app/code/Magento/Catalog/Test/Mftf/Test/AdminUnassignProductAttributeFromAttributeSetTest.xml+6 2 modified
    @@ -35,11 +35,15 @@
             <after>
                 <deleteData createDataKey="product" stepKey="deleteProduct"/>
                 <deleteData createDataKey="attribute" stepKey="deleteAttribute"/>
    -            <magentoCLI command="cron:run --group=index" stepKey="runCron"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="runCron">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
                 <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/>
     
                 <!-- Reindex invalidated indices after product attribute has been created/deleted -->
    -            <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindexInvalidatedIndices">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </after>
             <!-- Assert attribute presence in storefront product additional information -->
             <amOnPage url="/$$product.custom_attributes[url_key]$$.html" stepKey="onProductPage1"/>
    
  • app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateCategoryAndCheckDefaultUrlKeyOnStoreViewTest.xml+6 2 modified
    @@ -28,7 +28,9 @@
                 <actionGroup ref="DeleteCustomStoreActionGroup" stepKey="deleteCustomStore">
                     <argument name="storeGroupName" value="customStore.name"/>
                 </actionGroup>
    -            <magentoCron groups="index" stepKey="reindex"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
                 <deleteData createDataKey="rootCategory" stepKey="deleteRootCategory"/>
                 <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/>
             </after>
    @@ -48,7 +50,9 @@
                 <argument name="StoreGroup" value="customStore"/>
                 <argument name="customStore" value="customStore"/>
             </actionGroup>
    -        <magentoCron groups="index" stepKey="reindex"/>
    +        <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +            <argument name="indices" value=""/>
    +        </actionGroup>
     
             <!--Update Category-->
             <actionGroup ref="AdminOpenCategoryPageActionGroup" stepKey="openAdminCategoryIndexPage"/>
    
  • app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateCategoryNameWithStoreViewTest.xml+6 2 modified
    @@ -28,7 +28,9 @@
                 <actionGroup ref="DeleteCustomStoreActionGroup" stepKey="deleteCustomStore">
                     <argument name="storeGroupName" value="customStore.name"/>
                 </actionGroup>
    -            <magentoCron groups="index" stepKey="reindex"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
                 <deleteData createDataKey="rootCategory" stepKey="deleteRootCategory"/>
                 <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/>
             </after>
    @@ -45,7 +47,9 @@
                 <argument name="StoreGroup" value="customStore"/>
                 <argument name="customStore" value="customStore"/>
             </actionGroup>
    -        <magentoCron groups="index" stepKey="reindex"/>
    +        <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +            <argument name="indices" value=""/>
    +        </actionGroup>
     
             <!--Verify created SubCategory is present on Store Front -->
             <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="goToHomepage"/>
    
  • app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateCategoryUrlKeyWithStoreViewTest.xml+6 2 modified
    @@ -28,7 +28,9 @@
                 <actionGroup ref="DeleteCustomStoreActionGroup" stepKey="deleteCustomStore">
                     <argument name="storeGroupName" value="customStore.name"/>
                 </actionGroup>
    -            <magentoCron groups="index" stepKey="reindex"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
                 <deleteData createDataKey="rootCategory" stepKey="deleteRootCategory"/>
                 <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/>
             </after>
    @@ -45,7 +47,9 @@
                 <argument name="StoreGroup" value="customStore"/>
                 <argument name="customStore" value="customStore"/>
             </actionGroup>
    -        <magentoCron groups="index" stepKey="reindex"/>
    +        <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +            <argument name="indices" value=""/>
    +        </actionGroup>
     
             <!--Verify Category in Store View-->
             <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="goToHomepage"/>
    
  • app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateCategoryWithProductsDefaultSortingTest.xml+3 1 modified
    @@ -27,7 +27,9 @@
                 <deleteData createDataKey="createCategory" stepKey="deleteCategory"/>
                 <deleteData createDataKey="simpleProduct" stepKey="deleteSimpleProduct"/>
                 <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/>
    -            <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindexInvalidatedIndices">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </after>
     
             <!--Open Category Page-->
    
  • app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateFlatCategoryIncludeInNavigationTest.xml+6 2 modified
    @@ -34,7 +34,9 @@
                 <!--Open Index Management Page and Select Index mode "Update by Schedule" -->
                 <magentoCLI stepKey="setIndexerMode" command="indexer:set-mode" arguments="schedule" />
                 <!-- Reindex invalidated indices -->
    -            <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindexInvalidatedIndices">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
                 <comment userInput="Adding the comment to replace CliCacheFlushActionGroup action group ('cache:flush' command) for preserving Backward Compatibility" stepKey="flushCache"/>
             </before>
             <after>
    @@ -48,7 +50,9 @@
                 <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/>
                 <magentoCLI command="config:set catalog/frontend/flat_catalog_category 0 " stepKey="setFlatCatalogCategory"/>
                 <magentoCLI command="indexer:set-mode" arguments="realtime" stepKey="setIndexersMode"/>
    -            <magentoCron groups="index" stepKey="reindexInvalidatedIndicesAgain"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindexInvalidatedIndicesAgain">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </after>
             <!--Verify Category is not listed in navigation menu-->
             <amOnPage url="/{{CatNotIncludeInMenu.urlKey}}.html"  stepKey="openCategoryPage"/>
    
  • app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductNameToVerifyDataOverridingOnStoreViewLevelTest.xml+3 1 modified
    @@ -28,7 +28,9 @@
                     <requiredEntity createDataKey="initialCategoryEntity"/>
                 </createData>
                 <createData entity="SimpleSubCategory" stepKey="categoryEntity"/>
    -            <magentoCron groups="index" stepKey="reindex"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </before>
             <after>
                 <deleteData stepKey="deleteSimpleSubCategory" createDataKey="initialCategoryEntity"/>
    
  • app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductPriceToVerifyDataOverridingOnStoreViewLevelTest.xml+3 1 modified
    @@ -28,7 +28,9 @@
                     <requiredEntity createDataKey="initialCategoryEntity"/>
                 </createData>
                 <createData entity="SimpleSubCategory" stepKey="categoryEntity"/>
    -            <magentoCron groups="index" stepKey="reindex"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </before>
             <after>
                 <deleteData stepKey="deleteSimpleSubCategory" createDataKey="initialCategoryEntity"/>
    
  • app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductTieredPriceTest.xml+3 1 modified
    @@ -26,7 +26,9 @@
                 </createData>
                 <createData entity="SimpleSubCategory" stepKey="categoryEntity"/>
     
    -            <magentoCron groups="index" stepKey="reindex"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
                 <comment userInput="Adding the comment to replace CliCacheFlushActionGroup action group ('cache:flush' command) for preserving Backward Compatibility" stepKey="flushCache"/>
             </before>
             <after>
    
  • app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockDisabledProductTest.xml+3 1 modified
    @@ -24,7 +24,9 @@
                 <createData entity="defaultSimpleProduct" stepKey="initialSimpleProduct">
                     <requiredEntity createDataKey="initialCategoryEntity"/>
                 </createData>
    -            <magentoCron groups="index" stepKey="reindex"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </before>
             <after>
                 <deleteData stepKey="deleteSimpleSubCategory" createDataKey="initialCategoryEntity"/>
    
  • app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockEnabledFlatCatalogTest.xml+3 1 modified
    @@ -26,7 +26,9 @@
                     <requiredEntity createDataKey="initialCategoryEntity"/>
                 </createData>
                 <createData entity="SimpleSubCategory" stepKey="categoryEntity"/>
    -            <magentoCron groups="index" stepKey="reindex"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </before>
             <after>
                 <deleteData stepKey="deleteSimpleSubCategory" createDataKey="initialCategoryEntity"/>
    
  • app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockNotVisibleIndividuallyTest.xml+3 1 modified
    @@ -25,7 +25,9 @@
                     <requiredEntity createDataKey="initialCategoryEntity"/>
                 </createData>
                 <createData entity="SimpleSubCategory" stepKey="categoryEntity"/>
    -            <magentoCron groups="index" stepKey="reindex"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </before>
             <after>
                 <deleteData stepKey="deleteSimpleSubCategory" createDataKey="initialCategoryEntity"/>
    
  • app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockVisibleInCatalogAndSearchTest.xml+3 1 modified
    @@ -98,7 +98,9 @@
             <click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="clickAdminProductSEOSection1"/>
             <seeInField selector="{{AdminProductSEOSection.urlKeyInput}}" userInput="{{simpleProductRegularPrice245InStock.urlKey}}" stepKey="seeUrlKey"/>
     
    -        <magentoCron groups="index" stepKey="reindex"/>
    +        <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +            <argument name="indices" value=""/>
    +        </actionGroup>
     
             <!--Verify customer see updated simple product link on category page -->
             <amOnPage url="{{StorefrontCategoryPage.url($$categoryEntity.custom_attributes[url_key]$$)}}" stepKey="openCategoryPage"/>
    
  • app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockVisibleInCatalogOnlyTest.xml+3 1 modified
    @@ -128,7 +128,9 @@
             <scrollTo selector="{{AdminProductSEOSection.sectionHeader}}" x="0" y="-80" stepKey="scrollToAdminProductSEOSection1"/>
             <click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="clickAdminProductSEOSection1"/>
             <seeInField selector="{{AdminProductSEOSection.urlKeyInput}}" userInput="{{simpleProductRegularPrice32501InStock.urlKey}}" stepKey="seeUrlKey"/>
    -        <magentoCron groups="index" stepKey="reindex"/>
    +        <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +            <argument name="indices" value=""/>
    +        </actionGroup>
     
             <!--Verify customer see updated simple product link on category page -->
             <actionGroup ref="StorefrontGoToCategoryPageActionGroup" stepKey="openCategoryPage">
    
  • app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockVisibleInSearchOnlyTest.xml+3 1 modified
    @@ -25,7 +25,9 @@
                     <requiredEntity createDataKey="initialCategoryEntity"/>
                 </createData>
                 <createData entity="SimpleSubCategory" stepKey="categoryEntity"/>
    -            <magentoCron groups="index" stepKey="reindex"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </before>
             <after>
                 <deleteData stepKey="deleteSimpleSubCategory" createDataKey="initialCategoryEntity"/>
    
  • app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceOutOfStockTest.xml+3 1 modified
    @@ -25,7 +25,9 @@
                     <requiredEntity createDataKey="initialCategoryEntity"/>
                 </createData>
                 <createData entity="SimpleSubCategory" stepKey="categoryEntity"/>
    -            <magentoCron groups="index" stepKey="reindex"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </before>
             <after>
                 <deleteData stepKey="deleteSimpleSubCategory" createDataKey="initialCategoryEntity"/>
    
  • app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithRegularPriceInStockVisibleInCategoryOnlyTest.xml+3 1 modified
    @@ -26,7 +26,9 @@
                     <requiredEntity createDataKey="initialCategoryEntity"/>
                 </createData>
                 <createData entity="SimpleSubCategory" stepKey="categoryEntity"/>
    -            <magentoCron groups="index" stepKey="reindex"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </before>
             <after>
                 <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteVirtualProduct">
    
  • app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithRegularPriceOutOfStockVisibleInCategoryOnlyTest.xml+3 1 modified
    @@ -26,7 +26,9 @@
                     <requiredEntity createDataKey="initialCategoryEntity"/>
                 </createData>
                 <createData entity="SimpleSubCategory" stepKey="categoryEntity"/>
    -            <magentoCron groups="index" stepKey="reindex"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </before>
             <after>
                 <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="deleteDefaultVirtualProduct">
    
  • app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithRegularPriceOutOfStockVisibleInSearchOnlyTest.xml+3 1 modified
    @@ -25,7 +25,9 @@
                 <createData entity="defaultVirtualProduct" stepKey="initialVirtualProduct">
                     <requiredEntity createDataKey="initialCategoryEntity"/>
                 </createData>
    -            <magentoCron groups="index" stepKey="reindex"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </before>
             <after>
                 <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="deleteDefaultVirtualProduct">
    
  • app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithSpecialPriceInStockVisibleInCategoryAndSearchTest.xml+3 1 modified
    @@ -26,7 +26,9 @@
                     <requiredEntity createDataKey="initialCategoryEntity"/>
                 </createData>
                 <createData entity="SimpleSubCategory" stepKey="categoryEntity"/>
    -            <magentoCron groups="index" stepKey="reindex"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </before>
             <after>
                 <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="deleteDefaultVirtualProduct">
    
  • app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithSpecialPriceOutOfStockVisibleInCategoryAndSearchTest.xml+3 1 modified
    @@ -26,7 +26,9 @@
                     <requiredEntity createDataKey="initialCategoryEntity"/>
                 </createData>
                 <createData entity="SimpleSubCategory" stepKey="categoryEntity"/>
    -            <magentoCron groups="index" stepKey="reindex"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </before>
             <after>
                 <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="deleteDefaultVirtualProduct">
    
  • app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithTierPriceInStockVisibleInCategoryAndSearchTest.xml+3 1 modified
    @@ -26,7 +26,9 @@
                 </createData>
                 <createData entity="SimpleSubCategory" stepKey="categoryEntity"/>
                 <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/>
    -            <magentoCron groups="index" stepKey="RunToScheduleJobs"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="RunToScheduleJobs">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </before>
     
             <after>
    
  • app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithTierPriceInStockVisibleInCategoryOnlyTest.xml+3 1 modified
    @@ -26,7 +26,9 @@
                     <requiredEntity createDataKey="initialCategoryEntity"/>
                 </createData>
                 <createData entity="SimpleSubCategory" stepKey="categoryEntity"/>
    -            <magentoCron groups="index" stepKey="reindex"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </before>
             <after>
                 <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteVirtualProduct">
    
  • app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithTierPriceOutOfStockVisibleInCategoryAndSearchTest.xml+3 1 modified
    @@ -26,7 +26,9 @@
                     <requiredEntity createDataKey="initialCategoryEntity"/>
                 </createData>
                 <createData entity="SimpleSubCategory" stepKey="categoryEntity"/>
    -            <magentoCron groups="index" stepKey="reindex"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </before>
             <after>
                 <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteVirtualProduct">
    
  • app/code/Magento/Catalog/Test/Mftf/Test/AdminValidateProductPricesOnTheFrontendWithTierPricingSetupTest.xml+6 2 modified
    @@ -35,7 +35,9 @@
                 <!-- change configurations -->
                 <magentoCLI command="config:set {{CustomCatalogPrices.path}} {{CustomCatalogPrices.value}}" stepKey="selectIncludingTax"/>
                 <magentoCLI command="config:set {{CustomDisplayProductPricesInCatalog.path}} {{CustomDisplayProductPricesInCatalog.value}}" stepKey="selectInclAndExlTax"/>
    -            <magentoCLI command="cron:run --group=index" stepKey="runCronReindex"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="runCronReindex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </before>
             <after>
                 <!-- delete created product -->
    @@ -50,7 +52,9 @@
                 <!-- Revert back configurations -->
                 <magentoCLI command="config:set {{CatalogPrices.path}} {{CatalogPrices.value}}" stepKey="setExlTax"/>
                 <magentoCLI command="config:set {{DisplayProductPricesInCatalog.path}} {{DisplayProductPricesInCatalog.value}}" stepKey="selectExlTax"/>
    -            <magentoCron groups="index" stepKey="reindex"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
                 <actionGroup ref="CliCacheCleanActionGroup" stepKey="flushCache">
                     <argument name="tags" value="full_page"/>
                 </actionGroup>
    
  • app/code/Magento/Catalog/Test/Mftf/Test/AdminValidateRelatedUpsellCrossSellPositionValueInProductExportCsvTest.xml+3 1 modified
    @@ -27,7 +27,9 @@
                 <createData entity="SimpleProduct2" stepKey="simpleProduct3"/>
                 <createData entity="SimpleProduct2" stepKey="simpleProduct4"/>
                 <createData entity="SimpleProduct2" stepKey="simpleProduct5"/>
    -            <magentoCron stepKey="runCronIndex" groups="index"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="runCronIndex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </before>
     
             <after>
    
  • app/code/Magento/Catalog/Test/Mftf/Test/AdminVerifyCreateCustomProductAttributeTest.xml+3 1 modified
    @@ -25,7 +25,9 @@
                     <requiredEntity createDataKey="createCategory"/>
                 </createData>
                 <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/>
    -            <magentoCron groups="index" stepKey="reindex"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </before>
             <after>
                 <deleteData createDataKey="createCategory" stepKey="deleteCategory"/>
    
  • app/code/Magento/Catalog/Test/Mftf/Test/AlterAnchorCategoryTest.xml+3 1 modified
    @@ -130,7 +130,9 @@
             </assertStringContainsString>
             <click selector="{{AdminCategoryBasicFieldSection.acceptPopUp}}" stepKey="acceptPopUp"/>
             <wait time="10" stepKey="waitCategoryTreeToLoad"/>
    -        <magentoCLI command="indexer:reindex" stepKey="performReindex"/>
    +        <actionGroup ref="CliIndexerReindexActionGroup" stepKey="performReindex">
    +            <argument name="indices" value=""/>
    +        </actionGroup>
             <magentoCLI command="cache:flush" stepKey="cleanCache"/>
             <actionGroup ref="AdminAssertParentChildCategoryTreeElementsActionGroup" stepKey="assertParentChildCategoryTreeElements4thTime">
                 <argument name="parentCategoryName" value="Default Category"/>
    
  • app/code/Magento/Catalog/Test/Mftf/Test/ChangeScopeForProductStatusAttributeTest.xml+3 1 modified
    @@ -48,7 +48,9 @@
                     <argument name="customStore" value="storeViewData2"/>
                 </actionGroup>
     
    -            <magentoCron groups="index" stepKey="reindex"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
     
                 <!--Create Second website,store and 2 store views-->
                 <actionGroup ref="AdminCreateWebsiteActionGroup" stepKey="createWebsite" >
    
  • app/code/Magento/Catalog/Test/Mftf/Test/CheckTierPricingOfProductsTest.xml+10 3 modified
    @@ -55,7 +55,9 @@
             </actionGroup>
             <!--Set Configuration-->
             <createData entity="CatalogPriceScopeWebsite" stepKey="paymentMethodsSettingConfig"/>
    -        <magentoCron groups="index" stepKey="reindex"/>
    +        <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +            <argument name="indices" value=""/>
    +        </actionGroup>
             <!--Set advanced pricing for all 4 products-->
             <actionGroup ref="SearchForProductOnBackendActionGroup" stepKey="searchForSimpleProduct1">
                 <argument name="product" value="$$product1$$"/>
    @@ -147,8 +149,10 @@
             <see userInput="You saved the rule." stepKey="RuleSaved"/>
     
             <!--Create new order-->
    -        <actionGroup ref="NavigateToNewOrderPageExistingCustomerActionGroup" stepKey="CreateNewOrder">
    +        <actionGroup ref="AdminNavigateToNewOrderPageExistingCustomerActionGroup" stepKey="CreateNewOrder">
                 <argument name="customer" value="Simple_US_Customer"/>
    +        </actionGroup>
    +        <actionGroup ref="AdminSelectStoreDuringOrderCreationActionGroup" stepKey="selectCustomStore">
                 <argument name="storeView" value="customStore"/>
             </actionGroup>
     
    @@ -318,6 +322,7 @@
                 <deleteData createDataKey="product3" stepKey="deleteProduct3"/>
                 <deleteData createDataKey="product4" stepKey="deleteProduct4"/>
                 <deleteData createDataKey="category" stepKey="deleteCategory"/>
    +            <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="logoutCustomer" />
                 <deleteData createDataKey="customer" stepKey="deleteCustomer"/>
                 <createData entity="DefaultConfigCatalogPrice" stepKey="defaultConfigCatalogPrice"/>
                 <actionGroup ref="DeleteCartPriceRuleByName" stepKey="cleanUpRule">
    @@ -329,7 +334,9 @@
                 <createData entity="CustomerAccountSharingDefault" stepKey="setConfigCustomerAccountDefault"/>
                 <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/>
     
    -            <magentoCron groups="index" stepKey="reindex"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
                 <comment userInput="Adding the comment to replace CliCacheFlushActionGroup action group ('cache:flush' command) for preserving Backward Compatibility" stepKey="flushCache"/>
             </after>
         </test>
    
  • app/code/Magento/Catalog/Test/Mftf/Test/DeleteCategoriesTest.xml+3 1 modified
    @@ -37,7 +37,9 @@
                 <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/>
                 <actionGroup ref="AdminSystemStoreOpenPageActionGroup" stepKey="navigateToStores"/>
                 <actionGroup ref="AdminDeleteMultipleWebsitesActionGroup" stepKey="deleteWebsites"/>
    -            <magentoCron groups="index" stepKey="reindex"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </before>
             <after>
                 <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/>
    
  • app/code/Magento/Catalog/Test/Mftf/Test/DeleteUsedInConfigurableProductAttributeTest.xml+3 1 modified
    @@ -75,7 +75,9 @@
                 <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/>
     
                 <!-- Reindex invalidated indices after product attribute has been created/deleted -->
    -            <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindexInvalidatedIndices">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </after>
             <!-- Go to Stores > Attributes > Products. Search and select the product attribute that was used to create the configurable product-->
             <actionGroup ref="OpenProductAttributeFromSearchResultInGridActionGroup" stepKey="openProductAttributeFromSearchResultInGrid">
    
  • app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CAdminTest.xml+4 1 modified
    @@ -18,13 +18,16 @@
                 <testCaseId value="MAGETWO-87014"/>
                 <group value="pr_exclude"/>
             </annotations>
    +        <before>
    +            <magentoCLI command="config:set checkout/options/enable_guest_checkout_login 1" stepKey="EnablingGuestCheckoutLogin"/>
    +        </before>
             <after>
                 <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutOfAdmin"/>
    +            <magentoCLI command="config:set checkout/options/enable_guest_checkout_login 0" stepKey="DisablingGuestCheckoutLogin"/>
             </after>
     
             <!--Login to Admin Area-->
             <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminArea"/>
    -
             <!--Admin creates product-->
             <!--Create Simple Product-->
             <actionGroup ref="AdminOpenProductIndexPageActionGroup" stepKey="visitAdminProductPageSimple"/>
    
  • app/code/Magento/Catalog/Test/Mftf/Test/ProductAttributeWithoutValueInCompareListTest.xml+3 1 modified
    @@ -46,7 +46,9 @@
                 <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/>
     
                 <!-- Reindex invalidated indices after product attribute has been created/deleted -->
    -            <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindexInvalidatedIndices">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </after>
             <!--Open product page-->
             <amOnPage url="{{StorefrontProductPage.url($$createProductDefault.custom_attributes[url_key]$$)}}" stepKey="goToProductDefaultPage"/>
    
  • app/code/Magento/Catalog/Test/Mftf/Test/SaveProductWithCustomOptionsAdditionalWebsiteTest.xml+8 2 renamed
    @@ -17,6 +17,7 @@
                 <severity value="BLOCKER"/>
                 <testCaseId value="MC-25687"/>
                 <group value="product"/>
    +
             </annotations>
             <before>
                 <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/>
    @@ -34,7 +35,9 @@
                     <argument name="StoreGroup" value="customStoreGroup"/>
                     <argument name="customStore" value="customStore"/>
                 </actionGroup>
    -            <magentoCron groups="index" stepKey="reindex"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
     
                 <actionGroup ref="EnableWebUrlOptionsActionGroup" stepKey="addStoreCodeToUrls"/>
             </before>
    @@ -44,7 +47,9 @@
                 <actionGroup ref="AdminDeleteWebsiteActionGroup" stepKey="deleteTestWebsite">
                     <argument name="websiteName" value="{{customWebsite.name}}"/>
                 </actionGroup>
    -            <magentoCron groups="index" stepKey="reindex"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
                 <actionGroup ref="AdminClearFiltersActionGroup" stepKey="clearFilters"/>
                 <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/>
             </after>
    @@ -100,6 +105,7 @@
             <executeJS function="window.scrollTo({top: {$sectionPosition}-{$floatingHeaderHeight}})" stepKey="scrollToOptions"/>
             <click selector="{{AdminProductCustomizableOptionsSection.customizableOptions}}" stepKey="openCustomOptionsSection2"/>
             <waitForElementVisible selector=".admin__dynamic-rows[data-index='values'] tr.data-row" stepKey="waitForRowsToBeVisible"/>
    +        <waitForPageLoad stepKey="waitForLoadingMaskToDisappear" />
             <seeNumberOfElements selector=".admin__dynamic-rows[data-index='values'] tr.data-row" userInput="3" stepKey="see4RowsOfOptions"/>
         </test>
     </tests>
    
  • app/code/Magento/Catalog/Test/Mftf/Test/SpecialPriceCheckOnWishListPageTest.xml+1 0 modified
    @@ -35,6 +35,7 @@
             <after>
                 <deleteData createDataKey="createFirstSimpleProduct" stepKey="deleteSimpleProduct"/>
                 <deleteData createDataKey="createCategory" stepKey="deleteSimpleCategory"/>
    +            <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="logoutCustomer" />
                 <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/>
             </after>
             <!-- Login into Admin Panel-->
    
  • app/code/Magento/Catalog/Test/Mftf/Test/StoreFrontAddRelatedandUpsellstoCartfromproductpageTest.xml+1 0 modified
    @@ -65,6 +65,7 @@
                 <deleteData createDataKey="productU" stepKey="deleteVirtualProductU"/>
                 <deleteData createDataKey="productV" stepKey="deleteVirtualProductV"/>
                 <deleteData createDataKey="productW" stepKey="deleteVirtualProductW"/>
    +            <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="logoutCustomer" />
                 <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/>
     
             </after>
    
  • app/code/Magento/Catalog/Test/Mftf/Test/StoreFrontAssertProductFinalPriceChangesDynamicallyOnProductPageWithTierPricesConfiguredTest.xml+1 0 modified
    @@ -26,6 +26,7 @@
             <after>
                 <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/>
                 <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="customerLogout"/>
    +            <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="logoutCustomer" />
                 <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/>
                 <actionGroup ref="AdminOpenProductIndexPageActionGroup" stepKey="amOnProductGridPage"/>
                 <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearFilterProduct"/>
    
  • app/code/Magento/Catalog/Test/Mftf/Test/StorefrontCategorySidebarMobileMenuTest.xml+3 1 modified
    @@ -29,7 +29,7 @@
             </before>
             <after>
                 <!-- Reset the window size to its original state -->
    -            <resizeWindow width="1280" height="1024" stepKey="resizeWindowToDesktop"/>
    +            <resizeWindow width="1920" height="1080" stepKey="resizeWindowToDesktop"/>
                 <deleteData createDataKey="createSubCategory" stepKey="deleteSubCategory"/>
                 <deleteData createDataKey="createParentCategory" stepKey="deleteParentCategory"/>
             </after>
    @@ -38,7 +38,9 @@
             <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="openStorefrontPage"/>
     
             <!-- Open the side menu and expand the root category -->
    +        <waitForElementClickable selector="{{StorefrontHeaderSection.mobileMenuToggle}}" stepKey="waitForSideMenuClickable" />
             <click selector="{{StorefrontHeaderSection.mobileMenuToggle}}" stepKey="openSideMenu"/>
    +        <waitForElementClickable selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createParentCategory.name$$)}}" stepKey="waitForCategoryMenuClickable" />
             <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createParentCategory.name$$)}}" stepKey="expandCategoryMenu"/>
     
             <!-- Assert the category expanded successfully -->
    
  • app/code/Magento/Catalog/Test/Mftf/Test/StorefrontConfigurableOptionsThumbImagesTest.xml+3 1 modified
    @@ -166,7 +166,9 @@
                 <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/>
     
                 <!-- Reindex invalidated indices after product attribute has been created/deleted -->
    -           <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindexInvalidatedIndices">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </after>
     
             <!-- Open ConfigProduct in Store Front Page -->
    
  • app/code/Magento/Catalog/Test/Mftf/Test/StorefrontEnsureThatAccordionAnchorIsVisibleOnViewportOnceClickedTest.xml+2 1 modified
    @@ -60,6 +60,7 @@
                 <actionGroup ref="AdminDeleteReviewsByUserNicknameActionGroup" stepKey="deleteCustomerReview"/>
     
                 <!-- Delete customer -->
    +            <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="logoutCustomer" />
                 <actionGroup ref="DeleteCustomerByEmailActionGroup" stepKey="deleteCustomer">
                     <argument name="email" value="{{CustomerEntityOne.email}}"/>
                 </actionGroup>
    @@ -185,6 +186,6 @@
     
             <!-- Scroll so that the description is visible and More info tab is on the upper middle of the page -->
             <scrollTo selector="{{StorefrontProductInfoDetailsSection.detailsTab}}" stepKey="scrollToMoreInfoTab"/>
    -        <resizeWindow width="1280" height="1024" stepKey="resizeWindowToDesktop"/>
    +        <resizeWindow width="1920" height="1080" stepKey="resizeWindowToDesktop"/>
         </test>
     </tests>
    
  • app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductsCompareWithEmptyAttributeTest.xml+5 3 modified
    @@ -35,13 +35,15 @@
                 <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutOfAdmin"/>
     
                 <!-- Reindex invalidated indices after product attribute has been created/deleted -->
    -            <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindexInvalidatedIndices">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </after>
             <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin1"/>
             <amOnPage url="{{AdminProductAttributeSetGridPage.url}}" stepKey="amOnAttributeSetPage"/>
             <click selector="{{AdminProductAttributeSetGridSection.AttributeSetName('Default')}}" stepKey="chooseDefaultAttributeSet"/>
             <waitForPageLoad stepKey="waitForAttributeSetPageLoad"/>
    -        <dragAndDrop selector1="{{UnassignedAttributes.ProductAttributeName('testattribute')}}" selector2="{{Group.FolderName('Product Details')}}" stepKey="moveProductAttributeToGroup"/>
    +        <dragAndDrop selector1="{{UnassignedAttributes.ProductAttributeName('$$createProductAttribute.attribute_code$$')}}" selector2="{{Group.FolderName('Product Details')}}" stepKey="moveProductAttributeToGroup"/>
             <click selector="{{AttributeSetSection.Save}}" stepKey="saveAttributeSet"/>
             <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskToDisappear" />
             <seeElement selector=".message-success" stepKey="assertSuccess"/>
    @@ -81,6 +83,6 @@
                 <argument name="productVar" value="$$createSimpleProduct1$$"/>
             </actionGroup>
             <seeElement selector="//table[@id='product-comparison']/tbody/tr/th/*[contains(text(),'SKU')]" stepKey="seeCompareAttribute1"/>
    -        <dontSeeElement selector="//table[@id='product-comparison']/tbody/tr/th/*[contains(text(),'testattribute')]" stepKey="seeCompareAttribute2"/>
    +        <dontSeeElement selector="//table[@id='product-comparison']/tbody/tr/th/*[contains(text(),'$$createProductAttribute.attribute_code$$')]" stepKey="seeCompareAttribute2"/>
         </test>
     </tests>
    
  • app/code/Magento/Catalog/Test/Mftf/Test/StoreFrontProductsDisplayUsingElasticSearchTest.xml+3 1 modified
    @@ -114,7 +114,9 @@
                     <requiredEntity createDataKey="createCategory1"/>
                 </createData>
     
    -            <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindexInvalidatedIndices">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
                 <comment userInput="Adding the comment to replace CliIndexerReindexActionGroup action group ('indexer:reindex' commands) for preserving Backward Compatibility" stepKey="performReindex"/>
                 <actionGroup ref="CliCacheCleanActionGroup" stepKey="cleanFullPageCache">
                     <argument name="tags" value="full_page"/>
    
  • app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductWithEmptyAttributeTest.xml+5 3 modified
    @@ -32,13 +32,15 @@
                 <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutOfAdmin"/>
     
                 <!-- Reindex invalidated indices after product attribute has been created/deleted -->
    -            <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindexInvalidatedIndices">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </after>
             <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin1"/>
             <amOnPage url="{{AdminProductAttributeSetGridPage.url}}" stepKey="amOnAttributeSetPage"/>
             <click selector="{{AdminProductAttributeSetGridSection.AttributeSetName('Default')}}" stepKey="chooseDefaultAttributeSet"/>
             <waitForPageLoad stepKey="waitForAttributeSetPageLoad"/>
    -        <dragAndDrop selector1="{{UnassignedAttributes.ProductAttributeName('testattribute')}}" selector2="{{Group.FolderName('Product Details')}}" stepKey="moveProductAttributeToGroup"/>
    +        <dragAndDrop selector1="{{UnassignedAttributes.ProductAttributeName('$$createProductAttribute.attribute_code$$')}}" selector2="{{Group.FolderName('Product Details')}}" stepKey="moveProductAttributeToGroup"/>
             <click selector="{{AttributeSetSection.Save}}" stepKey="saveAttributeSet"/>
             <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskToDisappear" />
             <seeElement selector=".message-success" stepKey="assertSuccess"/>
    @@ -49,6 +51,6 @@
             <actionGroup ref="ClearCacheActionGroup" stepKey="clearCache"/>
             <amOnPage url="{{StorefrontProductPage.url(SimpleProduct.urlKey)}}" stepKey="goProductPageOnStorefront"/>
             <waitForPageLoad stepKey="waitForProductPageToLoad"/>
    -        <dontSeeElement selector="//table[@id='product-attribute-specs-table']/tbody/tr/th[contains(text(),'testattribute')]" stepKey="seeAttribute2"/>
    +        <dontSeeElement selector="//table[@id='product-attribute-specs-table']/tbody/tr/th[contains(text(),'$$createProductAttribute.attribute_code$$')]" stepKey="seeAttribute2"/>
         </test>
         </tests>
    
  • app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductCustomOptionsDifferentStoreViewsTest.xml+8 2 modified
    @@ -45,7 +45,9 @@
                 <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createStoreView2">
                     <argument name="customStore" value="customStoreFR"/>
                 </actionGroup>
    -            <magentoCron groups="index" stepKey="reindex"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </before>
     
             <after>
    @@ -67,7 +69,9 @@
                     <argument name="customStore" value="customStoreFR"/>
                 </actionGroup>
                 <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearWebsitesGridFilters"/>
    -            <magentoCron groups="index" stepKey="reindex"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
     
                 <actionGroup ref="AdminOrdersGridClearFiltersActionGroup" stepKey="clearOrdersGridFilter"/>
     
    @@ -97,12 +101,14 @@
             <click selector="{{AdminProductCustomizableOptionsSection.checkSelect('Custom Options 1')}}" stepKey="clickSelect1"/>
             <click selector="{{AdminProductCustomizableOptionsSection.checkDropDown('Custom Options 1')}}" stepKey="clickDropDown1"/>
             <click selector="{{AdminProductCustomizableOptionsSection.clickAddValue('Custom Options 1')}}" stepKey="clickAddValue1"/>
    +        <waitForElementVisible selector="{{AdminProductCustomizableOptionsSection.fillOptionValueTitle('Custom Options 1', '0')}}" stepKey="waitForOptionValueTitle1" />
             <fillField selector="{{AdminProductCustomizableOptionsSection.fillOptionValueTitle('Custom Options 1', '0')}}" userInput="option1" stepKey="fillOptionValueTitle1"/>
             <fillField selector="{{AdminProductCustomizableOptionsSection.fillOptionValuePrice('Custom Options 1', '0')}}" userInput="5" stepKey="fillOptionValuePrice1"/>
     
             <!-- Update Product with Option Value 1 DropDown 1-->
     
             <click selector="{{AdminProductCustomizableOptionsSection.clickAddValue('Custom Options 1')}}" stepKey="clickAddValue2"/>
    +        <waitForElementVisible selector="{{AdminProductCustomizableOptionsSection.fillOptionValueTitle('Custom Options 1', '0')}}" stepKey="waitForOptionValueTitle2" />
             <fillField selector="{{AdminProductCustomizableOptionsSection.fillOptionValueTitle('Custom Options 1', '1')}}" userInput="option2" stepKey="fillOptionValueTitle2"/>
             <fillField selector="{{AdminProductCustomizableOptionsSection.fillOptionValuePrice('Custom Options 1', '1')}}" userInput="50" stepKey="fillOptionValuePrice2"/>
             <selectOption selector="{{AdminProductCustomizableOptionsSection.clickSelectPriceType('Custom Options 1', '1')}}" userInput="percent" stepKey="clickSelectPriceType"/>
    
  • app/code/Magento/Catalog/Test/Mftf/Test/StoreFrontRecentlyViewedAtStoreLevelTest.xml+6 2 modified
    @@ -51,7 +51,9 @@
                 </actionGroup>
                 <!--  Set Stores > Configurations > Catalog > Recently Viewed/Compared Products > Show for Current = store -->
                 <magentoCLI command="config:set {{RecentlyViewedProductScopeStoreGroup.path}} {{RecentlyViewedProductScopeStoreGroup.value}}" stepKey="RecentlyViewedProductScopeStoreGroup"/>
    -            <magentoCron groups="index" stepKey="reindex"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </before>
             <after>
                 <!-- Delete Product and Category -->
    @@ -75,7 +77,9 @@
                 </actionGroup>
                 <!-- Logout Admin -->
                 <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/>
    -            <magentoCron groups="index" stepKey="reindex"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
                 <comment userInput="Adding the comment to replace CliCacheFlushActionGroup action group ('cache:flush' command) for preserving Backward Compatibility" stepKey="flushCacheAfterDeletion"/>
             </after>
             <!--Create widget for recently viewed products-->
    
  • app/code/Magento/Catalog/Test/Mftf/Test/StoreFrontRecentlyViewedAtStoreViewLevelTest.xml+6 2 modified
    @@ -37,7 +37,9 @@
                 <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createStoreViewOne">
                     <argument name="customStore" value="customStoreEN"/>
                 </actionGroup>
    -            <magentoCron stepKey="runCronIndex" groups="index"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="runCronIndex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
     
                 <!--  Set Stores > Configurations > Catalog > Recently Viewed/Compared Products > Show for Current = store view-->
                 <magentoCLI command="config:set {{RecentlyViewedProductScopeStore.path}} {{RecentlyViewedProductScopeStore.value}}" stepKey="RecentlyViewedProductScopeStore"/>
    @@ -66,7 +68,9 @@
     
                 <!-- Logout Admin -->
                 <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/>
    -            <magentoCron groups="index" stepKey="reindex"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
                 <comment userInput="Adding the comment to replace CliCacheFlushActionGroup action group ('cache:flush' command) for preserving Backward Compatibility" stepKey="flushCacheAfterDeletion"/>
             </after>
     
    
  • app/code/Magento/Catalog/Test/Mftf/Test/StorefrontSpecialPriceForDifferentTimezonesForWebsitesTest.xml+1 0 modified
    @@ -31,6 +31,7 @@
             <after>
                 <!--Delete create data-->
                 <deleteData createDataKey="createProduct" stepKey="deleteProduct"/>
    +            <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="logoutCustomer" />
                 <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/>
     
                 <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/>
    
  • app/code/Magento/Catalog/Test/Mftf/Test/StorefrontVerifyCategoryProductAndProductCategoryPartialReindexTest.xml+13 5 modified
    @@ -58,7 +58,9 @@
                     <requiredEntity createDataKey="categoryN"/>
                     <requiredEntity createDataKey="productC"/>
                 </createData>
    -            <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindexInvalidatedIndices">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </before>
             <after>
                 <!-- Change indexers to "Update on Save" mode -->
    @@ -72,7 +74,9 @@
                 <deleteData createDataKey="categoryM" stepKey="deleteCategoryM"/>
                 <deleteData createDataKey="categoryL" stepKey="deleteCategoryL"/>
                 <deleteData createDataKey="categoryK" stepKey="deleteCategoryK"/>
    -            <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindexInvalidatedIndices">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </after>
     
             <!-- Open categories K, L, M, N on Storefront -->
    @@ -139,7 +143,9 @@
             <dontSee userInput="$productB.name$" selector="{{StorefrontCategoryMainSection.productName}}" stepKey="dontSeeProductBInCategoryN"/>
     
             <!-- Run cron -->
    -        <magentoCron groups="index" stepKey="runCronIndex"/>
    +        <actionGroup ref="CliIndexerReindexActionGroup" stepKey="runCronIndex">
    +            <argument name="indices" value=""/>
    +        </actionGroup>
     
             <!-- Open categories K, L, M, N on Storefront in order to make sure that new assignments are applied -->
             <!-- Category K contains only Products A, C -->
    @@ -204,8 +210,10 @@
             <dontSee userInput="$productA.name$" selector="{{StorefrontCategoryMainSection.productName}}" stepKey="dontSeeProductAOnTheCategoryN"/>
             <dontSee userInput="$productB.name$" selector="{{StorefrontCategoryMainSection.productName}}" stepKey="dontSeeProductBOnTheCategoryN"/>
     
    -        <!-- Run Cron once to reindex product changes -->
    -        <magentoCron groups="index" stepKey="runCronIndex2"/>
    +        <!-- Reindex product changes -->
    +        <actionGroup ref="CliIndexerReindexActionGroup" stepKey="runCronIndex2">
    +            <argument name="indices" value=""/>
    +        </actionGroup>
     
             <!-- Open categories K, L, M, N on Storefront in order to make sure that new assignments are applied -->
     
    
  • app/code/Magento/Catalog/Test/Mftf/Test/StorefrontVerifyProductAfterPartialReindexOnSeveralWebsitesTest.xml+3 1 modified
    @@ -67,7 +67,9 @@
                 <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetProductGridColumnsInitial"/>
                 <actionGroup ref="ResetWebUrlOptionsActionGroup" stepKey="resetUrlOption"/>
                 <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/>
    -            <magentoCLI command="indexer:reindex" stepKey="reindex"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
                 <magentoCLI command="cache:clean" stepKey="cleanCacheAfter"/>
             </after>
     
    
  • app/code/Magento/Catalog/Test/Mftf/Test/VerifyCategoryProductAndProductCategoryPartialReindexTest.xml+16 13 modified
    @@ -59,9 +59,11 @@
                     <argument name="categoryName" value="$$categoryN.name$$, $$categoryM.name$$"/>
                 </actionGroup>
     
    -            <wait stepKey="waitBeforeRunCronIndex" time="60"/>
    -            <magentoCLI stepKey="runCronIndex" command="cron:run --group=index"/>
    -            <wait stepKey="waitAfterRunCronIndex" time="120"/>
    +            <comment userInput="BIC workaround" stepKey="waitBeforeRunCronIndex"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="runCronIndex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
    +            <comment userInput="BIC workaround" stepKey="waitAfterRunCronIndex"/>
             </before>
             <after>
                 <!-- Change "Category Products" and "Product Categories" indexers to "Update on Save" mode -->
    @@ -147,11 +149,11 @@
             <amOnPage url="{{StorefrontCategoryPage.url($$categoryK.custom_attributes[url_key]$$/$$categoryN.custom_attributes[url_key]$$)}}" stepKey="amOnCategoryN"/>
             <see userInput="$$productC.name$$" selector="{{StorefrontCategoryMainSection.productName}}" stepKey="seeProductInCategoryN"/>
     
    -        <!-- Run cron -->
    -        <wait stepKey="waitBeforeRunMagentoCron" time="60"/>
    -        <magentoCLI stepKey="runMagentoCron" command="cron:run --group=index"/>
    -
    -        <wait stepKey="waitAfterRunMagentoCron" time="90"/>
    +        <comment userInput="BIC workaround" stepKey="waitBeforeRunMagentoCron"/>
    +        <actionGroup ref="CliIndexerReindexActionGroup" stepKey="runMagentoCron">
    +            <argument name="indices" value=""/>
    +        </actionGroup>
    +        <comment userInput="BIC workaround" stepKey="waitAfterRunMagentoCron"/>
     
             <!-- Open categories K, L, M, N on Storefront in order to make sure that new assigments are applied -->
             <!-- Category K contains only Products A, C -->
    @@ -214,11 +216,12 @@
             <amOnPage url="{{StorefrontCategoryPage.url($$categoryK.custom_attributes[url_key]$$/$$categoryN.custom_attributes[url_key]$$)}}" stepKey="onStorefrontCategoryN"/>
             <see userInput="$$productC.name$$" selector="{{StorefrontCategoryMainSection.productName}}" stepKey="productCOnCategoryN"/>
     
    -        <!-- Run Cron once to reindex product changes -->
    -        <wait stepKey="waitBeforeRunCronIndexAfterProductAssignToCategory" time="60"/>
    -        <magentoCLI stepKey="runCronIndexAfterProductAssignToCategory" command="cron:run --group=index"/>
    -
    -        <wait stepKey="waitAfterRunCronIndexAfterProductAssignToCategory" time="90"/>
    +        <!-- Reindex -->
    +        <comment userInput="BIC workaround" stepKey="waitBeforeRunCronIndexAfterProductAssignToCategory"/>
    +        <actionGroup ref="CliIndexerReindexActionGroup" stepKey="runCronIndexAfterProductAssignToCategory">
    +            <argument name="indices" value=""/>
    +        </actionGroup>
    +        <comment userInput="BIC workaround" stepKey="waitAfterRunCronIndexAfterProductAssignToCategory"/>
     
             <!-- Open categories K, L, M, N on Storefront in order to make sure that new assigments are applied -->
     
    
  • app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/NewActionTest.php+77 13 modified
    @@ -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,78 @@ 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()
    +    /**
    +     * Test execute method input validation.
    +     *
    +     * @param string $value
    +     * @param bool $exceptionThrown
    +     * @dataProvider validationCases
    +     */
    +    public function testExecute(string $value, bool $exceptionThrown): void
    +    {
    +        if ($exceptionThrown) {
    +            $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());
    +        } else {
    +            $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));
    +        }
    +    }
    +
    +    /**
    +     * Validation cases.
    +     *
    +     * @return array
    +     */
    +    public function validationCases(): array
         {
    -        $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();
    +        return [
    +            'execute-with-exception' => ['simple\' and true()]|*[self%3a%3ahandle%20or%20self%3a%3alayout',true],
    +            'execute-without-exception' => ['catalog_product_new',false]
    +        ];
         }
     }
    
  • app/code/Magento/Catalog/Test/Unit/Pricing/Render/FinalPriceBoxTest.php+33 0 modified
    @@ -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);
         }
    
  • app/code/Magento/CatalogUrlRewrite/composer.json+17 15 modified
    @@ -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.6-p4",
         "require": {
             "php": "~8.1.0||~8.2.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 @@
             }
         }
     }
    +
    
  • app/code/Magento/CatalogUrlRewriteGraphQl/composer.json+14 12 modified
    @@ -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.4",
         "require": {
             "php": "~8.1.0||~8.2.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 @@
             }
         }
     }
    +
    
  • app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/AdminRewriteProductWithTwoStoreTest.xml+6 2 modified
    @@ -20,7 +20,9 @@
                 <comment userInput="Adding the comment to replace CliCacheFlushActionGroup action group ('cache:flush' command) for preserving Backward Compatibility" stepKey="flushCache"/>
                 <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/>
                 <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createStoreView" />
    -            <magentoCron groups="index" stepKey="reindex"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
                 <createData entity="_defaultCategoryDifferentUrlStore" stepKey="defaultCategory"/>
                 <createData entity="SimpleSubCategoryDifferentUrlStore" stepKey="subCategory">
                     <requiredEntity createDataKey="defaultCategory"/>
    @@ -36,7 +38,9 @@
                 <deleteData createDataKey="simpleProduct" stepKey="deleteSimpleProduct"/>
                 <deleteData createDataKey="defaultCategory" stepKey="deleteNewRootCategory"/>
                 <magentoCLI command="config:set {{DisableCategoriesPathProductUrls.path}} {{DisableCategoriesPathProductUrls.value}}" stepKey="disableUseCategoriesPath"/>
    -            <magentoCron groups="index" stepKey="reindex"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
                 <comment userInput="Adding the comment to replace CliCacheFlushActionGroup action group ('cache:flush' command) for preserving Backward Compatibility" stepKey="flushCache"/>
             </after>
     
    
  • app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/AdminUrlForProductRewrittenCorrectlyTest.xml+3 2 modified
    @@ -76,8 +76,9 @@
                 <argument name="consumerName" value="{{AdminProductAttributeWebsiteUpdateConsumerData.consumerName}}"/>
                 <argument name="maxMessages" value="{{AdminProductAttributeWebsiteUpdateConsumerData.messageLimit}}"/>
             </actionGroup>
    -        <!-- Run cron -->
    -        <magentoCLI command="cron:run --group=index" stepKey="runCron"/>
    +        <actionGroup ref="CliIndexerReindexActionGroup" stepKey="runCron">
    +            <argument name="indices" value=""/>
    +        </actionGroup>
     
             <!--Got to Store front product page and check url-->
             <amOnPage url="{{StorefrontProductPage.url($$createProduct.custom_attributes[url_key]$$-new)}}" stepKey="navigateToSimpleProductPage"/>
    
  • app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/RewriteStoreLevelUrlKeyOfChildCategoryTest.xml+6 2 modified
    @@ -20,7 +20,9 @@
             <before>
                 <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/>
                 <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createStoreView" />
    -            <magentoCron groups="index" stepKey="reindex"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
     
                 <createData entity="_defaultCategory" stepKey="defaultCategory"/>
                 <createData entity="SubCategoryWithParent" stepKey="subCategory">
    @@ -58,7 +60,9 @@
     
             <after>
                 <actionGroup ref="AdminDeleteStoreViewActionGroup" stepKey="deleteStoreView"/>
    -            <magentoCron groups="index" stepKey="reindex"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
                 <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/>
     
                 <deleteData createDataKey="subCategory" stepKey="deleteSubCategory"/>
    
  • app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/StorefrontCheckCategoryUrlPathForCustomStoreAfterChangingHierarchyTest.xml+6 2 modified
    @@ -34,7 +34,9 @@
                 <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createEnStoreView">
                     <argument name="customStore" value="customStoreEN"/>
                 </actionGroup>
    -            <magentoCron groups="index" stepKey="reindex"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </before>
             <after>
                 <!-- Delete categories -->
    @@ -47,7 +49,9 @@
                 </actionGroup>
                 <!-- Clear grid filters -->
                 <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearStoreFilters"/>
    -            <magentoCron groups="index" stepKey="reindex"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
                 <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/>
             </after>
     
    
  • app/code/Magento/Catalog/view/adminhtml/web/js/category-tree.js+2 3 modified
    @@ -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
                     },
    
  • app/code/Magento/CatalogWidget/composer.json+17 15 modified
    @@ -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.6",
         "require": {
             "php": "~8.1.0||~8.2.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 @@
             }
         }
     }
    +
    
  • app/code/Magento/Checkout/Model/ShippingInformationManagement.php+20 14 modified
    @@ -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();
    
  • app/code/Magento/Checkout/Observer/CspPolicyObserver.php+69 0 added
    @@ -0,0 +1,69 @@
    +<?php
    +/**
    + * Copyright © Magento, Inc. All rights reserved.
    + * See COPYING.txt for license details.
    + */
    +declare(strict_types=1);
    +
    +namespace Magento\Checkout\Observer;
    +
    +use Magento\Csp\Model\Collector\DynamicCollector;
    +use Magento\Csp\Model\Policy\FetchPolicy;
    +use Magento\Framework\Event\Observer;
    +use Magento\Framework\Event\ObserverInterface;
    +use Magento\Framework\Translate\InlineInterface;
    +
    +/**
    + * Observer for adding CSP policy for inline translation
    + */
    +class CspPolicyObserver implements ObserverInterface
    +{
    +    /**
    +     * @var InlineInterface
    +     */
    +    private InlineInterface $inlineTranslate;
    +
    +    /**
    +     * @var DynamicCollector
    +     */
    +    private DynamicCollector $dynamicCollector;
    +
    +    /**
    +     * @param InlineInterface $inlineTranslate
    +     * @param DynamicCollector $dynamicCollector
    +     */
    +    public function __construct(InlineInterface $inlineTranslate, DynamicCollector $dynamicCollector)
    +    {
    +        $this->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);
    +        }
    +    }
    +}
    
  • app/code/Magento/Checkout/Test/Mftf/ActionGroup/AdminDisableGuestCheckoutActionGroup.xml+18 0 added
    @@ -0,0 +1,18 @@
    +<?xml version="1.0" encoding="UTF-8"?>
    +<!--
    + /**
    +  * Copyright © Magento, Inc. All rights reserved.
    +  * See COPYING.txt for license details.
    +  */
    +-->
    +
    +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    +              xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd">
    +    <actionGroup name="AdminDisableGuestCheckoutActionGroup">
    +        <annotations>
    +            <description>Runs bin/magento command to disable guest checkout</description>
    +        </annotations>
    +        <magentoCLI command="config:set checkout/options/guest_checkout 0" stepKey="disableGuestCheckout"/>
    +        <magentoCLI command="cache:flush" stepKey="cleanCache"/>
    +    </actionGroup>
    +</actionGroups>
    
  • app/code/Magento/Checkout/Test/Mftf/ActionGroup/AdminEnableGuestCheckoutActionGroup.xml+18 0 added
    @@ -0,0 +1,18 @@
    +<?xml version="1.0" encoding="UTF-8"?>
    +<!--
    + /**
    +  * Copyright © Magento, Inc. All rights reserved.
    +  * See COPYING.txt for license details.
    +  */
    +-->
    +
    +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    +              xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd">
    +    <actionGroup name="AdminEnableGuestCheckoutActionGroup">
    +        <annotations>
    +            <description>Runs bin/magento command to enable guest checkout</description>
    +        </annotations>
    +        <magentoCLI command="config:set checkout/options/guest_checkout 1" stepKey="enableGuestCheckout"/>
    +        <magentoCLI command="cache:flush" stepKey="cleanCache"/>
    +    </actionGroup>
    +</actionGroups>
    
  • app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertShoppingCartIsEmptyActionGroup.xml+1 1 modified
    @@ -15,6 +15,6 @@
     
             <amOnPage url="{{CheckoutCartPage.url}}" stepKey="amOnPageShoppingCart"/>
             <waitForPageLoad stepKey="waitForCheckoutPageLoad"/>
    -        <see userInput="You have no items in your shopping cart." stepKey="seeNoItemsInShoppingCart"/>
    +        <waitForText userInput="You have no items in your shopping cart." stepKey="seeNoItemsInShoppingCart"/>
         </actionGroup>
     </actionGroups>
    
  • app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertStorefrontNotCalculatedValueInShippingTotalInOrderSummaryActionGroup.xml+1 0 modified
    @@ -16,6 +16,7 @@
             <arguments>
                 <argument name="value" defaultValue="Not yet calculated" type="string"/>
             </arguments>
    +        <waitForPageLoad time="60" stepKey="waitForThePageToLoadInSixtySeconds"/>
             <waitForElementVisible selector="{{CheckoutOrderSummarySection.shippingTotalNotYetCalculated}}" time="30" stepKey="waitForShippingTotalToBeVisible"/>
             <see selector="{{CheckoutOrderSummarySection.shippingTotalNotYetCalculated}}" userInput="{{value}}" stepKey="assertShippingTotalIsNotYetCalculated"/>
         </actionGroup>
    
  • app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertStorefrontOrderCannotBePlacedActionGroup.xml+1 0 modified
    @@ -18,6 +18,7 @@
             </arguments>
             <waitForElement selector="{{CheckoutPaymentSection.placeOrder}}" time="30" stepKey="waitForPlaceOrderButton"/>
             <click selector="{{CheckoutPaymentSection.placeOrderWithoutTimeout}}" stepKey="clickPlaceOrder"/>
    +        <wait time="30" stepKey="waitForThirtySeconds"/>
             <waitForElement selector="{{CheckoutCartMessageSection.errorMessage}}" time="30" stepKey="waitForErrorMessage"/>
             <see selector="{{CheckoutCartMessageSection.errorMessage}}" userInput="{{error}}" stepKey="assertErrorMessage"/>
         </actionGroup>
    
  • app/code/Magento/Checkout/Test/Mftf/ActionGroup/FillGuestCheckoutShippingAddressFormActionGroup.xml+1 0 modified
    @@ -13,6 +13,7 @@
                 <argument name="customer" defaultValue="Simple_US_Customer" type="entity"/>
                 <argument name="customerAddress" defaultValue="US_Address_TX" type="entity"/>
             </arguments>
    +        <waitForElementVisible selector="{{CheckoutShippingSection.emailAddress}}" stepKey="waitForCustomerEmailField" />
             <fillField selector="{{CheckoutShippingSection.emailAddress}}" userInput="{{customer.email}}" stepKey="setCustomerEmail"/>
             <fillField selector="{{CheckoutShippingSection.firstName}}" userInput="{{customer.firstname}}" stepKey="SetCustomerFirstName"/>
             <fillField selector="{{CheckoutShippingSection.lastName}}" userInput="{{customer.lastname}}" stepKey="SetCustomerLastName"/>
    
  • app/code/Magento/Checkout/Test/Mftf/ActionGroup/GuestCheckoutFillingShippingSectionActionGroup.xml+1 1 modified
    @@ -36,6 +36,6 @@
             <click selector="{{CheckoutShippingSection.next}}" stepKey="clickNext"/>
             <waitForPageLoad stepKey="waitForPaymentLoading"/>
             <waitForElementVisible selector="{{CheckoutPaymentSection.paymentSectionTitle}}" time="30" stepKey="waitForPaymentSectionLoaded"/>
    -        <seeInCurrentUrl url="{{CheckoutPage.url}}/#payment" stepKey="assertCheckoutPaymentUrl"/>
    +        <seeCurrentUrlMatches regex="~/checkout/?#payment~" stepKey="assertCheckoutPaymentUrl"/>
         </actionGroup>
     </actionGroups>
    
  • app/code/Magento/Checkout/Test/Mftf/ActionGroup/GuestCheckoutFillingShippingSectionUnavailablePaymentActionGroup.xml+3 1 modified
    @@ -26,10 +26,12 @@
             <fillField selector="{{CheckoutShippingSection.postcode}}" userInput="{{customerAddressVar.postcode}}" stepKey="enterPostcode"/>
             <fillField selector="{{CheckoutShippingSection.telephone}}" userInput="{{customerAddressVar.telephone}}" stepKey="enterTelephone"/>
             <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask"/>
    +
    +        <waitForElementClickable selector="{{CheckoutShippingSection.firstShippingMethod}}" stepKey="waitForFirstShippingMethodClickable" />
             <click selector="{{CheckoutShippingSection.firstShippingMethod}}" stepKey="selectFirstShippingMethod"/>
             <waitForElement selector="{{CheckoutShippingSection.next}}" time="30" stepKey="waitForNextButton"/>
             <click selector="{{CheckoutShippingSection.next}}" stepKey="clickNext"/>
    -        <seeInCurrentUrl url="{{CheckoutPage.url}}/#payment" stepKey="assertCheckoutPaymentUrl"/>
    +        <seeCurrentUrlMatches regex="~/checkout/?#payment~" stepKey="assertCheckoutPaymentUrl"/>
             <waitForElementVisible selector="{{CheckoutPaymentSection.noQuotes}}" stepKey="waitMessage"/>
             <see userInput="No Payment method available." stepKey="checkMessage"/>
         </actionGroup>
    
  • app/code/Magento/Checkout/Test/Mftf/ActionGroup/GuestCheckoutFillingShippingSectionWithoutRegionActionGroup.xml+2 1 modified
    @@ -26,10 +26,11 @@
             <selectOption selector="{{CheckoutShippingSection.country}}" userInput="{{customerAddressVar.country_id}}" stepKey="enterCountry"/>
             <fillField selector="{{CheckoutShippingSection.telephone}}" userInput="{{customerAddressVar.telephone}}" stepKey="enterTelephone"/>
             <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask"/>
    +        <waitForElementClickable selector="{{CheckoutShippingSection.firstShippingMethod}}" stepKey="waitForFirstShippingMethodClickable" />
             <click selector="{{CheckoutShippingSection.firstShippingMethod}}" stepKey="selectFirstShippingMethod"/>
             <waitForElement selector="{{CheckoutShippingSection.next}}" time="30" stepKey="waitForNextButton"/>
             <click selector="{{CheckoutShippingSection.next}}" stepKey="clickNext"/>
             <waitForElement selector="{{CheckoutPaymentSection.paymentSectionTitle}}" time="30" stepKey="waitForPaymentSectionLoaded"/>
    -        <seeInCurrentUrl url="{{CheckoutPage.url}}/#payment" stepKey="assertCheckoutPaymentUrl"/>
    +        <seeCurrentUrlMatches regex="~/checkout/?#payment~" stepKey="assertCheckoutPaymentUrl"/>
         </actionGroup>
     </actionGroups>
    
  • app/code/Magento/Checkout/Test/Mftf/ActionGroup/GuestCheckoutFillingShippingWithMultipleStreetLinesSectionActionGroup.xml+1 1 modified
    @@ -37,6 +37,6 @@
             <click selector="{{CheckoutShippingSection.next}}" stepKey="clickNext"/>
             <waitForPageLoad stepKey="waitForPaymentLoading"/>
             <waitForElementVisible selector="{{CheckoutPaymentSection.paymentSectionTitle}}" time="30" stepKey="waitForPaymentSectionLoaded"/>
    -        <seeInCurrentUrl url="{{CheckoutPage.url}}/#payment" stepKey="assertCheckoutPaymentUrl"/>
    +        <seeCurrentUrlMatches regex="~/checkout/?#payment~" stepKey="assertCheckoutPaymentUrl"/>
         </actionGroup>
     </actionGroups>
    
  • app/code/Magento/Checkout/Test/Mftf/ActionGroup/GuestCheckoutFillNewBillingAddressActionGroup.xml+1 0 modified
    @@ -18,6 +18,7 @@
                 <argument name="customerAddressVar"/>
             </arguments>
     
    +        <waitForElementVisible selector="{{CheckoutShippingSection.emailAddress}}" stepKey="waitForEmailField" />
             <fillField selector="{{CheckoutShippingSection.email}}" userInput="{{customerVar.email}}" stepKey="enterEmail"/>
             <waitForPageLoad stepKey="waitForLoading3"/>
             <fillField selector="{{CheckoutPaymentSection.guestFirstName}}" userInput="{{customerVar.firstname}}" stepKey="enterFirstName"/>
    
  • app/code/Magento/Checkout/Test/Mftf/ActionGroup/GuestCheckoutFillNewShippingAddressActionGroup.xml+2 1 modified
    @@ -17,10 +17,11 @@
                 <argument name="address" type="entity"/>
             </arguments>
     
    +        <waitForElementVisible selector="{{CheckoutShippingSection.emailAddress}}" stepKey="waitForEmailFieldVisible" />
             <fillField selector="{{CheckoutShippingSection.email}}" userInput="{{customer.email}}" stepKey="fillEmailField"/>
             <fillField selector="{{CheckoutShippingSection.firstName}}" userInput="{{customer.firstName}}" stepKey="fillFirstName"/>
             <fillField selector="{{CheckoutShippingSection.lastName}}" userInput="{{customer.lastName}}" stepKey="fillLastName"/>
    -        <fillField selector="{{CheckoutShippingSection.street}}" userInput="{{address.street}}" stepKey="fillStreet"/>
    +        <fillField selector="{{CheckoutShippingSection.street}}" userInput="{{address.street[0]}}" stepKey="fillStreet"/>
             <fillField selector="{{CheckoutShippingSection.city}}" userInput="{{address.city}}" stepKey="fillCity"/>
             <selectOption selector="{{CheckoutShippingSection.region}}" userInput="{{address.state}}" stepKey="selectRegion"/>
             <fillField selector="{{CheckoutShippingSection.postcode}}" userInput="{{address.postcode}}" stepKey="fillZipCode"/>
    
  • app/code/Magento/Checkout/Test/Mftf/ActionGroup/LoggedInUserCheckoutAddNewShippingSectionWithoutRegionActionGroup.xml+2 1 modified
    @@ -26,10 +26,11 @@
             <fillField selector="{{CheckoutShippingSection.telephone}}" userInput="{{customerAddressVar.telephone}}" stepKey="enterTelephone"/>
             <click selector="{{CheckoutShippingSection.saveAddress}}" stepKey="clickSaveAddress"/>
             <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask"/>
    +        <waitForElementClickable selector="{{CheckoutShippingSection.firstShippingMethod}}" stepKey="waitForFirstShippingMethodClickable" />
             <click selector="{{CheckoutShippingSection.firstShippingMethod}}" stepKey="selectFirstShippingMethod"/>
             <waitForElement selector="{{CheckoutShippingSection.next}}" time="30" stepKey="waitForNextButton"/>
             <click selector="{{CheckoutShippingSection.next}}" stepKey="clickNext"/>
             <waitForElement selector="{{CheckoutPaymentSection.paymentSectionTitle}}" time="30" stepKey="waitForPaymentSectionLoaded"/>
    -        <seeInCurrentUrl url="{{CheckoutPage.url}}/#payment" stepKey="assertCheckoutPaymentUrl"/>
    +        <seeCurrentUrlMatches regex="~/checkout/?#payment~" stepKey="assertCheckoutPaymentUrl"/>
         </actionGroup>
     </actionGroups>
    
  • app/code/Magento/Checkout/Test/Mftf/ActionGroup/LoggedInUserCheckoutFillingShippingSectionActionGroup.xml+2 1 modified
    @@ -25,11 +25,12 @@
             <fillField selector="{{CheckoutShippingSection.postcode}}" userInput="{{customerAddressVar.postcode}}" stepKey="enterPostcode"/>
             <fillField selector="{{CheckoutShippingSection.telephone}}" userInput="{{customerAddressVar.telephone}}" stepKey="enterTelephone"/>
             <waitForPageLoad stepKey="waitForLoadingMask"/>
    +        <waitForElementClickable selector="{{CheckoutShippingSection.firstShippingMethod}}" stepKey="waitForFirstShippingMethodClickable" />
             <click selector="{{CheckoutShippingSection.firstShippingMethod}}" stepKey="selectFirstShippingMethod"/>
             <waitForElementVisible selector="{{CheckoutShippingSection.next}}" time="30" stepKey="waitForNextButton"/>
             <waitForPageLoad stepKey="waitForShippingLoadingMask"/>
             <click selector="{{CheckoutShippingSection.next}}" stepKey="clickNext"/>
             <waitForElementVisible selector="{{CheckoutPaymentSection.paymentSectionTitle}}" time="30" stepKey="waitForPaymentSectionLoaded"/>
    -        <seeInCurrentUrl url="{{CheckoutPage.url}}/#payment" stepKey="assertCheckoutPaymentUrl"/>
    +        <seeCurrentUrlMatches regex="~/checkout/?#payment~" stepKey="assertCheckoutPaymentUrl"/>
         </actionGroup>
     </actionGroups>
    
  • app/code/Magento/Checkout/Test/Mftf/ActionGroup/LoginAsCustomerOnCheckoutPageActionGroup.xml+2 1 modified
    @@ -17,7 +17,8 @@
             </arguments>
     
             <waitForPageLoad stepKey="waitForCheckoutShippingSectionToLoad"/>
    -        <fillField selector="{{CheckoutShippingSection.email}}" userInput="{{customer.email}}" stepKey="fillEmailField"/>
    +        <waitForElementVisible selector="{{CheckoutShippingSection.emailAddress}}" stepKey="waitForEmailFieldVisible"/>
    +        <fillField selector="{{CheckoutShippingSection.emailAddress}}" userInput="{{customer.email}}" stepKey="fillEmailField"/>
             <waitForPageLoad stepKey="waitForLoadingMaskToDisappear"/>
             <waitForElementVisible selector="{{CheckoutShippingSection.password}}" stepKey="waitForElementVisible"/>
             <fillField selector="{{CheckoutShippingSection.password}}" userInput="{{customer.password}}" stepKey="fillPasswordField"/>
    
  • app/code/Magento/Checkout/Test/Mftf/ActionGroup/PlaceOrderWithLoggedUserActionGroup.xml+1 1 modified
    @@ -25,7 +25,7 @@
             <waitForElement selector="{{CheckoutShippingSection.next}}" stepKey="waitForNextButton"/>
             <click selector="{{CheckoutShippingSection.next}}" stepKey="clickNext"/>
             <waitForElement selector="{{CheckoutPaymentSection.paymentSectionTitle}}" stepKey="waitForPaymentSectionLoaded"/>
    -        <seeInCurrentUrl url="{{CheckoutPage.url}}/#payment" stepKey="assertCheckoutPaymentUrl"/>
    +        <seeCurrentUrlMatches regex="~/checkout/?#payment~" stepKey="assertCheckoutPaymentUrl"/>
             <waitForElementVisible selector="{{CheckoutPaymentSection.placeOrder}}" stepKey="waitForPlaceOrderButton"/>
             <click selector="{{CheckoutPaymentSection.placeOrder}}" stepKey="clickPlaceOrder"/>
             <see selector="{{CheckoutSuccessMainSection.successTitle}}" userInput="Thank you for your purchase!" stepKey="waitForLoadSuccessPage"/>
    
a3c6d6e5e95e

Magento Release 2.4.5-p8

https://github.com/magento/magento2magento packaging serviceJun 6, 2024via ghsa
300 files changed · +6459 2332
  • app/code/Magento/AdminAdobeIms/composer.json+21 19 modified
    @@ -1,33 +1,34 @@
     {
         "name": "magento/module-admin-adobe-ims",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.0-p2",
         "require": {
             "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    -        "magento/module-adobe-ims": "*",
    -        "magento/module-adobe-ims-api": "*",
    -        "magento/module-config": "*",
    -        "magento/module-backend": "*",
    -        "magento/module-user": "*",
    -        "magento/module-captcha": "*",
    -        "magento/module-authorization": "*",
    -        "magento/module-store": "*",
    -        "magento/module-email": "*",
    -        "magento/module-integration": "*",
    -        "magento/module-jwt-user-token": "*",
    -        "magento/module-security": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-adobe-ims": "2.1.*",
    +        "magento/module-adobe-ims-api": "2.1.*",
    +        "magento/module-config": "101.2.*",
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-user": "101.2.*",
    +        "magento/module-captcha": "100.4.*",
    +        "magento/module-authorization": "100.4.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/module-email": "101.1.*",
    +        "magento/module-integration": "100.4.*",
    +        "magento/module-jwt-user-token": "100.4.*",
    +        "magento/module-security": "100.4.*"
         },
         "suggest": {
    -        "magento/module-theme": "*"
    +        "magento/module-theme": "101.1.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -37,3 +38,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/AdminAdobeIms/Controller/Adminhtml/OAuth/ImsCallback.php+31 0 modified
    @@ -21,7 +21,16 @@
     use Magento\Backend\Model\View\Result\Redirect;
     use Magento\Framework\App\Action\HttpGetActionInterface;
     use Magento\Framework\Exception\AuthenticationException;
    +use Magento\Framework\App\ActionInterface;
    +use Magento\Framework\App\RequestInterface;
    +use Magento\Framework\App\ResponseInterface;
     
    +/**
    + * Class ImsCallback is responsible to get the Access Token, User Profile,
    + * check if the assigned organization is valid, And Check if user exists and then do the login
    + *
    + * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
    + */
     class ImsCallback extends Auth implements HttpGetActionInterface
     {
         public const ACTION_NAME = 'imscallback';
    @@ -75,6 +84,28 @@ public function __construct(
             $this->logger = $logger;
         }
     
    +    /**
    +     * Validate IMS state is valid
    +     *
    +     * @param RequestInterface $request
    +     * @return ResponseInterface
    +     */
    +    public function dispatch(RequestInterface $request)
    +    {
    +        $request->setParam('form_key', $request->getParam('state', null));
    +        if (!$this->_formKeyValidator->validate($request)) {
    +            $this->logger->critical(__('Invalid state returned in callback from IMS.'));
    +            $this->imsErrorMessage(
    +                'Error signing in',
    +                'Something went wrong and we could not sign you in. ' .
    +                'Please try again or contact your administrator.'
    +            );
    +            $this->_actionFlag->set('', ActionInterface::FLAG_NO_DISPATCH, true);
    +            return $this->_redirect($this->_helper->getHomePageUrl());
    +        }
    +        return parent::dispatch($request);
    +    }
    +
         /**
          * Execute AdobeIMS callback
          *
    
  • app/code/Magento/AdminAdobeIms/Controller/Adminhtml/OAuth/ImsReauthCallback.php+18 0 modified
    @@ -21,6 +21,8 @@
     use Magento\Framework\Controller\Result\Raw;
     use Magento\Framework\Controller\ResultFactory;
     use Magento\Framework\Exception\AuthenticationException;
    +use Magento\Framework\App\RequestInterface;
    +use Magento\Framework\Exception\NotFoundException;
     
     class ImsReauthCallback extends Auth implements HttpGetActionInterface
     {
    @@ -111,6 +113,7 @@ public function execute(): ResultInterface
             }
     
             try {
    +            $this->validateStateKey($this->getRequest());
                 $code = $this->getRequest()->getParam('code');
     
                 if ($code === null) {
    @@ -149,4 +152,19 @@ public function execute(): ResultInterface
     
             return $resultRaw;
         }
    +
    +    /**
    +     * Validate IMS state is valid
    +     *
    +     * @param RequestInterface $request
    +     * @return void
    +     * @throws NotFoundException
    +     */
    +    private function validateStateKey(RequestInterface $request): void
    +    {
    +        $request->setParam('form_key', $request->getParam('state', null));
    +        if (!$this->_formKeyValidator->validate($request)) {
    +            throw new NotFoundException(__('Invalid state returned from IMS'));
    +        }
    +    }
     }
    
  • app/code/Magento/AdminAdobeIms/etc/config.xml+4 4 modified
    @@ -11,8 +11,8 @@
                 <integration>
                     <admin_enabled>0</admin_enabled>
                     <admin>
    -                    <auth_url_pattern><![CDATA[https://ims-na1.adobelogin.com/ims/authorize/v2?client_id=#{client_id}&redirect_uri=#{redirect_uri}&locale=#{locale}&scope=#{scope}&response_type=code]]></auth_url_pattern>
    -                    <reauth_url_pattern><![CDATA[https://ims-na1.adobelogin.com/ims/authorize/v2?client_id=#{client_id}&redirect_uri=#{redirect_uri}&locale=#{locale}&scope=#{scope}&response_type=code&reauth=check]]></reauth_url_pattern>
    +                    <auth_url_pattern><![CDATA[https://ims-na1.adobelogin.com/ims/authorize/v2?client_id=#{client_id}&redirect_uri=#{redirect_uri}&locale=#{locale}&scope=#{scope}&state=#{state}&response_type=code]]></auth_url_pattern>
    +                    <reauth_url_pattern><![CDATA[https://ims-na1.adobelogin.com/ims/authorize/v2?client_id=#{client_id}&redirect_uri=#{redirect_uri}&locale=#{locale}&scope=#{scope}&state=#{state}&response_type=code&reauth=check]]></reauth_url_pattern>
                         <scopes>
                             <AdobeID>AdobeID</AdobeID>
                             <openid>openid</openid>
    @@ -27,9 +27,9 @@
                     <token_url>https://ims-na1.adobelogin.com/ims/token</token_url>
                     <profile_url><![CDATA[https://ims-na1.adobelogin.com/ims/profile/v1?client_id=#{client_id}]]></profile_url>
                     <organization_membership_url><![CDATA[https://graph.identity.adobe.com/#{org_id}@AdobeOrg/membership]]></organization_membership_url>
    -                <admin_logout_url><![CDATA[https://ims-na1.adobelogin.com/ims/logout/v1?access_token=#{access_token}&client_id=#{client_id}&client_secret=#{client_secret}]]></admin_logout_url>
    +                <admin_logout_url><![CDATA[https://ims-na1.adobelogin.com/ims/logout/v1]]></admin_logout_url>
                     <certificate_path><![CDATA[https://static.adobelogin.com/keys/prod/]]></certificate_path>
    -                <validate_token_url><![CDATA[https://ims-na1.adobelogin.com/ims/validate_token/v1?token=#{token}&client_id=#{client_id}&type=#{token_type}]]></validate_token_url>
    +                <validate_token_url><![CDATA[https://ims-na1.adobelogin.com/ims/validate_token/v1]]></validate_token_url>
                 </integration>
                 <email>
                     <header_template>admin_adobe_ims_email_header_template</header_template>
    
  • app/code/Magento/AdminAdobeIms/etc/di.xml+6 1 modified
    @@ -67,7 +67,6 @@
     
         <preference for="Magento\AdminAdobeIms\Api\TokenReaderInterface" type="Magento\AdminAdobeIms\Model\TokenReader"/>
     
    -
         <type name="Magento\Integration\Model\AdminTokenService">
             <plugin name="admin_adobe_ims_admin_token_plugin"
                     type="Magento\AdminAdobeIms\Plugin\AdminTokenPlugin" />
    @@ -77,4 +76,10 @@
             <plugin name="admin_adobe_ims_backend_auth_session"
                     type="Magento\AdminAdobeIms\Plugin\BackendAuthSessionPlugin"/>
         </type>
    +
    +    <type name="Magento\AdminAdobeIms\Service\ImsConfig">
    +        <arguments>
    +            <argument name="formKey" xsi:type="object">Magento\Framework\Data\Form\FormKey\Proxy</argument>
    +        </arguments>
    +    </type>
     </config>
    
  • app/code/Magento/AdminAdobeIms/i18n/en_US.csv+66 0 added
    @@ -0,0 +1,66 @@
    +"Admin Adobe IMS integration is disabled","Admin Adobe IMS integration is disabled"
    +"Admin Adobe IMS integration is enabled","Admin Adobe IMS integration is enabled"
    +"The Client ID, Client Secret, Organization ID and 2FA are required when enabling the Admin Adobe IMS Module","The Client ID, Client Secret, Organization ID and 2FA are required when enabling the Admin Adobe IMS Module"
    +"Module is disabled","Module is disabled"
    +"Admin Adobe IMS integration is %1","Admin Adobe IMS integration is %1"
    +"Invalid state returned in callback from IMS.","Invalid state returned in callback from IMS."
    +"An authentication error occurred. Verify and try again.","An authentication error occurred. Verify and try again."
    +"Adobe Sign-In is disabled.","Adobe Sign-In is disabled."
    +"Authorization was successful","Authorization was successful"
    +"Invalid state returned from IMS","Invalid state returned from IMS"
    +"Could not connect to Adobe IMS Service: %1.","Could not connect to Adobe IMS Service: %1."
    +"Could not get a valid response from Adobe IMS Service.","Could not get a valid response from Adobe IMS Service."
    +"Could not verify the access_token","Could not verify the access_token"
    +"Profile body is empty","Profile body is empty"
    +"Could not save ims token.","Could not save ims token."
    +"Could not find ims token id: %id.","Could not find ims token id: %id."
    +"Could not delete ims tokens for admin user id %1.","Could not delete ims tokens for admin user id %1."
    +"An error occurred during logout operation.","An error occurred during logout operation."
    +"Failed to get JWK","Failed to get JWK"
    +"Failed to read JWT token","Failed to read JWT token"
    +"JWT does not contain claims","JWT does not contain claims"
    +"created_at not provided by the received JWT","created_at not provided by the received JWT"
    +"expires_in not provided by the received JWT","expires_in not provided by the received JWT"
    +"Token has expired","Token has expired"
    +"The account sign-in was incorrect or your account is disabled temporarily. Please wait and try again later.","The account sign-in was incorrect or your account is disabled temporarily. Please wait and try again later."
    +"More permissions are needed to access this.","More permissions are needed to access this."
    +"Please sign in with Adobe ID","Please sign in with Adobe ID"
    +"Admin token generation is disabled. Please use Adobe IMS ACCESS_TOKEN.","Admin token generation is disabled. Please use Adobe IMS ACCESS_TOKEN."
    +"Identity Verification","Identity Verification"
    +"Verify Identity with Adobe IMS","Verify Identity with Adobe IMS"
    +"Confirm Identity","Confirm Identity"
    +"To apply changes you need to verify your Adobe identity.","To apply changes you need to verify your Adobe identity."
    +"Identity Verified with Adobe IMS","Identity Verified with Adobe IMS"
    +"Please perform the AdobeIms reAuth and try again.","Please perform the AdobeIms reAuth and try again."
    +"Use the same email user has in Adobe IMS organization.","Use the same email user has in Adobe IMS organization."
    +"The tokens couldn't be revoked.","The tokens couldn't be revoked."
    +"No matching admin user found for Adobe ID.","No matching admin user found for Adobe ID."
    +"This field is required to enable the Admin Adobe IMS Module","This field is required to enable the Admin Adobe IMS Module"
    +"No valid Organization ID provided","No valid Organization ID provided"
    +"No valid Client ID provided","No valid Client ID provided"
    +"No valid Client Secret provided","No valid Client Secret provided"
    +"2FA is required when enabling the Admin Adobe IMS Module","2FA is required when enabling the Admin Adobe IMS Module"
    +"Can't check user membership in organization.","Can't check user membership in organization."
    +"Could not check Organization Membership. Response is empty.","Could not check Organization Membership. Response is empty."
    +"User is not a member of configured Adobe Organization.","User is not a member of configured Adobe Organization."
    +"Organization Membership check can't be performed","Organization Membership check can't be performed"
    +"The ims token wasn't found.","The ims token wasn't found."
    +"Sign in to access the Adobe Commerce for your organization.","Sign in to access the Adobe Commerce for your organization."
    +"Sign In","Sign In"
    +"This Commerce instance is managed by an organization. Contact your organization administrator to request access.","This Commerce instance is managed by an organization. Contact your organization administrator to request access."
    +"Sign in with Adobe ID","Sign in with Adobe ID"
    +Footer,Footer
    +"User Guides","User Guides"
    +"Customer Support","Customer Support"
    +Forums,Forums
    +Header,Header
    +"%user_name, you now have access to Adobe Commerce","%user_name, you now have access to Adobe Commerce"
    +"Your administrator at %store_name has given you access to Adobe Commerce","Your administrator at %store_name has given you access to Adobe Commerce"
    +"Get started","Get started"
    +"Here are a few links to help you get up and running:","Here are a few links to help you get up and running:"
    +Documentation,Documentation
    +"Release notes","Release notes"
    +"If you have any questions about access to Adobe Commerce, contact your administrator or your Adobe account team for more information.","If you have any questions about access to Adobe Commerce, contact your administrator or your Adobe account team for more information."
    +"Enable Logging for Admin Adobe IMS Module","Enable Logging for Admin Adobe IMS Module"
    +"Adobe Commerce","Adobe Commerce"
    +
    
  • app/code/Magento/AdminAdobeIms/Model/ImsConnection.php+6 2 modified
    @@ -167,8 +167,12 @@ public function validateToken(?string $token, string $tokenType = 'access_token'
             $curl->addHeader('cache-control', 'no-cache');
     
             $curl->post(
    -            $this->adminImsConfig->getValidateTokenUrl($token, $tokenType),
    -            []
    +            $this->adminImsConfig->getValidateTokenUrl(),
    +            [
    +                'token' => $token,
    +                'type' => $tokenType,
    +                'client_id' => $this->adminImsConfig->getApiKey()
    +            ]
             );
     
             if ($curl->getBody() === '') {
    
  • app/code/Magento/AdminAdobeIms/Model/LogOut.php+6 2 modified
    @@ -112,8 +112,12 @@ private function externalLogOut(string $accessToken): void
             $curl->addHeader('cache-control', 'no-cache');
     
             $curl->post(
    -            $this->adminImsConfig->getBackendLogoutUrl($accessToken),
    -            []
    +            $this->adminImsConfig->getBackendLogoutUrl(),
    +            [
    +                'access_token' => $accessToken,
    +                'client_secret' => $this->adminImsConfig->getPrivateKey(),
    +                'client_id' => $this->adminImsConfig->getApiKey()
    +            ]
             );
     
             if ($curl->getStatus() !== self::HTTP_OK || ($this->checkUserProfile($accessToken))) {
    
  • app/code/Magento/AdminAdobeIms/Service/ImsConfig.php+18 18 modified
    @@ -18,6 +18,7 @@
     use Magento\Framework\Encryption\EncryptorInterface;
     use Magento\Framework\Exception\LocalizedException;
     use Magento\Framework\UrlInterface;
    +use Magento\Framework\Data\Form\FormKey;
     
     class ImsConfig extends Config
     {
    @@ -58,25 +59,33 @@ class ImsConfig extends Config
          */
         private BackendUrlInterface $backendUrl;
     
    +    /**
    +     * @var FormKey
    +     */
    +    private FormKey $formKey;
    +
         /**
          * @param ScopeConfigInterface $scopeConfig
          * @param UrlInterface $url
          * @param WriterInterface $writer
          * @param EncryptorInterface $encryptor
          * @param BackendUrlInterface $backendUrl
    +     * @param FormKey $formKey
          */
         public function __construct(
             ScopeConfigInterface $scopeConfig,
             UrlInterface $url,
             WriterInterface $writer,
             EncryptorInterface $encryptor,
    -        BackendUrlInterface $backendUrl
    +        BackendUrlInterface $backendUrl,
    +        FormKey $formKey
         ) {
             parent::__construct($scopeConfig, $url);
             $this->writer = $writer;
             $this->encryptor = $encryptor;
             $this->scopeConfig = $scopeConfig;
             $this->backendUrl = $backendUrl;
    +        $this->formKey = $formKey;
         }
     
         /**
    @@ -180,17 +189,11 @@ public function getProfileUrl(): string
         /**
          * Get Token validation url
          *
    -     * @param string $code
    -     * @param string $tokenType
          * @return string
          */
    -    public function getValidateTokenUrl(string $code, string $tokenType): string
    +    public function getValidateTokenUrl(): string
         {
    -        return str_replace(
    -            ['#{token}', '#{client_id}', '#{token_type}'],
    -            [$code, $this->getApiKey(), $tokenType],
    -            $this->scopeConfig->getValue(self::XML_PATH_VALIDATE_TOKEN_URL)
    -        );
    +        return $this->scopeConfig->getValue(self::XML_PATH_VALIDATE_TOKEN_URL);
         }
     
         /**
    @@ -253,11 +256,12 @@ public function getAdminAdobeImsAuthUrl(?string $clientId): string
             }
     
             return str_replace(
    -            ['#{client_id}', '#{redirect_uri}', '#{scope}', '#{locale}'],
    +            ['#{client_id}', '#{redirect_uri}', '#{scope}', '#{state}', '#{locale}'],
                 [
                     $clientId,
                     $this->getAdminAdobeImsCallBackUrl(),
                     $this->getScopes(),
    +                $this->formKey->getFormKey(),
                     $this->getLocale()
                 ],
                 $this->scopeConfig->getValue(self::XML_PATH_ADMIN_AUTH_URL_PATTERN)
    @@ -272,11 +276,12 @@ public function getAdminAdobeImsAuthUrl(?string $clientId): string
         public function getAdminAdobeImsReAuthUrl(): string
         {
             return str_replace(
    -            ['#{client_id}', '#{redirect_uri}', '#{scope}', '#{locale}'],
    +            ['#{client_id}', '#{redirect_uri}', '#{scope}', '#{state}', '#{locale}'],
                 [
                     $this->getApiKey(),
                     $this->getAdminAdobeImsReAuthCallBackUrl(),
                     $this->getScopes(),
    +                $this->formKey->getFormKey(),
                     $this->getLocale()
                 ],
                 $this->scopeConfig->getValue(self::XML_PATH_ADMIN_REAUTH_URL_PATTERN)
    @@ -345,16 +350,11 @@ private function getLocale(): string
         /**
          * Get BackendLogout URL
          *
    -     * @param string $accessToken
          * @return string
          */
    -    public function getBackendLogoutUrl(string $accessToken) : string
    +    public function getBackendLogoutUrl() : string
         {
    -        return str_replace(
    -            ['#{access_token}', '#{client_secret}', '#{client_id}'],
    -            [$accessToken, $this->getPrivateKey(), $this->getApiKey()],
    -            $this->scopeConfig->getValue(self::XML_PATH_ADMIN_LOGOUT_URL)
    -        );
    +        return $this->scopeConfig->getValue(self::XML_PATH_ADMIN_LOGOUT_URL);
         }
     
         /**
    
  • app/code/Magento/AdminAdobeIms/Service/ImsOrganizationService.php+0 1 modified
    @@ -75,7 +75,6 @@ public function checkOrganizationMembership(string $access_token): void
                         __('User is not a member of configured Adobe Organization.')
                     );
                 }
    -
             } catch (\Exception $exception) {
                 throw new AdobeImsOrganizationAuthorizationException(
                     __('Organization Membership check can\'t be performed')
    
  • app/code/Magento/AdminAdobeIms/Test/Unit/Controller/Adminhtml/OAuth/ImsCallbackTest.php+252 0 added
    @@ -0,0 +1,252 @@
    +<?php
    +/**
    + * Copyright © Magento, Inc. All rights reserved.
    + * See COPYING.txt for license details.
    + */
    +declare(strict_types=1);
    +
    +namespace Magento\AdminAdobeIms\Test\Unit\Controller\Adminhtml\OAuth;
    +
    +use Magento\AdminAdobeIms\Controller\Adminhtml\OAuth\ImsCallback;
    +use Magento\AdminAdobeIms\Logger\AdminAdobeImsLogger;
    +use Magento\AdminAdobeIms\Model\ImsConnection;
    +use Magento\AdminAdobeIms\Service\AdminLoginProcessService;
    +use Magento\AdminAdobeIms\Service\ImsConfig;
    +use Magento\AdminAdobeIms\Service\ImsOrganizationService;
    +use Magento\Backend\App\Action\Context;
    +use Magento\Backend\Helper\Data;
    +use Magento\Backend\Model\Auth\Session;
    +use Magento\Framework\App\ActionFlag;
    +use Magento\Framework\App\Request\Http;
    +use Magento\Framework\App\RequestInterface;
    +use Magento\Framework\Data\Form\FormKey\Validator;
    +use Magento\Framework\Message\Manager;
    +use Magento\Framework\ObjectManager\ObjectManager;
    +use Magento\Framework\Validator\Locale;
    +use PHPUnit\Framework\MockObject\MockObject;
    +use PHPUnit\Framework\TestCase;
    +
    +/**
    + * Unit test for \Magento\AdminAdobeIms\Controller\Adminhtml\OAuth\ImsCallback controller.
    + *
    + * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
    + */
    +class ImsCallbackTest extends TestCase
    +{
    +    /**
    +     * @var Validator|mixed|MockObject
    +     */
    +    private mixed $validatorMock;
    +
    +    /**
    +     * @var RequestInterface|mixed|MockObject
    +     */
    +    private mixed $requestMock;
    +
    +    /**
    +     * @var AdminAdobeImsLogger|mixed|MockObject
    +     */
    +    private mixed $loggerMock;
    +
    +    /**
    +     * @var Data|mixed|MockObject
    +     */
    +    private mixed $helperMock;
    +
    +    /**
    +     * @var Manager|mixed|MockObject
    +     */
    +    private mixed $messagesMock;
    +
    +    /**
    +     * @var mixed|ImsCallback
    +     */
    +    private mixed $controller;
    +
    +    /**
    +     * @var Session|mixed|MockObject
    +     */
    +    private mixed $authSessionMock;
    +
    +    /**
    +     * @var ActionFlag|mixed|MockObject
    +     */
    +    private mixed $actionFlagMock;
    +
    +    /**
    +     * @var Session|mixed|MockObject
    +     */
    +    private mixed $authMock;
    +
    +    /**
    +     * @var ObjectManager|mixed|MockObject
    +     */
    +    private mixed $objectManagerMock;
    +
    +    /**
    +     * @var Locale|mixed|MockObject
    +     */
    +    private mixed $localeMock;
    +
    +    /**
    +     * @var \Magento\Backend\Model\Locale\Manager|mixed|MockObject
    +     */
    +    private mixed $managerMock;
    +
    +    /**
    +     * @var ImsConnection|mixed|MockObject
    +     */
    +    private $imsConnectionMock;
    +
    +    /**
    +     * @inheritDoc
    +     */
    +    protected function setUp(): void
    +    {
    +        $this->objectManagerMock = $this->getMockBuilder(ObjectManager::class)
    +            ->disableOriginalConstructor()
    +            ->onlyMethods(['get', 'create'])
    +            ->getMock();
    +        $this->requestMock = $this->getMockBuilder(Http::class)
    +            ->disableOriginalConstructor()
    +            ->onlyMethods(['getParam', 'setParam'])
    +            ->getMock();
    +        $responseMock = $this->getMockBuilder(\Magento\Framework\App\Response\Http::class)
    +            ->disableOriginalConstructor()
    +            ->addMethods([])
    +            ->getMock();
    +        $this->validatorMock = $this->getMockBuilder(Validator::class)
    +            ->disableOriginalConstructor()
    +            ->getMock();
    +        $this->loggerMock = $this->getMockBuilder(AdminAdobeImsLogger::class)
    +            ->disableOriginalConstructor()
    +            ->getMock();
    +        $this->messagesMock = $this->getMockBuilder(Manager::class)
    +            ->disableOriginalConstructor()
    +            ->onlyMethods(['addComplexErrorMessage'])
    +            ->getMockForAbstractClass();
    +        $this->authSessionMock = $this->getMockBuilder(Session::class)
    +            ->disableOriginalConstructor()
    +            ->addMethods(['setIsUrlNotice', 'getLocale'])
    +            ->getMock();
    +        $this->authMock = $this->getMockBuilder(Session::class)
    +            ->disableOriginalConstructor()
    +            ->getMock();
    +        $this->actionFlagMock = $this->getMockBuilder(ActionFlag::class)
    +            ->disableOriginalConstructor()
    +            ->getMock();
    +        $this->imsConnectionMock = $this->getMockBuilder(ImsConnection::class)
    +            ->disableOriginalConstructor()
    +            ->getMock();
    +        $this->localeMock = $this->getMockBuilder(Locale::class)
    +            ->disableOriginalConstructor()
    +            ->onlyMethods(['isValid'])
    +            ->getMock();
    +        $this->managerMock = $this->getMockBuilder(\Magento\Backend\Model\Locale\Manager::class)
    +            ->disableOriginalConstructor()
    +            ->onlyMethods(['switchBackendInterfaceLocale'])
    +            ->getMock();
    +        $this->helperMock = $this->getMockBuilder(Data::class)
    +            ->disableOriginalConstructor()
    +            ->onlyMethods(['getHomePageUrl', 'getUrl'])
    +            ->getMock();
    +        $imsConfigMock = $this->getMockBuilder(ImsConfig::class)
    +            ->disableOriginalConstructor()
    +            ->getMock();
    +        $imsOrganizationServiceMock = $this->getMockBuilder(ImsOrganizationService::class)
    +            ->disableOriginalConstructor()
    +            ->getMock();
    +        $loginProcessServiceMock = $this->createMock(AdminLoginProcessService::class);
    +        $contextMock = $this->getMockBuilder(Context::class)
    +            ->addMethods(['getFrontController', 'getTranslator'])
    +            ->onlyMethods([
    +                'getRequest',
    +                'getFormKeyValidator',
    +                'getMessageManager',
    +                'getHelper',
    +                'getActionFlag',
    +                'getResponse',
    +                'getSession',
    +                'getAuth',
    +                'getObjectManager'
    +            ])
    +            ->disableOriginalConstructor()
    +            ->getMock();
    +        $contextMock->expects($this->once())->method('getObjectManager')->willReturn($this->objectManagerMock);
    +        $contextMock->expects($this->once())->method('getResponse')->willReturn($responseMock);
    +        $contextMock->expects($this->once())->method('getAuth')->willReturn($this->authMock);
    +        $contextMock->expects($this->once())->method('getRequest')->willReturn($this->requestMock);
    +        $contextMock->expects($this->once())->method('getFormKeyValidator')->willReturn($this->validatorMock);
    +        $contextMock->expects($this->once())->method('getActionFlag')->willReturn($this->actionFlagMock);
    +        $contextMock->expects($this->once())->method('getSession')->willReturn($this->authSessionMock);
    +        $contextMock->expects($this->once())->method('getHelper')->willReturn($this->helperMock);
    +        $contextMock->expects($this->once())->method('getMessageManager')->willReturn($this->messagesMock);
    +
    +        $this->controller = new ImsCallback(
    +            $contextMock,
    +            $this->imsConnectionMock,
    +            $imsConfigMock,
    +            $imsOrganizationServiceMock,
    +            $loginProcessServiceMock,
    +            $this->loggerMock,
    +        );
    +    }
    +
    +    /**
    +     * Validate if state exists in ims callback url.
    +     * @return void
    +     */
    +    public function testStateExistsInImsCallback(): void
    +    {
    +        $this->addMockData();
    +        $this->validatorMock->expects($this->once())->method('validate')
    +            ->with($this->requestMock)
    +            ->willReturn(true);
    +        $response = $this->controller->dispatch($this->requestMock);
    +        $this->assertEquals(200, $response->getHttpResponseCode());
    +    }
    +
    +    /**
    +     * Validate if state not exists in ims callback url.
    +     * @return void
    +     */
    +    public function testStateNotExistsInImsCallback(): void
    +    {
    +        $this->addMockData();
    +        $this->validatorMock->expects($this->once())->method('validate')
    +            ->with($this->requestMock)
    +            ->willReturn(false);
    +        $response = $this->controller->dispatch($this->requestMock);
    +        $this->assertEquals(302, $response->getHttpResponseCode());
    +    }
    +
    +    /**
    +     * Add mock data for tests
    +     * @return void
    +     */
    +    private function addMockData(): void
    +    {
    +        $this->requestMock->expects($this->any())->method('setParam')
    +            ->with('form_key')
    +            ->willReturnSelf();
    +        $this->requestMock->expects($this->any())->method('getParam')
    +            ->withConsecutive(['state'], ['locale'])
    +            ->willReturnOnConsecutiveCalls('abc', 'en');
    +        $this->authSessionMock->expects($this->any())->method('setIsUrlNotice')
    +            ->willReturnSelf();
    +        $this->authSessionMock->expects($this->any())->method('getLocale')
    +            ->willReturn('en');
    +        $this->actionFlagMock->expects($this->any())->method('get')
    +            ->with('', 'check_url_settings')
    +            ->willReturn(true);
    +        $this->helperMock->expects($this->any())->method('getHomePageUrl')
    +            ->willReturn('https://magento.test/admin');
    +        $this->helperMock->expects($this->any())->method('getUrl')
    +            ->willReturn('https://magento.test/admin');
    +        $this->authMock->expects($this->any())->method('isLoggedIn')->willReturn(false);
    +        $this->objectManagerMock
    +            ->method('get')
    +            ->withConsecutive([Locale::class], [\Magento\Backend\Model\Locale\Manager::class])
    +            ->willReturnOnConsecutiveCalls($this->localeMock, $this->managerMock);
    +    }
    +}
    
  • app/code/Magento/AdminAdobeIms/Test/Unit/Controller/Adminhtml/OAuth/ImsReauthCallbackTest.php+157 0 added
    @@ -0,0 +1,157 @@
    +<?php
    +/**
    + * Copyright © Magento, Inc. All rights reserved.
    + * See COPYING.txt for license details.
    + */
    +declare(strict_types=1);
    +
    +namespace Magento\AdminAdobeIms\Test\Unit\Controller\Adminhtml\OAuth;
    +
    +use Magento\AdminAdobeIms\Controller\Adminhtml\OAuth\ImsReauthCallback;
    +use Magento\AdminAdobeIms\Logger\AdminAdobeImsLogger;
    +use Magento\AdminAdobeIms\Model\ImsConnection;
    +use Magento\AdminAdobeIms\Service\AdminReauthProcessService;
    +use Magento\AdminAdobeIms\Service\ImsConfig;
    +use Magento\AdminAdobeIms\Service\ImsOrganizationService;
    +use Magento\Backend\App\Action\Context;
    +use Magento\Framework\App\Request\Http;
    +use Magento\Framework\App\RequestInterface;
    +use Magento\Framework\Controller\Result\Raw;
    +use Magento\Framework\Controller\ResultFactory;
    +use Magento\Framework\Data\Form\FormKey\Validator;
    +use Magento\Framework\Message\Manager;
    +use PHPUnit\Framework\MockObject\MockObject;
    +use PHPUnit\Framework\TestCase;
    +
    +/**
    + * Unit test for \Magento\AdminAdobeIms\Controller\Adminhtml\OAuth\ImsReauthCallback controller.
    + *
    + * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
    + */
    +class ImsReauthCallbackTest extends TestCase
    +{
    +    /**
    +     * @var RequestInterface|mixed|MockObject
    +     */
    +    private mixed $request;
    +
    +    /**
    +     * @var Raw|mixed|MockObject
    +     */
    +    private mixed $resultRaw;
    +
    +    /**
    +     * @var ResultFactory|mixed|MockObject
    +     */
    +    private mixed $resultFactory;
    +
    +    /**
    +     * @var Context|mixed|MockObject
    +     */
    +    private mixed $context;
    +
    +    /**
    +     * @var AdminAdobeImsLogger|mixed|MockObject
    +     */
    +    private mixed $loggerMock;
    +
    +    /**
    +     * @var ImsReauthCallback
    +     */
    +    private ImsReauthCallback $controller;
    +
    +    /**
    +     * @var ImsConfig|mixed|MockObject
    +     */
    +    private mixed $imsConfigMock;
    +
    +    /**
    +     * @var Manager|mixed|MockObject
    +     */
    +    private mixed $messagesMock;
    +
    +    /**
    +     * @var Validator|mixed|MockObject
    +     */
    +    private mixed $validatorMock;
    +
    +    /**
    +     * @inheritDoc
    +     */
    +    protected function setUp(): void
    +    {
    +        $this->request = $this->getMockBuilder(Http::class)
    +            ->disableOriginalConstructor()
    +            ->onlyMethods(['getParam', 'setParam'])
    +            ->getMock();
    +        $this->resultRaw = $this->createMock(Raw::class);
    +        $this->resultFactory = $this->createMock(ResultFactory::class);
    +        $this->context = $this->createMock(Context::class);
    +        $this->imsConfigMock = $this->createMock(ImsConfig::class);
    +        $this->loggerMock = $this->createMock(AdminAdobeImsLogger::class);
    +        $this->messagesMock = $this->getMockBuilder(Manager::class)
    +            ->disableOriginalConstructor()
    +            ->onlyMethods(['addErrorMessage'])
    +            ->getMockForAbstractClass();
    +    }
    +
    +    /**
    +     * Validate state key not exists in callback
    +     * @return void
    +     */
    +    public function testExecuteStateKeyNotExistsInCallBack(): void
    +    {
    +        $this->setMockData();
    +        $content = 'auth[code=error;message=Invalid state returned from IMS]';
    +        $this->resultRaw->expects($this->once())->method('setContents')->with($content)->willReturnSelf();
    +        $this->validatorMock->expects($this->once())->method('validate')
    +            ->with($this->request)
    +            ->willReturn(false);
    +
    +        $this->assertSame($this->resultRaw, $this->controller->execute());
    +    }
    +
    +    /**
    +     * Set mock objects data
    +     * @return void
    +     */
    +    private function setMockData(): void
    +    {
    +        $this->request->expects($this->any())->method('setParam')
    +            ->with('form_key')
    +            ->willReturnSelf();
    +        $this->request->expects($this->any())->method('getParam')
    +            ->withConsecutive(['state'], ['code'])
    +            ->willReturnOnConsecutiveCalls(null, 'asdasdasdad');
    +        $this->resultFactory->expects($this->once())
    +            ->method('create')
    +            ->with(ResultFactory::TYPE_RAW)
    +            ->willReturn($this->resultRaw);
    +        $this->validatorMock = $this->getMockBuilder(Validator::class)
    +            ->disableOriginalConstructor()
    +            ->getMock();
    +        $imsConnectionMock = $this->getMockBuilder(ImsConnection::class)
    +            ->disableOriginalConstructor()
    +            ->getMock();
    +        $imsOrganizationServiceMock = $this->getMockBuilder(ImsOrganizationService::class)
    +            ->disableOriginalConstructor()
    +            ->getMock();
    +        $loginProcessServiceMock = $this->createMock(AdminReauthProcessService::class);
    +
    +        $this->context->expects($this->once())->method('getRequest')->willReturn($this->request);
    +        $this->context->expects($this->once())->method('getResultFactory')->willReturn($this->resultFactory);
    +        $this->context->expects($this->any())->method('getMessageManager')->willReturn($this->messagesMock);
    +        $this->context->expects($this->any())->method('getFormKeyValidator')->willReturn($this->validatorMock);
    +
    +        $this->imsConfigMock->expects($this->once())->method('enabled')
    +            ->willReturn(true);
    +        $this->controller = new ImsReauthCallback(
    +            $this->context,
    +            $imsConnectionMock,
    +            $this->imsConfigMock,
    +            $imsOrganizationServiceMock,
    +            $loginProcessServiceMock,
    +            $this->loggerMock
    +        );
    +    }
    +}
    
  • app/code/Magento/AdminAdobeIms/Test/Unit/Model/ImsConnectionTest.php+42 5 modified
    @@ -38,25 +38,35 @@ class ImsConnectionTest extends TestCase
          */
         private $adminImsConnection;
     
    +    /**
    +     * @var Json|mixed|\PHPUnit\Framework\MockObject\MockObject
    +     */
    +    private $json;
    +
    +    /**
    +     * @var ImsConfig|mixed|\PHPUnit\Framework\MockObject\MockObject
    +     */
    +    private $adminImsConfigMock;
    +
         protected function setUp(): void
         {
             $objectManagerHelper = new ObjectManagerHelper($this);
     
    -        $adminImsConfigMock = $this->createMock(ImsConfig::class);
    -        $adminImsConfigMock
    +        $this->adminImsConfigMock = $this->createMock(ImsConfig::class);
    +        $this->adminImsConfigMock
                 ->method('getAuthUrl')
                 ->willReturn(self::AUTH_URL);
     
             $this->curlFactory = $this->createMock(CurlFactory::class);
     
    -        $json = $this->createMock(Json::class);
    +        $this->json = $this->createMock(Json::class);
     
             $this->adminImsConnection = $objectManagerHelper->getObject(
                 ImsConnection::class,
                 [
                     'curlFactory' => $this->curlFactory,
    -                'adminImsConfig' => $adminImsConfigMock,
    -                'json' => $json,
    +                'adminImsConfig' => $this->adminImsConfigMock,
    +                'json' => $this->json,
                 ]
             );
         }
    @@ -92,4 +102,31 @@ public function testAuthThrowsExceptionWhenResponseContainsError(): void
             $this->expectExceptionMessage('Could not connect to Adobe IMS Service: invalid_scope.');
             $this->adminImsConnection->auth();
         }
    +
    +    /**
    +     * Token validate test
    +     *
    +     * @return void
    +     */
    +    public function testValidateToken(): void
    +    {
    +        $this->adminImsConfigMock->method('getValidateTokenUrl')
    +            ->willReturn('https://ims-na1-stg1.adobelogin.com/ims/validate_token/v1');
    +        $this->adminImsConfigMock->method('getApiKey')
    +            ->willReturn('api_key');
    +        $curlMock = $this->createMock(Curl::class);
    +        $curlMock->expects($this->once())
    +            ->method('post')
    +            ->willReturn(null);
    +        $curlMock->method('getBody')
    +            ->willReturn('{"valid":1}');
    +        $curlMock->method('getStatus')
    +            ->willReturn(302);
    +        $this->json->method('unserialize')
    +            ->with('{"valid":1}')
    +            ->willReturn(['valid' => true]);
    +        $this->curlFactory->method('create')
    +            ->willReturn($curlMock);
    +        $this->assertTrue($this->adminImsConnection->validateToken('valid_token'));
    +    }
     }
    
  • app/code/Magento/AdminAdobeIms/Test/Unit/Model/LogOutTest.php+155 0 added
    @@ -0,0 +1,155 @@
    +<?php
    +/**
    + * Copyright © Magento, Inc. All rights reserved.
    + * See COPYING.txt for license details.
    + */
    +
    +declare(strict_types=1);
    +
    +namespace Magento\AdminAdobeIms\Test\Unit\Model;
    +
    +use Magento\AdminAdobeIms\Model\Auth;
    +use Magento\AdminAdobeIms\Model\ImsConnection;
    +use Magento\AdminAdobeIms\Model\LogOut;
    +use Magento\AdminAdobeIms\Service\ImsConfig;
    +use Magento\AdobeImsApi\Api\ConfigInterface;
    +use Magento\AdobeImsApi\Api\FlushUserTokensInterface;
    +use Magento\AdobeImsApi\Api\GetAccessTokenInterface;
    +use Magento\Backend\Model\Auth\StorageInterface;
    +use Magento\Framework\HTTP\Client\Curl;
    +use Magento\Framework\HTTP\Client\CurlFactory;
    +use PHPUnit\Framework\MockObject\MockObject;
    +use PHPUnit\Framework\TestCase;
    +use Psr\Log\LoggerInterface;
    +
    +/**
    + * Test the Adobe Ims log out service
    + */
    +class LogOutTest extends TestCase
    +{
    +    private const HTTP_FOUND = 200;
    +    private const HTTP_ERROR = 500;
    +
    +    /**
    +     * @var CurlFactory|MockObject
    +     */
    +    private $curlFactoryMock;
    +
    +    /**
    +     * @var LoggerInterface|MockObject
    +     */
    +    private $loggerInterfaceMock;
    +
    +    /**
    +     * @var LogOut|MockObject $model
    +     */
    +    private $model;
    +    /**
    +     * @var ImsConnection|mixed|MockObject
    +     */
    +    private $imsConnection;
    +    /**
    +     * @var Auth|mixed|MockObject
    +     */
    +    private $auth;
    +
    +    /**
    +     * @var StorageInterface|mixed|MockObject
    +     */
    +    private $session;
    +
    +    /**
    +     * @var ImsConfig|mixed|MockObject
    +     */
    +    private $config;
    +
    +    /**
    +     * @inheritdoc
    +     */
    +    protected function setUp(): void
    +    {
    +        $this->curlFactoryMock = $this->createMock(CurlFactory::class);
    +        $this->config = $this->createMock(ImsConfig::class);
    +        $this->loggerInterfaceMock = $this->createMock(LoggerInterface::class);
    +        $this->imsConnection = $this->createMock(ImsConnection::class);
    +        $this->auth = $this->createMock(Auth::class);
    +        $this->session = $this->getMockBuilder(StorageInterface::class)
    +            ->addMethods(['getAdobeAccessToken'])
    +            ->getMockForAbstractClass();
    +        $this->model = new LogOut(
    +            $this->loggerInterfaceMock,
    +            $this->config,
    +            $this->curlFactoryMock,
    +            $this->imsConnection,
    +            $this->auth
    +        );
    +    }
    +
    +    /**
    +     * Test LogOut.
    +     * @return void
    +     */
    +    public function testExecute(): void
    +    {
    +        $this->session->expects($this->any())
    +            ->method('getAdobeAccessToken')
    +            ->willReturn('access_token');
    +        $this->auth->expects($this->any())
    +            ->method('getAuthStorage')
    +            ->willReturn($this->session);
    +        $this->imsConnection->expects($this->exactly(2))
    +            ->method('getProfile')
    +            ->willReturnOnConsecutiveCalls(['email' => 'test@email.com'], []);
    +
    +        $curl = $this->createMock(Curl::class);
    +
    +        $this->curlFactoryMock->expects($this->once())
    +            ->method('create')
    +            ->willReturn($curl);
    +        $curl->expects($this->exactly(2))
    +            ->method('addHeader')
    +            ->willReturn(null);
    +        $curl->expects($this->once())
    +            ->method('post')
    +            ->willReturn(null);
    +        $curl->expects($this->any())
    +            ->method('getStatus')
    +            ->willReturn(self::HTTP_FOUND);
    +
    +        $this->assertEquals(true, $this->model->execute('access_token'));
    +    }
    +
    +    /**
    +     * Test LogOut with Error.
    +     * @return void
    +     */
    +    public function testExecuteWithError(): void
    +    {
    +        $this->session->expects($this->any())
    +            ->method('getAdobeAccessToken')
    +            ->willReturn('access_token');
    +        $this->auth->expects($this->any())
    +            ->method('getAuthStorage')
    +            ->willReturn($this->session);
    +        $this->imsConnection->expects($this->exactly(1))
    +            ->method('getProfile')
    +            ->willReturnOnConsecutiveCalls(['email' => 'test@email.com'], []);
    +
    +        $curl = $this->createMock(Curl::class);
    +
    +        $this->curlFactoryMock->expects($this->once())
    +            ->method('create')
    +            ->willReturn($curl);
    +        $curl->expects($this->exactly(2))
    +            ->method('addHeader')
    +            ->willReturn(null);
    +        $curl->expects($this->once())
    +            ->method('post')
    +            ->willReturn(null);
    +        $curl->expects($this->any())
    +            ->method('getStatus')
    +            ->willReturn(self::HTTP_ERROR);
    +
    +        $this->assertEquals(false, $this->model->execute('access_token'));
    +    }
    +}
    
  • app/code/Magento/AdminAnalytics/composer.json+14 11 modified
    @@ -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-p8",
         "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 @@
             }
         }
     }
    +
    
  • app/code/Magento/AdminAnalytics/view/adminhtml/templates/tracking.phtml+17 9 modified
    @@ -6,6 +6,7 @@
     
     /**
      * @var \Magento\Framework\View\Helper\SecureHtmlRenderer $secureRenderer
    + * @var \Magento\Framework\Escaper $escaper
      */
     ?>
     
    @@ -22,18 +23,25 @@
     <?php
     /** @var \Magento\AdminAnalytics\ViewModel\Metadata $metadata */
     $metadata = $block->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 . '";
     ';
     ?>
     <?= /* @noEscape */ $secureRenderer->renderTag('script', [], $scriptString, false); ?>
    
  • app/code/Magento/AdminAnalytics/ViewModel/Metadata.php+29 1 modified
    @@ -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;
    +    }
     }
    
  • app/code/Magento/AdminNotification/composer.json+13 11 modified
    @@ -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.4",
         "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 @@
             }
         }
     }
    +
    
  • app/code/Magento/AdobeImsApi/Api/ConfigInterface.php+1 2 modified
    @@ -53,11 +53,10 @@ public function getCallBackUrl(): string;
         /**
          * Return logout url for AdobeSdk.
          *
    -     * @param string $accessToken
          * @param string $redirectUrl
          * @return string
          */
    -    public function getLogoutUrl(string $accessToken, string $redirectUrl = ''): string;
    +    public function getLogoutUrl(string $redirectUrl = ''): string;
     
         /**
          * Return image url for AdobeSdk.
    
  • app/code/Magento/AdobeImsApi/composer.json+6 4 modified
    @@ -1,15 +1,16 @@
     {
         "name": "magento/module-adobe-ims-api",
         "description": "Implementation of Magento module responsible for authentication to Adobe services",
    -    "require": {
    -        "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*"
    -    },
         "type": "magento2-module",
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "2.1.2-p2",
    +    "require": {
    +        "php": "~7.4.0||~8.1.0",
    +        "magento/framework": "103.0.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -19,3 +20,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/AdobeIms/composer.json+11 9 modified
    @@ -1,20 +1,21 @@
     {
         "name": "magento/module-adobe-ims",
         "description": "Magento module responsible for authentication to Adobe services",
    -    "require": {
    -        "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    -        "magento/module-adobe-ims-api": "*",
    -        "magento/module-authorization": "*",
    -        "magento/module-backend": "*",
    -        "magento/module-config": "*",
    -        "magento/module-user": "*"
    -    },
         "type": "magento2-module",
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "2.1.4-p2",
    +    "require": {
    +        "php": "~7.4.0||~8.1.0",
    +        "magento/framework": "103.0.*",
    +        "magento/module-adobe-ims-api": "2.1.*",
    +        "magento/module-authorization": "100.4.*",
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-config": "101.2.*",
    +        "magento/module-user": "101.2.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -24,3 +25,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/AdobeIms/etc/config.xml+1 1 modified
    @@ -12,7 +12,7 @@
                     <api_key backend_model="Magento\Config\Model\Config\Backend\Encrypted"/>
                     <private_key backend_model="Magento\Config\Model\Config\Backend\Encrypted"/>
                     <token_url>https://ims-na1.adobelogin.com/ims/token</token_url>
    -                <logout_url><![CDATA[https://ims-na1.adobelogin.com/ims/logout?access_token=#{access_token}&redirect_uri=#{redirect_uri}]]></logout_url>
    +                <logout_url><![CDATA[https://ims-na1.adobelogin.com/ims/logout/v1?redirect_uri=#{redirect_uri}]]></logout_url>
                     <image_url><![CDATA[https://cc-api-behance.adobe.io/v2/users/me?api_key=#{api_key}]]></image_url>
                     <auth_url_pattern><![CDATA[https://ims-na1.adobelogin.com/ims/authorize?client_id=#{client_id}&redirect_uri=#{redirect_uri}&locale=#{locale}&scope=openid,creative_sdk,email,profile&response_type=code]]></auth_url_pattern>
                 </integration>
    
  • app/code/Magento/AdobeIms/Model/Config.php+3 3 modified
    @@ -106,15 +106,15 @@ private function getLocale(): string
         /**
          * @inheritdoc
          */
    -    public function getLogoutUrl(string $accessToken, string $redirectUrl = '') : string
    +    public function getLogoutUrl(string $redirectUrl = '') : string
         {
             // there is no success response with empty redirect url
             if ($redirectUrl === '') {
                 $redirectUrl = 'self';
             }
             return str_replace(
    -            ['#{access_token}', '#{redirect_uri}'],
    -            [$accessToken, $redirectUrl],
    +            '#{redirect_uri}',
    +            $redirectUrl,
                 $this->scopeConfig->getValue(self::XML_PATH_LOGOUT_URL_PATTERN) ?? ''
             );
         }
    
  • app/code/Magento/AdobeIms/Model/LogOut.php+8 1 modified
    @@ -103,7 +103,14 @@ private function externalLogOut(string $accessToken): void
             $curl = $this->curlFactory->create();
             $curl->addHeader('Content-Type', 'application/x-www-form-urlencoded');
             $curl->addHeader('cache-control', 'no-cache');
    -        $curl->get($this->config->getLogoutUrl($accessToken));
    +        $curl->post(
    +            $this->config->getLogoutUrl(),
    +            [
    +                'access_token' => $accessToken,
    +                'client_secret' => $this->config->getPrivateKey(),
    +                'client_id' => $this->config->getApiKey()
    +            ]
    +        );
     
             if ($curl->getStatus() !== self::HTTP_FOUND) {
                 throw new LocalizedException(
    
  • app/code/Magento/AdobeIms/Test/Unit/Model/ConfigTest.php+3 5 modified
    @@ -56,9 +56,8 @@ class ConfigTest extends TestCase
          */
         private const XML_PATH_LOGOUT_URL_PATTERN = 'adobe_ims/integration/logout_url';
         private const LOGOUT_URL_PATTERN = 'https://logout-url.com/pattern' .
    -    '?access_token=#{access_token}&redirect_uri=#{redirect_uri}';
    +    '?redirect_uri=#{redirect_uri}';
         private const REDIRECT_URI = 'REDIRECT_URI';
    -    private const ACCCESS_TOKEN = 'ACCCESS_TOKEN';
     
         /**
          * Profile image URL constants
    @@ -187,9 +186,8 @@ public function testGetLogoutUrl(): void
                 ->willReturn(self::LOGOUT_URL_PATTERN);
     
             $this->assertEquals(
    -            'https://logout-url.com/pattern?access_token=' . self::ACCCESS_TOKEN .
    -                '&redirect_uri=' . self::REDIRECT_URI,
    -            $this->config->getLogoutUrl(self::ACCCESS_TOKEN, self::REDIRECT_URI)
    +            'https://logout-url.com/pattern?redirect_uri=' . self::REDIRECT_URI,
    +            $this->config->getLogoutUrl(self::REDIRECT_URI)
             );
         }
     
    
  • app/code/Magento/AdobeIms/Test/Unit/Model/LogOutTest.php+3 3 modified
    @@ -96,7 +96,7 @@ public function testExecute(): void
                 ->method('addHeader')
                 ->willReturn(null);
             $curl->expects($this->once())
    -            ->method('get')
    +            ->method('post')
                 ->willReturnSelf();
             $curl->expects($this->once())
                 ->method('getStatus')
    @@ -125,7 +125,7 @@ public function testExecuteWithError(): void
                 ->method('addHeader')
                 ->willReturn(null);
             $curl->expects($this->once())
    -            ->method('get')
    +            ->method('post')
                 ->willReturnSelf();
             $curl->expects($this->once())
                 ->method('getStatus')
    @@ -156,7 +156,7 @@ public function testExecuteWithException(): void
                 ->method('addHeader')
                 ->willReturn(null);
             $curl->expects($this->once())
    -            ->method('get')
    +            ->method('post')
                 ->willReturnSelf();
             $curl->expects($this->once())
                 ->method('getStatus')
    
  • app/code/Magento/AdvancedPricingImportExport/composer.json+16 14 modified
    @@ -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.5",
         "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 @@
             }
         }
     }
    +
    
  • app/code/Magento/AdvancedSearch/composer.json+15 13 modified
    @@ -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.3",
         "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 @@
             }
         }
     }
    +
    
  • app/code/Magento/Amqp/composer.json+10 8 modified
    @@ -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 @@
             }
         }
     }
    +
    
  • app/code/Magento/Analytics/composer.json+10 8 modified
    @@ -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.5-p3",
    +    "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 @@
             }
         }
     }
    +
    
  • app/code/Magento/Analytics/Test/Mftf/ActionGroup/AssertAdminAdvancedReportingPageUrlActionGroup.xml+1 1 modified
    @@ -15,6 +15,6 @@
     
             <switchToNextTab stepKey="switchToNewTab"/>
             <waitForPageLoad stepKey="waitForAdvancedReportingPageLoad"/>
    -        <seeInCurrentUrl url="reports/advanced-reporting" stepKey="seeAssertAdvancedReportingPageUrl"/>
    +        <seeInCurrentUrl url="report" stepKey="seeAssertAdvancedReportingPageUrl"/>
         </actionGroup>
     </actionGroups>
    
  • app/code/Magento/AsynchronousOperations/composer.json+14 12 modified
    @@ -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.5",
         "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 @@
             }
         }
     }
    +
    
  • app/code/Magento/Authorization/composer.json+9 7 modified
    @@ -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.5",
         "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 @@
             }
         }
     }
    +
    
  • app/code/Magento/AwsS3/composer.json+8 6 modified
    @@ -1,18 +1,19 @@
     {
         "name": "magento/module-aws-s3",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "proprietary"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.3",
         "require": {
             "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    -        "magento/module-remote-storage": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-remote-storage": "100.4.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "proprietary"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -22,3 +23,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Backend/Block/System/Store/Grid/Render/Group.php+5 3 modified
    @@ -5,6 +5,8 @@
      */
     namespace Magento\Backend\Block\System\Store\Grid\Render;
     
    +use Magento\Framework\DataObject;
    +
     /**
      * Store render group
      *
    @@ -13,9 +15,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 +30,6 @@ public function render(\Magento\Framework\DataObject $row)
             '">' .
             $this->escapeHtml($row->getData($this->getColumn()->getIndex())) .
             '</a><br />'
    -        . '(' . __('Code') . ': ' . $row->getGroupCode() . ')';
    +        . '(' . __('Code') . ': ' . $this->escapeHtml($row->getGroupCode()) . ')';
         }
     }
    
  • app/code/Magento/Backend/composer.json+26 24 modified
    @@ -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.5-p8",
         "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 @@
             }
         }
     }
    +
    
  • app/code/Magento/Backend/etc/adminhtml/system.xml+3 0 modified
    @@ -10,6 +10,9 @@
             <tab id="general" translate="label" sortOrder="100">
                 <label>General</label>
             </tab>
    +        <tab id="security" translate="label" sortOrder="200">
    +            <label>Security</label>
    +        </tab>
             <tab id="service" translate="label" sortOrder="99999">
                 <label>Services</label>
             </tab>
    
  • app/code/Magento/Backup/composer.json+11 9 modified
    @@ -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.5",
         "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 @@
             }
         }
     }
    +
    
  • app/code/Magento/Bundle/composer.json+27 25 modified
    @@ -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.5-p6",
         "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 @@
             }
         }
     }
    +
    
  • app/code/Magento/BundleGraphQl/composer.json+15 13 modified
    @@ -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.5",
    +    "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 @@
             }
         }
     }
    +
    
  • app/code/Magento/BundleImportExport/composer.json+14 12 modified
    @@ -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.4",
         "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 @@
             }
         }
     }
    +
    
  • app/code/Magento/Bundle/Test/Mftf/ActionGroup/StoreFrontAddProductToCartFromBundleActionGroup.xml+4 0 modified
    @@ -17,11 +17,15 @@
                 <argument name="currency" type="string" defaultValue="US Dollar"/>
             </arguments>
     
    +        <waitForElementVisible selector="{{StorefrontBundledSection.currencyTrigger}}" stepKey="waitForCurrencyTriggerVisible"/>
             <click selector="{{StorefrontBundledSection.currencyTrigger}}" stepKey="openCurrencyTrigger"/>
    +        <waitForElementVisible selector="{{StorefrontBundledSection.currency(currency)}}" stepKey="waitForChooseCurrencyVisible"/>
             <click selector="{{StorefrontBundledSection.currency(currency)}}" stepKey="chooseCurrency"/>
    +        <waitForElementVisible selector="{{StorefrontBundledSection.addToCart}}" stepKey="waitForCustomizeButtonVisible"/>
             <click selector="{{StorefrontBundledSection.addToCart}}" stepKey="clickCustomize"/>
             <waitForPageLoad stepKey="waitForBundleOpen"/>
             <checkOption selector="{{StorefrontBundledSection.productInBundle(product.name)}}" stepKey="chooseProduct"/>
    +        <waitForElementVisible selector="{{StorefrontBundledSection.addToCartConfigured}}" stepKey="waitForAddToCartButtonVisible"/>
             <click selector="{{StorefrontBundledSection.addToCartConfigured}}" stepKey="addToCartProduct"/>
             <scrollToTopOfPage stepKey="scrollToTop"/>
         </actionGroup>
    
  • app/code/Magento/Bundle/view/frontend/templates/catalog/product/view/type/bundle/option/radio.phtml+28 22 modified
    @@ -4,6 +4,8 @@
      * See COPYING.txt for license details.
      */
     use Magento\Bundle\ViewModel\ValidateQuantity;
    +
    +// phpcs:disable Generic.Files.LineLength.TooLong
     ?>
     <?php /* @var $block \Magento\Bundle\Block\Catalog\Product\View\Type\Bundle\Option\Radio */ ?>
     <?php $_option = $block->getOption(); ?>
    @@ -20,42 +22,45 @@ $viewModel = $block->getData('validateQuantityViewModel');
         </label>
         <div class="control">
             <div class="nested options-list">
    -            <?php if ($block->showSingle()) : ?>
    +            <?php if ($block->showSingle()): ?>
                     <?= /* @noEscape */ $block->getSelectionTitlePrice($_selections[0]) ?>
                     <?= /* @noEscape */ $block->getTierPriceRenderer()->renderTierPrice($_selections[0]) ?>
                     <input type="hidden"
    -                    class="bundle-option-<?= (int)$_option->getId() ?>  product bundle option"
    -                    name="bundle_option[<?= (int)$_option->getId() ?>]"
    -                    value="<?= (int)$_selections[0]->getSelectionId() ?>"
    -                    id="bundle-option-<?= (int)$_option->getId() ?>-<?= (int)$_selections[0]->getSelectionId() ?>"
    -                    checked="checked"
    +                       class="bundle-option-<?= (int)$_option->getId() ?>  product bundle option"
    +                       name="bundle_option[<?= (int)$_option->getId() ?>]"
    +                       value="<?= (int)$_selections[0]->getSelectionId() ?>"
    +                       id="bundle-option-<?= (int)$_option->getId() ?>-<?= (int)$_selections[0]->getSelectionId() ?>"
    +                       checked="checked"
                     />
    -            <?php else :?>
    -                <?php if (!$_option->getRequired()) : ?>
    +            <?php else: ?>
    +                <?php if (!$_option->getRequired()): ?>
                         <div class="field choice">
                             <input type="radio"
                                    class="radio product bundle option"
                                    id="bundle-option-<?= $block->escapeHtmlAttr($_option->getId()) ?>"
                                    name="bundle_option[<?= $block->escapeHtmlAttr($_option->getId()) ?>]"
                                    data-selector="bundle_option[<?= $block->escapeHtmlAttr($_option->getId()) ?>]"
    -                               <?= ($_default && $_default->isSalable())?'':' checked="checked" ' ?>
    +                            <?= ($_default && $_default->isSalable())?'':' checked="checked" ' ?>
                                    value=""/>
                             <label class="label" for="bundle-option-<?= $block->escapeHtmlAttr($_option->getId()) ?>">
                                 <span><?= $block->escapeHtml(__('None')) ?></span>
                             </label>
                         </div>
                     <?php endif; ?>
    -                <?php foreach ($_selections as $_selection) : ?>
    +                <?php foreach ($_selections as $_selection): ?>
                         <div class="field choice">
                             <input type="radio"
                                    class="radio product bundle option change-container-classname"
                                    id="bundle-option-<?= $block->escapeHtmlAttr($_option->getId()) ?>-<?= $block->escapeHtmlAttr($_selection->getSelectionId()) ?>"
    -                               <?php if ($_option->getRequired()) { echo 'data-validate="{\'validate-one-required-by-name\':true}"'; }?>
    +                            <?php if ($_option->getRequired()) {
    +                                echo 'data-validate="{\'validate-one-required-by-name\':true}"';
    +                            } ?>
                                    name="bundle_option[<?= $block->escapeHtmlAttr($_option->getId()) ?>]"
                                    data-selector="bundle_option[<?= $block->escapeHtmlAttr($_option->getId()) ?>]"
    -                               <?php if ($block->isSelected($_selection)) { echo ' checked="checked"'; } ?>
    -                               <?php if (!$_selection->isSaleable()) { echo ' disabled="disabled"'; } ?>
    -                               value="<?= $block->escapeHtmlAttr($_selection->getSelectionId()) ?>"/>
    +                            <?php if ($block->isSelected($_selection)) { echo ' checked="checked"'; } ?>
    +                            <?php if (!$_selection->isSaleable()) { echo ' disabled="disabled"'; } ?>
    +                               value="<?= $block->escapeHtmlAttr($_selection->getSelectionId()) ?>"
    +                               data-errors-message-box="#validation-message-box-radio"/>
                             <label class="label"
                                    for="bundle-option-<?= $block->escapeHtmlAttr($_option->getId()) ?>-<?= $block->escapeHtmlAttr($_selection->getSelectionId()) ?>">
                                 <span><?= /* @noEscape */ $block->getSelectionTitlePrice($_selection) ?></span>
    @@ -65,21 +70,22 @@ $viewModel = $block->getData('validateQuantityViewModel');
                         </div>
                     <?php endforeach; ?>
                     <div id="bundle-option-<?= $block->escapeHtmlAttr($_option->getId()) ?>-container"></div>
    +                <div id="validation-message-box-radio"></div>
                 <?php endif; ?>
                 <div class="field qty qty-holder">
                     <label class="label" for="bundle-option-<?= $block->escapeHtmlAttr($_option->getId()) ?>-qty-input">
                         <span><?= $block->escapeHtml(__('Quantity')) ?></span>
                     </label>
                     <div class="control">
                         <input <?php if (!$_canChangeQty) { echo ' disabled="disabled"'; } ?>
    -                           id="bundle-option-<?= $block->escapeHtmlAttr($_option->getId()) ?>-qty-input"
    -                           class="input-text qty<?php if (!$_canChangeQty) { echo ' qty-disabled'; } ?>"
    -                           type="number"
    -                           min="0"
    -                           data-validate="<?= $block->escapeHtmlAttr($viewModel->getQuantityValidators()) ?>"
    -                           name="bundle_option_qty[<?= $block->escapeHtmlAttr($_option->getId()) ?>]"
    -                           data-selector="bundle_option_qty[<?= $block->escapeHtmlAttr($_option->getId()) ?>]"
    -                           value="<?= $block->escapeHtmlAttr($_defaultQty) ?>"/>
    +                        id="bundle-option-<?= $block->escapeHtmlAttr($_option->getId()) ?>-qty-input"
    +                        class="input-text qty<?php if (!$_canChangeQty) { echo ' qty-disabled'; } ?>"
    +                        type="number"
    +                        min="0"
    +                        data-validate="<?= $block->escapeHtmlAttr($viewModel->getQuantityValidators()) ?>"
    +                        name="bundle_option_qty[<?= $block->escapeHtmlAttr($_option->getId()) ?>]"
    +                        data-selector="bundle_option_qty[<?= $block->escapeHtmlAttr($_option->getId()) ?>]"
    +                        value="<?= $block->escapeHtmlAttr($_defaultQty) ?>"/>
                     </div>
                 </div>
             </div>
    
  • app/code/Magento/CacheInvalidate/composer.json+9 7 modified
    @@ -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.3",
         "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 @@
             }
         }
     }
    +
    
  • app/code/Magento/Captcha/composer.json+14 12 modified
    @@ -1,26 +1,27 @@
     {
         "name": "magento/module-captcha",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.5-p3",
         "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.12",
             "laminas/laminas-db": "^2.13.4"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -30,3 +31,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Captcha/Test/Mftf/Test/StorefrontCaptchaOnOnepageCheckoutPyamentTest.xml+2 0 modified
    @@ -21,6 +21,7 @@
                 <group value="storefront_captcha_enabled"/>
             </annotations>
             <before>
    +            <magentoCLI command="config:set checkout/options/enable_guest_checkout_login 1" stepKey="EnablingGuestCheckoutLogin"/>
                 <!-- Create Simple Product -->
                 <createData entity="SimpleProduct2" stepKey="createSimpleProduct">
                     <field key="price">20</field>
    @@ -62,6 +63,7 @@
     
                 <!-- Delete customer -->
                 <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/>
    +            <magentoCLI command="config:set checkout/options/enable_guest_checkout_login 0" stepKey="DisablingGuestCheckoutLogin"/>
             </after>
     
             <!-- Reindex and flush cache -->
    
  • app/code/Magento/CardinalCommerce/composer.json+11 9 modified
    @@ -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.3",
         "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 @@
             }
         }
     }
    +
    
  • app/code/Magento/CatalogAnalytics/composer.json+8 6 modified
    @@ -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 @@
             }
         }
     }
    +
    
  • app/code/Magento/CatalogCmsGraphQl/composer.json+12 10 modified
    @@ -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 @@
             }
         }
     }
    +
    
  • app/code/Magento/Catalog/composer.json+36 34 modified
    @@ -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.5-p6",
         "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 @@
             }
         }
     }
    +
    
  • app/code/Magento/Catalog/Controller/Adminhtml/Product/NewAction.php+21 4 modified
    @@ -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');
             }
    
  • app/code/Magento/CatalogCustomerGraphQl/composer.json+9 7 modified
    @@ -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 @@
             }
         }
     }
    +
    
  • app/code/Magento/CatalogGraphQl/composer.json+19 17 modified
    @@ -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.5",
         "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 @@
             }
         }
     }
    +
    
  • app/code/Magento/Catalog/i18n/en_US.csv+1 0 modified
    @@ -816,4 +816,5 @@ 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."
     
    
  • app/code/Magento/CatalogImportExport/composer.json+18 16 modified
    @@ -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.5-p3",
         "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 @@
             }
         }
     }
    +
    
  • app/code/Magento/CatalogImportExport/Model/Import/Product.php+11 0 modified
    @@ -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';
    @@ -227,6 +228,7 @@ class Product extends AbstractEntity
          * Links attribute name-to-link type ID.
          *
          * @deprecated 101.1.0 use DI for LinkProcessor class if you want to add additional types
    +     * @see LinkProcessor::fetchProductLinks
          *
          * @var array
          */
    @@ -548,6 +550,7 @@ class Product extends AbstractEntity
         /**
          * @var \Magento\CatalogInventory\Model\ResourceModel\Stock\ItemFactory
          * @deprecated 101.0.0 this variable isn't used anymore.
    +     * @see 101.0.0
          */
         protected $_stockResItemFac;
     
    @@ -612,6 +615,7 @@ class Product extends AbstractEntity
         /**
          * @var array
          * @deprecated 100.0.3
    +     * @see 100.0.3
          * @since 100.0.3
          */
         protected $productUrlKeys = [];
    @@ -1280,6 +1284,7 @@ protected function _prepareRowForDb(array $rowData)
          * Must be called after ALL products saving done.
          *
          * @deprecated 101.1.0 use linkProcessor Directly
    +     * @see LinkProcessor::saveLinks
          *
          * @return $this
          */
    @@ -1489,6 +1494,7 @@ private function getNewSkuFieldsForSelect()
          * @return void
          * @since 100.0.4
          * @deprecated 100.2.3
    +     * @see 100.2.3
          */
         protected function initMediaGalleryResources()
         {
    @@ -1611,6 +1617,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);
     
    
  • app/code/Magento/CatalogInventory/composer.json+15 13 modified
    @@ -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.5",
         "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-metapackage"
     }
    +
    
  • app/code/Magento/CatalogInventoryGraphQl/composer.json+10 8 modified
    @@ -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.2",
    +    "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 @@
             }
         }
     }
    +
    
  • app/code/Magento/CatalogRule/composer.json+17 15 modified
    @@ -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.5-p5",
         "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 @@
             }
         }
     }
    +
    
  • app/code/Magento/CatalogRuleConfigurable/composer.json+12 10 modified
    @@ -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.4",
         "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 @@
             }
         }
     }
    +
    
  • app/code/Magento/CatalogRuleGraphQl/composer.json+8 6 modified
    @@ -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 @@
             }
         }
     }
    +
    
  • app/code/Magento/CatalogRule/Test/Mftf/Test/AdminDeleteCatalogPriceRuleEntityTest/AdminDeleteCatalogPriceRuleEntityFromSimpleProductTest.xml+2 0 modified
    @@ -27,6 +27,8 @@
     
                 <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin1"/>
     
    +            <actionGroup ref="AdminCartPriceRuleDeleteAllActionGroup" stepKey="deleteAllCartPriceRules"/>
    +            
                 <amOnPage url="{{AdminNewCatalogPriceRulePage.url}}" stepKey="openNewCatalogPriceRulePage"/>
                 <waitForPageLoad stepKey="waitForPageToLoad1"/>
     
    
  • app/code/Magento/CatalogSearch/composer.json+20 18 modified
    @@ -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.5",
         "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 @@
             }
         }
     }
    +
    
  • app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCreateSimpleProductWithTextOptionCharLimitActionGroup.xml+1 0 modified
    @@ -29,6 +29,7 @@
             <fillField userInput="{{simpleProduct.urlKey}}" selector="{{AdminProductSEOSection.urlKeyInput}}" stepKey="fillUrlKey"/>
     
             <click selector="{{AdminProductCustomizableOptionsSection.customizableOptions}}" stepKey="openCustomOptionsSection"/>
    +        <waitForElementClickable selector="{{AdminProductCustomizableOptionsSection.addOptionBtn}}" stepKey="waitForAddOption"/>
             <click selector="{{AdminProductCustomizableOptionsSection.addOptionBtn}}" stepKey="clickAddOption"/>
             <fillField selector="{{AdminProductCustomizableOptionsSection.optionTitleInput('0')}}" userInput="option1" stepKey="fillOptionTitle"/>
             <click selector="{{AdminProductCustomizableOptionsSection.optionTypeOpenDropDown}}" stepKey="openTypeDropDown"/>
    
  • app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateNewProductAttributeSection.xml+2 0 modified
    @@ -30,5 +30,7 @@
             <element name="allowHtmlTags" type="checkbox" selector="//input[contains(@name, 'is_html_allowed_on_front')]/..//label"/>
             <element name="visibleOnStorefront" type="checkbox" selector="//input[contains(@name, 'is_visible_on_front')]/..//label"/>
             <element name="sortProductListing" type="checkbox" selector="//input[contains(@name, 'is_visible_on_front')]/..//label"/>
    +        <element name="visualSwatchAdmin" type="input" selector="input[name='optionvisual[value][option_{{var}}][1]']" parameterized="true"/>
    +        <element name="visualSwatchStore" type="input" selector="input[name='optionvisual[value][option_{{var}}][0]']" parameterized="true"/>
         </section>
     </sections>
    
  • app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryMainSection.xml+1 0 modified
    @@ -40,5 +40,6 @@
             <element name="productOptionList" type="text" selector="#narrow-by-list"/>
             <element name="productNameByPosition" type="text" selector=".products-grid li:nth-of-type({{position}}) .product-item-name a" parameterized="true"/>
             <element name="sidebarAdditional" type="block" selector="#maincontent .sidebar.sidebar-additional"/>
    +        <element name="searchStore" type="input" selector="//div/input[@id='search']" />
         </section>
     </sections>
    
  • app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductTest/AdminCreateSimpleProductNegativePriceTest.xml+1 1 modified
    @@ -32,7 +32,7 @@
             </actionGroup>
             <actionGroup ref="AdminProductFormSaveActionGroup" stepKey="clickSave"/>
             <actionGroup ref="AssertAdminValidationErrorAppearedForPriceFieldOnProductEditPageActionGroup" stepKey="seePriceValidationError">
    -            <argument name="errorMessage" value="Please enter a number 0 or greater in this field."/>
    +            <argument name="errorMessage" value="Please enter a number 0 or greater, without comma in this field."/>
             </actionGroup>
         </test>
     </tests>
    
  • app/code/Magento/Catalog/Test/Mftf/Test/AdminRestrictedUserAddCategoryFromProductPageTest.xml+1 0 modified
    @@ -97,6 +97,7 @@
             </assertStringContainsString>
             <!--Remove the category from the product and assert that it removed-->
             <comment userInput="Remove the category from the product and assert that it removed" stepKey="assertCategoryRemoved"/>
    +        <scrollTo selector='//label/span[contains(text(),"Quantity")]' stepKey="scrollToCategorySection"/>
             <actionGroup ref="RemoveCategoryFromProductActionGroup" stepKey="removeCategoryFromProduct">
                 <argument name="categoryName" value="$$createCategory.name$$"/>
             </actionGroup>
    
  • app/code/Magento/Catalog/Test/Mftf/Test/SaveProductWithCustomOptionsSecondWebsiteTest.xml+17 7 modified
    @@ -17,6 +17,7 @@
                 <severity value="BLOCKER"/>
                 <testCaseId value="MC-25687"/>
                 <group value="product"/>
    +            <group value="cloud"/>
             </annotations>
             <before>
                 <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/>
    @@ -34,7 +35,9 @@
                     <argument name="StoreGroup" value="customStoreGroup"/>
                     <argument name="customStore" value="customStore"/>
                 </actionGroup>
    -            <magentoCron groups="index" stepKey="reindex"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
     
                 <actionGroup ref="EnableWebUrlOptionsActionGroup" stepKey="addStoreCodeToUrls"/>
             </before>
    @@ -44,7 +47,9 @@
                 <actionGroup ref="AdminDeleteWebsiteActionGroup" stepKey="deleteTestWebsite">
                     <argument name="websiteName" value="{{customWebsite.name}}"/>
                 </actionGroup>
    -            <magentoCron groups="index" stepKey="reindex"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
                 <actionGroup ref="AdminClearFiltersActionGroup" stepKey="clearFilters"/>
                 <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/>
             </after>
    @@ -65,33 +70,37 @@
             <conditionalClick selector="{{AdminProductCustomizableOptionsSection.customizableOptions}}" dependentSelector="{{AdminProductCustomizableOptionsSection.checkIfCustomizableOptionsTabOpen}}" visible="true" stepKey="clickIfContentTabCloses2"/>
             <click selector="{{AdminProductCustomizableOptionsSection.addOptionBtn}}" stepKey="clickAddOption"/>
             <waitForPageLoad stepKey="waitAfterAddOption"/>
    -        <fillField selector="input[name='product[options][0][title]']" userInput="Radio Option" stepKey="fillOptionTitle"/>
    -        <click selector=".admin__dynamic-rows[data-index='options'] .action-select" stepKey="openOptionTypeDropDown"/>
    +        <waitForElementVisible selector="{{AdminProductCustomizableOptionsSection.optionTitleInput('0')}}" stepKey="waitForOptionTitle"/>
    +        <fillField selector="{{AdminProductCustomizableOptionsSection.optionTitleInput('0')}}" userInput="Radio Option" stepKey="fillOptionTitle"/>
    +        <click selector="{{AdminProductCustomizableOptionsSection.optionTypeOpenDropDown}}" stepKey="openOptionTypeDropDown"/>
             <click selector=".admin__dynamic-rows[data-index='options'] .action-menu._active li:nth-of-type(3) li:nth-of-type(2)" stepKey="selectRadioButtonType"/>
     
             <!--Add Option Values -->
             <click selector="{{AdminProductCustomizableOptionsSection.clickAddValue('Radio Option')}}" stepKey="clickAddValue1"/>
    +        <waitForElementVisible selector="{{AdminProductCustomizableOptionsSection.fillOptionValueTitle('Radio Option', '0')}}" stepKey="waitForOptionValueTitle1"/>
             <fillField selector="{{AdminProductCustomizableOptionsSection.fillOptionValueTitle('Radio Option', '0')}}" userInput="option 1" stepKey="fillOptionValueTitle1"/>
             <fillField selector="{{AdminProductCustomizableOptionsSection.fillOptionValuePrice('Radio Option', '0')}}" userInput="5" stepKey="fillOptionValuePrice1"/>
     
             <click selector="{{AdminProductCustomizableOptionsSection.clickAddValue('Radio Option')}}" stepKey="clickAddValue2"/>
    +        <waitForElementVisible selector="{{AdminProductCustomizableOptionsSection.fillOptionValueTitle('Radio Option', '1')}}" stepKey="waitForOptionValueTitle2"/>
             <fillField selector="{{AdminProductCustomizableOptionsSection.fillOptionValueTitle('Radio Option', '1')}}" userInput="option 2" stepKey="fillOptionValueTitle2"/>
             <fillField selector="{{AdminProductCustomizableOptionsSection.fillOptionValuePrice('Radio Option', '1')}}" userInput="6" stepKey="fillOptionValuePrice2"/>
     
             <click selector="{{AdminProductCustomizableOptionsSection.clickAddValue('Radio Option')}}" stepKey="clickAddValue3"/>
    +        <waitForElementVisible selector="{{AdminProductCustomizableOptionsSection.fillOptionValueTitle('Radio Option', '2')}}" stepKey="waitForOptionValueTitle3"/>
             <fillField selector="{{AdminProductCustomizableOptionsSection.fillOptionValueTitle('Radio Option', '2')}}" userInput="option 3" stepKey="fillOptionValueTitle3"/>
             <fillField selector="{{AdminProductCustomizableOptionsSection.fillOptionValuePrice('Radio Option', '2')}}" userInput="7" stepKey="fillOptionValuePrice3"/>
     
             <!-- Save the product with custom options -->
             <actionGroup ref="AdminProductFormSaveActionGroup" stepKey="clickSaveButton"/>
    -        <seeElement selector="{{AdminProductMessagesSection.successMessage}}" stepKey="seeProductSavedMessage"/>
    +        <waitForElementVisible selector="{{AdminProductMessagesSection.successMessage}}" stepKey="seeProductSavedMessage"/>
     
             <!-- Add this product to second website -->
             <click selector="{{ProductInWebsitesSection.sectionHeader}}" stepKey="openProductInWebsitesSection1"/>
             <click selector="{{ProductInWebsitesSection.website(customWebsite.name)}}" stepKey="selectSecondWebsite"/>
             <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSave"/>
    -        <waitForLoadingMaskToDisappear stepKey="waitForProductPagetoSaveAgain"/>
    -        <seeElement selector="{{AdminProductMessagesSection.successMessage}}" stepKey="seeSaveProductMessageAgain"/>
    +        <waitForPageLoad stepKey="waitForProductPagetoSaveAgain"/>
    +        <waitForElementVisible selector="{{AdminProductMessagesSection.successMessage}}" stepKey="seeSaveProductMessageAgain"/>
     
             <!-- Verify the product's custom options -->
             <waitForElement selector="{{AdminProductCustomizableOptionsSection.customizableOptions}}" stepKey="waitForSection"/>
    @@ -100,6 +109,7 @@
             <executeJS function="window.scrollTo({top: {$sectionPosition}-{$floatingHeaderHeight}})" stepKey="scrollToOptions"/>
             <click selector="{{AdminProductCustomizableOptionsSection.customizableOptions}}" stepKey="openCustomOptionsSection2"/>
             <waitForElementVisible selector=".admin__dynamic-rows[data-index='values'] tr.data-row" stepKey="waitForRowsToBeVisible"/>
    +        <waitForPageLoad stepKey="waitForLoadingMaskToDisappear" />
             <seeNumberOfElements selector=".admin__dynamic-rows[data-index='values'] tr.data-row" userInput="3" stepKey="see4RowsOfOptions"/>
         </test>
     </tests>
    
  • app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/NewActionTest.php+77 13 modified
    @@ -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,78 @@ 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()
    +    /**
    +     * Test execute method input validation.
    +     *
    +     * @param string $value
    +     * @param bool $exceptionThrown
    +     * @dataProvider validationCases
    +     */
    +    public function testExecute(string $value, bool $exceptionThrown): void
    +    {
    +        if ($exceptionThrown) {
    +            $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());
    +        } else {
    +            $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));
    +        }
    +    }
    +
    +    /**
    +     * Validation cases.
    +     *
    +     * @return array
    +     */
    +    public function validationCases(): array
         {
    -        $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();
    +        return [
    +            'execute-with-exception' => ['simple\' and true()]|*[self%3a%3ahandle%20or%20self%3a%3alayout',true],
    +            'execute-without-exception' => ['catalog_product_new',false]
    +        ];
         }
     }
    
  • app/code/Magento/Catalog/Test/Unit/Pricing/Render/FinalPriceBoxTest.php+33 0 modified
    @@ -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);
         }
    
  • app/code/Magento/CatalogUrlRewrite/composer.json+17 15 modified
    @@ -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.5",
         "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 @@
             }
         }
     }
    +
    
  • app/code/Magento/CatalogUrlRewriteGraphQl/composer.json+14 12 modified
    @@ -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 @@
             }
         }
     }
    +
    
  • app/code/Magento/Catalog/view/adminhtml/web/js/category-tree.js+2 3 modified
    @@ -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
                     },
    
  • app/code/Magento/CatalogWidget/composer.json+17 15 modified
    @@ -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.5-p1",
         "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 @@
             }
         }
     }
    +
    
  • app/code/Magento/CatalogWidget/Test/Mftf/Test/StorefrontProductGridUIUpdatesOnDesktopTest.xml+1 0 modified
    @@ -61,6 +61,7 @@
             <actionGroup ref="StorefrontGoToCMSPageActionGroup" stepKey="navigateAndOpenCreatedCmsPage">
                 <argument name="identifier" value="$createCmsPage.identifier$"/>
             </actionGroup>
    +        <moveMouseOver selector="{{StorefrontCategoryMainSection.searchStore}}" stepKey="hoverSearchTextField"/>
             <actionGroup ref="AssertStorefrontProductControlsAreNotVisibleWithoutHoverActionGroup" stepKey="assertProductControlsAreNotVisibleWithoutHoverOnCmsPage"/>
             <seeElement selector="{{StorefrontCategoryMainSection.productLinkByHref($createFirstSimpleProduct.custom_attributes[url_key]$)}}" stepKey="assertProductName"/>
             <seeElement selector="{{StorefrontCategoryMainSection.productPriceByName($createFirstSimpleProduct.name$)}}" stepKey="assertProductPrice"/>
    
  • app/code/Magento/CheckoutAgreements/composer.json+12 10 modified
    @@ -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.4-p5",
         "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 @@
             }
         }
     }
    +
    
  • app/code/Magento/CheckoutAgreementsGraphQl/composer.json+10 8 modified
    @@ -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 @@
             }
         }
     }
    +
    
  • app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/AdminOpenEditPageTermsConditionsByNameActionGroup.xml+26 0 added
    @@ -0,0 +1,26 @@
    +<?xml version="1.0" encoding="UTF-8"?>
    +<!--
    + /**
    +  * Copyright © Magento, Inc. All rights reserved.
    +  * See COPYING.txt for license details.
    +  */
    +-->
    +
    +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    +              xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd">
    +    <actionGroup name="AdminOpenEditPageTermsConditionsByNameActionGroup">
    +        <annotations>
    +            <description>Opens Edit Page of Terms and Conditions By Provided Name</description>
    +        </annotations>
    +        <arguments>
    +            <argument name="termName" type="string"/>
    +        </arguments>
    +
    +        <fillField selector="{{AdminTermGridSection.filterByTermName}}" userInput="{{termName}}" stepKey="fillTermNameFilter"/>
    +        <click selector="{{AdminTermGridSection.searchButton}}" stepKey="clickSearchButton"/>
    +        <waitForPageLoad stepKey="waitForPageLoad" />
    +        <grabAttributeFrom selector="{{AdminTermGridSection.firstRow}}" userInput="title" stepKey="termsEditUrl" />
    +        <amOnUrl url="{$termsEditUrl}" stepKey="openTermsEditPage" />
    +        <waitForPageLoad stepKey="waitForEditTermPageLoad"/>
    +    </actionGroup>
    +</actionGroups>
    
  • app/code/Magento/CheckoutAgreements/Test/Mftf/Section/AdminTermGridSection.xml+1 0 modified
    @@ -11,6 +11,7 @@
             <element name="searchButton" type="button" selector="//div[contains(@class,'admin__data-grid-header')]//div[contains(@class,'admin__filter-actions')]/button[1]"/>
             <element name="resetButton" type="button" selector="//div[contains(@class,'admin__data-grid-header')]//div[contains(@class,'admin__filter-actions')]/button[2]"/>
             <element name="filterByTermName" type="input" selector="#agreementGrid_filter_name"/>
    +        <element name="firstRow" type="block" selector=".data-grid>tbody>tr"/>
             <element name="firstRowConditionName" type="text" selector=".data-grid>tbody>tr>td.col-name"/>
             <element name="firstRowConditionId" type="text" selector=".data-grid>tbody>tr>td.col-id.col-agreement_id"/>
             <element name="successMessage" type="text" selector=".message-success"/>
    
  • app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminUpdateDisabledHtmlTermEntityTest.xml+1 1 modified
    @@ -32,7 +32,7 @@
                 <deleteData createDataKey="createProduct" stepKey="deletedProduct"/>
     
                 <actionGroup ref="AdminTermsConditionsOpenGridActionGroup" stepKey="openTermsGridToDelete"/>
    -            <actionGroup ref="AdminTermsConditionsEditTermByNameActionGroup" stepKey="openTermToDelete">
    +            <actionGroup ref="AdminOpenEditPageTermsConditionsByNameActionGroup" stepKey="openTermToDelete">
                     <argument name="termName" value="{{activeTextTerm.name}}"/>
                 </actionGroup>
                 <actionGroup ref="AdminTermsConditionsDeleteTermByNameActionGroup" stepKey="deleteOpenedTerm"/>
    
  • app/code/Magento/Checkout/composer.json+30 27 modified
    @@ -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.5-p8",
         "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 @@
             }
         }
     }
    +
    
  • app/code/Magento/Checkout/etc/adminhtml/system.xml+18 1 modified
    @@ -13,6 +13,11 @@
                 <resource>Magento_Checkout::checkout</resource>
                 <group id="options" translate="label" type="text" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="1">
                     <label>Checkout Options</label>
    +                <field id="enable_guest_checkout_login" translate="label" type="select" sortOrder="4" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1">
    +                    <label>Enable Guest Checkout Login</label>
    +                    <source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
    +                    <comment>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.</comment>
    +                </field>
                     <field id="onepage_checkout_enabled" translate="label" type="select" sortOrder="5" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1">
                         <label>Enable Onepage Checkout</label>
                         <source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
    @@ -23,7 +28,7 @@
                     </field>
                     <field id="display_billing_address_on" translate="label" type="select" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1">
                         <label>Display Billing Address On</label>
    -                    <source_model>\Magento\Checkout\Model\Adminhtml\BillingAddressDisplayOptions</source_model>
    +                    <source_model>Magento\Checkout\Model\Adminhtml\BillingAddressDisplayOptions</source_model>
                     </field>
                     <field id="max_items_display_count" translate="label" type="text" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1">
                         <label>Maximum Number of Items to Display in Order Summary</label>
    @@ -101,5 +106,17 @@
                     </field>
                 </group>
             </section>
    +        <section id="csp">
    +            <group id="mode">
    +                <group id="storefront_checkout_index_index" translate="label" sortOrder="4" showInDefault="1" showInWebsite="1" showInStore="1">
    +                    <label>Storefront > One Page Checkout</label>
    +                    <field id="report_uri" translate="label" type="text" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="1">
    +                        <label>Report URI</label>
    +                        <comment>If empty, Default Report URI for storefront will be used.</comment>
    +                        <validate>validate-url</validate>
    +                    </field>
    +                </group>
    +            </group>
    +        </section>
         </system>
     </config>
    
  • app/code/Magento/Checkout/etc/config.xml+16 0 modified
    @@ -9,6 +9,7 @@
         <default>
             <checkout>
                 <options>
    +                <enable_guest_checkout_login>0</enable_guest_checkout_login>
                     <onepage_checkout_enabled>1</onepage_checkout_enabled>
                     <guest_checkout>1</guest_checkout>
                     <display_billing_address_on>0</display_billing_address_on>
    @@ -55,5 +56,20 @@
                     </shown_to_logged_in_user>
                 </captcha>
             </customer>
    +        <csp>
    +            <mode>
    +                <storefront_checkout_index_index>
    +                    <report_only>0</report_only>
    +                </storefront_checkout_index_index>
    +            </mode>
    +            <policies>
    +                <storefront_checkout_index_index>
    +                    <scripts>
    +                        <inline>0</inline>
    +                        <event_handlers>1</event_handlers>
    +                    </scripts>
    +                </storefront_checkout_index_index>
    +            </policies>
    +        </csp>
         </default>
     </config>
    
  • app/code/Magento/Checkout/etc/frontend/events.xml+4 0 modified
    @@ -12,4 +12,8 @@
         <event name="customer_logout">
             <observer name="unsetAll" instance="Magento\Checkout\Observer\UnsetAllObserver" />
         </event>
    +    <event name="controller_action_predispatch_checkout_index_index">
    +        <observer name="cps_storefront_checkout_index_index_predispatch"
    +                  instance="Magento\Checkout\Observer\CspPolicyObserver"/>
    +    </event>
     </config>
    
  • app/code/Magento/Checkout/Model/ShippingInformationManagement.php+20 14 modified
    @@ -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();
    
  • app/code/Magento/Checkout/Observer/CspPolicyObserver.php+69 0 added
    @@ -0,0 +1,69 @@
    +<?php
    +/**
    + * Copyright © Magento, Inc. All rights reserved.
    + * See COPYING.txt for license details.
    + */
    +declare(strict_types=1);
    +
    +namespace Magento\Checkout\Observer;
    +
    +use Magento\Csp\Model\Collector\DynamicCollector;
    +use Magento\Csp\Model\Policy\FetchPolicy;
    +use Magento\Framework\Event\Observer;
    +use Magento\Framework\Event\ObserverInterface;
    +use Magento\Framework\Translate\InlineInterface;
    +
    +/**
    + * Observer for adding CSP policy for inline translation
    + */
    +class CspPolicyObserver implements ObserverInterface
    +{
    +    /**
    +     * @var InlineInterface
    +     */
    +    private InlineInterface $inlineTranslate;
    +
    +    /**
    +     * @var DynamicCollector
    +     */
    +    private DynamicCollector $dynamicCollector;
    +
    +    /**
    +     * @param InlineInterface $inlineTranslate
    +     * @param DynamicCollector $dynamicCollector
    +     */
    +    public function __construct(InlineInterface $inlineTranslate, DynamicCollector $dynamicCollector)
    +    {
    +        $this->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);
    +        }
    +    }
    +}
    
  • app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertMiniCartEmptyActionGroup.xml+2 1 modified
    @@ -13,8 +13,9 @@
                 <description>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.</description>
             </annotations>
     
    +        <wait stepKey="waitForMinicartAjaxCallToComplete" time="15"/>
             <dontSeeElement selector="{{StorefrontMinicartSection.productCount}}" stepKey="dontSeeMinicartProductCount"/>
             <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="expandMinicart"/>
    -        <see selector="{{StorefrontMinicartSection.minicartContent}}" userInput="You have no items in your shopping cart." stepKey="seeEmptyCartMessage"/>
    +        <see selector="{{StorefrontMinicartSection.messageEmptyCart}}" userInput="You have no items in your shopping cart." stepKey="seeEmptyCartMessage"/>
         </actionGroup>
     </actionGroups>
    
  • app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutAsCustomerUsingDefaultAddressTest.xml+2 1 modified
    @@ -20,11 +20,11 @@
                 <group value="mtf_migrated"/>
             </annotations>
             <before>
    +            <magentoCLI command="config:set checkout/options/enable_guest_checkout_login 1" stepKey="EnablingGuestCheckoutLogin"/>
                 <!-- Create Simple Product -->
                 <createData entity="SimpleProduct2" stepKey="createSimpleProduct">
                     <field key="price">560</field>
                 </createData>
    -
                 <!-- Create customer -->
                 <createData entity="Simple_US_Customer_Multiple_Addresses" stepKey="createCustomer"/>
             </before>
    @@ -40,6 +40,7 @@
     
                 <!-- Delete customer -->
                 <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/>
    +            <magentoCLI command="config:set checkout/options/enable_guest_checkout_login 0" stepKey="DisablingGuestCheckoutLogin"/>
             </after>
     
             <!-- Add Simple Product to cart -->
    
  • app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutAsCustomerUsingNonDefaultAddressTest.xml+2 0 modified
    @@ -20,6 +20,7 @@
                 <group value="mtf_migrated"/>
             </annotations>
             <before>
    +            <magentoCLI command="config:set checkout/options/enable_guest_checkout_login 1" stepKey="EnablingGuestCheckoutLogin"/>
                 <!-- Create Simple Product -->
                 <createData entity="SimpleProduct2" stepKey="createSimpleProduct">
                     <field key="price">560</field>
    @@ -40,6 +41,7 @@
     
                 <!-- Delete customer -->
                 <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/>
    +            <magentoCLI command="config:set checkout/options/enable_guest_checkout_login 0" stepKey="DisablingGuestCheckoutLogin"/>
             </after>
     
             <!-- Add Simple Product to cart -->
    
  • app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckoutWithSpecialPriceProductsTest.xml+2 0 modified
    @@ -18,6 +18,7 @@
             </annotations>
     
             <before>
    +            <magentoCLI command="config:set checkout/options/enable_guest_checkout_login 1" stepKey="EnablingGuestCheckoutLogin"/>
                 <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/>
                 <createData entity="Simple_US_Customer" stepKey="createCustomer"/>
                 <createData entity="defaultSimpleProduct" stepKey="simpleProduct">
    @@ -105,6 +106,7 @@
     
                 <!-- Reindex invalidated indices after product attribute has been created/deleted -->
                 <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/>
    +            <magentoCLI command="config:set checkout/options/enable_guest_checkout_login 0" stepKey="DisablingGuestCheckoutLogin"/>
             </after>
     
             <!--Open Product page in StoreFront and assert product and price range -->
    
  • app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerLoginDuringCheckoutTest.xml+2 1 modified
    @@ -19,6 +19,7 @@
                 <group value="OnePageCheckout"/>
             </annotations>
             <before>
    +            <magentoCLI command="config:set checkout/options/enable_guest_checkout_login 1" stepKey="EnablingGuestCheckoutLogin"/>
                 <!-- Create simple product -->
                 <createData entity="SimpleProduct2" stepKey="createProduct"/>
     
    @@ -39,9 +40,9 @@
                 <actionGroup ref="AdminDeleteCustomerActionGroup" stepKey="deleteCustomer">
                     <argument name="customerEmail" value="CustomerEntityOne.email"/>
                 </actionGroup>
    -
                 <!-- Logout admin -->
                 <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/>
    +            <magentoCLI command="config:set checkout/options/enable_guest_checkout_login 0" stepKey="DisablingGuestCheckoutLogin"/>
             </after>
             <!-- Go to Storefront as Guest and create new account -->
             <actionGroup ref="StorefrontOpenCustomerAccountCreatePageActionGroup" stepKey="openCreateAccountPage"/>
    
  • app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUpdatePriceInShoppingCartAfterProductSaveTest.xml+3 3 modified
    @@ -66,12 +66,12 @@
             <!--Check price-->
             <actionGroup ref="ReloadPageActionGroup" stepKey="reloadPage"/>
             <comment userInput="Replacing reload action and preserve Backward Compatibility" stepKey="waitForCheckoutPageReload"/>
    -        <!-- change below waitForElementVisible action to waitForElementClickable to prevent flakiness once MQE-3210 is complete -->
    +        <comment userInput="Preserve BIC" stepKey="waitForCartItemsActive1"/>
    +        <comment userInput="Preserve BIC" stepKey="waitForOrderSummaryLoad2"/>
             <waitForElementVisible selector="{{CheckoutPaymentSection.cartItemsArea}}" stepKey="waitForCartItemsVisible1"/>
    -        <waitForElementNotVisible selector="{{CheckoutPaymentSection.cartItemsAreaActive}}" stepKey="waitForCartItemsActive1"/>
    -        <waitForPageLoad stepKey="waitForOrderSummaryLoad2"/>
             <click selector="{{CheckoutPaymentSection.cartItemsArea}}" stepKey="openItemProductBlock1"/>
             <waitForPageLoad stepKey="waitForCartItemLoaded"/>
    +        <waitForElementVisible selector="{{CheckoutPaymentSection.orderSummarySubtotal}}" stepKey="waitForSummarySubtotalVisible"/>
             <see userInput="$120.00" selector="{{CheckoutPaymentSection.orderSummarySubtotal}}" stepKey="checkSummarySubtotal1"/>
             <see userInput="$120.00" selector="{{CheckoutPaymentSection.productItemPriceByName($$createSimpleProduct.name$$)}}" stepKey="checkItemPrice1"/>
         </test>
    
  • app/code/Magento/Checkout/Test/Mftf/Test/StorefrontVerifySecureURLRedirectCheckoutTest.xml+3 1 modified
    @@ -45,8 +45,10 @@
             </after>
             <executeJS function="return window.location.host" stepKey="hostname"/>
             <amOnUrl url="http://{$hostname}/checkout" stepKey="goToUnsecureCheckoutURL"/>
    -        <seeCurrentUrlEquals url="https://{$hostname}/checkout" stepKey="seeSecureCheckoutURL"/>
    +        <waitForPageLoad stepKey="waitForCheckoutShippingPageToLoad" />
    +        <seeCurrentUrlMatches regex="~https://$hostname/checkout(?:#shipping)?~" stepKey="seeSecureCheckoutURL"/>
             <amOnUrl url="http://{$hostname}/checkout/sidebar" stepKey="goToUnsecureCheckoutSidebarURL"/>
    +        <waitForPageLoad stepKey="waitForUnsecureCheckoutSidebarPageToLoad"/>
             <seeCurrentUrlEquals url="http://{$hostname}/checkout/sidebar" stepKey="seeUnsecureCheckoutSidebarURL"/>
         </test>
     </tests>
    
  • app/code/Magento/Cms/composer.json+18 16 modified
    @@ -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.5-p1",
         "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 @@
             }
         }
     }
    +
    
  • app/code/Magento/Cms/Controller/Noroute/Index.php+5 2 modified
    @@ -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 */
    
  • app/code/Magento/CmsGraphQl/composer.json+13 11 modified
    @@ -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 @@
             }
         }
     }
    +
    
  • app/code/Magento/Cms/Test/Unit/Controller/Noroute/IndexTest.php+7 5 modified
    @@ -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(
    
  • app/code/Magento/CmsUrlRewrite/composer.json+11 9 modified
    @@ -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.4",
         "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 @@
             }
         }
     }
    +
    
  • app/code/Magento/CmsUrlRewriteGraphQl/composer.json+13 11 modified
    @@ -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.3",
         "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 @@
             }
         }
     }
    +
    
  • app/code/Magento/CompareListGraphQl/composer.json+8 6 modified
    @@ -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 @@
             }
         }
     }
    +
    
  • app/code/Magento/Config/Block/System/Config/Form/Field/File.php+3 1 modified
    @@ -9,6 +9,8 @@
      *
      * @author     Magento Core Team <core@magentocommerce.com>
      */
    +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 .= '<div>' . $this->getValue() . ' ';
    +            $html .= '<div>' . $this->_escaper->escapeHtml($this->getValue()) . ' ';
                 $html .= '<input type="checkbox" name="' .
                     parent::getName() .
                     '[delete]" value="1" class="checkbox" id="' .
    
  • app/code/Magento/Config/composer.json+15 13 modified
    @@ -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.5-p7",
         "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 @@
             }
         }
     }
    +
    
  • app/code/Magento/Config/i18n/en_US.csv+1 0 modified
    @@ -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"
    
  • app/code/Magento/Config/Model/Config/Backend/File.php+20 1 modified
    @@ -3,6 +3,8 @@
      * Copyright © Magento, Inc. All rights reserved.
      * See COPYING.txt for license details.
      */
    +declare(strict_types=1);
    +
     namespace Magento\Config\Model\Config\Backend;
     
     use Exception;
    @@ -114,7 +116,7 @@ public function beforeSave()
                 if (is_array($value) && !empty($value['delete'])) {
                     $this->setValue('');
                 } elseif (is_array($value) && !empty($value['value'])) {
    -                $this->setValue($value['value']);
    +                $this->setValueAfterValidation($value['value']);
                 } else {
                     $this->unsValue();
                 }
    @@ -266,4 +268,21 @@ protected function _getAllowedExtensions()
         {
             return [];
         }
    +
    +    /**
    +     * Validate if the value is intercepted
    +     *
    +     * @param string $value
    +     * @return void
    +     * @throws LocalizedException
    +     */
    +    private function setValueAfterValidation(string $value): void
    +    {
    +        // avoid intercepting value
    +        if (preg_match('/[^a-z0-9_\/\\-\\.]+/i', $value)) {
    +            throw new LocalizedException(__('Invalid file name'));
    +        }
    +
    +        $this->setValue($value);
    +    }
     }
    
  • app/code/Magento/Config/Test/Unit/Block/System/Config/Form/Field/FileTest.php+62 19 modified
    @@ -8,49 +8,85 @@
     namespace Magento\Config\Test\Unit\Block\System\Config\Form\Field;
     
     use Magento\Config\Block\System\Config\Form\Field\File;
    +use Magento\Framework\Data\Form\Element\Factory;
    +use Magento\Framework\Data\Form\Element\CollectionFactory;
     use Magento\Framework\DataObject;
     use Magento\Framework\Escaper;
     use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
    +use PHPUnit\Framework\MockObject\MockObject;
     use PHPUnit\Framework\TestCase;
     
     /**
      * Tests for \Magento\Framework\Data\Form\Field\File
      */
     class FileTest extends TestCase
     {
    +    /**
    +     * XSS value
    +     */
    +    private const XSS_FILE_NAME_TEST = '<img src=x onerror=alert(1)>.crt';
    +
    +    /**
    +     * Input name
    +     */
    +    private const INPUT_NAME_TEST = 'test_name';
    +
         /**
          * @var File
          */
         protected $file;
     
    +    /**
    +     * @var Factory|MockObject
    +     */
    +    private $factoryMock;
    +
    +    /**
    +     * @var CollectionFactory|MockObject
    +     */
    +    private $factoryCollectionMock;
    +
    +    /**
    +     * @var Escaper|MockObject
    +     */
    +    private $escaperMock;
    +
         /**
          * @var array
          */
    -    protected $testData;
    +    protected array $testData = [
    +        'before_element_html' => 'test_before_element_html',
    +        'html_id' => 'test_id',
    +        'name' => 'test_name',
    +        'value' => 'test_value',
    +        'title' => 'test_title',
    +        'disabled' => true,
    +        'after_element_js' => 'test_after_element_js',
    +        'after_element_html' => 'test_after_element_html',
    +        'html_id_prefix' => 'test_id_prefix_',
    +        'html_id_suffix' => '_test_id_suffix',
    +    ];
     
         protected function setUp(): void
         {
             $objectManager = new ObjectManager($this);
     
    -        $this->testData = [
    -            'before_element_html' => 'test_before_element_html',
    -            'html_id' => 'test_id',
    -            'name' => 'test_name',
    -            'value' => 'test_value',
    -            'title' => 'test_title',
    -            'disabled' => true,
    -            'after_element_js'    => 'test_after_element_js',
    -            'after_element_html'  => 'test_after_element_html',
    -            'html_id_prefix'      => 'test_id_prefix_',
    -            'html_id_suffix'      => '_test_id_suffix',
    -        ];
    -
    +        $this->factoryMock = $this->getMockBuilder(Factory::class)
    +            ->disableOriginalConstructor()
    +            ->getMock();
    +        $this->factoryCollectionMock = $this->getMockBuilder(CollectionFactory::class)
    +            ->disableOriginalConstructor()
    +            ->getMock();
    +        $this->escaperMock = $this->getMockBuilder(Escaper::class)
    +            ->disableOriginalConstructor()
    +            ->getMock();
             $this->file = $objectManager->getObject(
                 File::class,
                 [
    -                '_escaper' => $objectManager->getObject(Escaper::class),
    +                'factoryElement' => $this->factoryMock,
    +                'factoryCollection' => $this->factoryCollectionMock,
    +                '_escaper' => $this->escaperMock,
                     'data' => $this->testData,
    -
                 ]
             );
     
    @@ -60,13 +96,20 @@ protected function setUp(): void
             $this->file->setForm($formMock);
         }
     
    -    public function testGetElementHtml()
    +    public function testGetElementHtml(): void
         {
    -        $html = $this->file->getElementHtml();
    -
             $expectedHtmlId = $this->testData['html_id_prefix']
                 . $this->testData['html_id']
                 . $this->testData['html_id_suffix'];
    +        $this->escaperMock->expects($this->any())->method('escapeHtml')->willReturnMap(
    +            [
    +                [$expectedHtmlId, null, $expectedHtmlId],
    +                [self::XSS_FILE_NAME_TEST, null, self::XSS_FILE_NAME_TEST],
    +                [self::INPUT_NAME_TEST, null, self::INPUT_NAME_TEST],
    +            ]
    +        );
    +
    +        $html = $this->file->getElementHtml();
     
             $this->assertStringContainsString('<label class="addbefore" for="' . $expectedHtmlId . '"', $html);
             $this->assertStringContainsString($this->testData['before_element_html'], $html);
    
  • app/code/Magento/ConfigurableImportExport/composer.json+14 12 modified
    @@ -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.3",
         "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 @@
             }
         }
     }
    +
    
  • app/code/Magento/ConfigurableProduct/composer.json+26 24 modified
    @@ -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.5-p1",
         "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 @@
             }
         }
     }
    +
    
  • app/code/Magento/ConfigurableProductGraphQl/composer.json+13 11 modified
    @@ -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.5",
    +    "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 @@
             }
         }
     }
    +
    
  • app/code/Magento/ConfigurableProductSales/composer.json+12 10 modified
    @@ -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 @@
             }
         }
     }
    +
    
  • app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/CreateNewAttributeActionGroup.xml+1 0 modified
    @@ -51,6 +51,7 @@
             <waitForPageLoad stepKey="waitForSavingSettings"/>
     
             <!--Select created Attribute -->
    +        <switchToIFrame stepKey="switchOutOfIFrame"/>
             <click selector="{{ConfigurableProductSection.selectCreatedAttribute}}" stepKey="selectCreatedAttribute"/>
     
             <!--Click Next button-->
    
  • app/code/Magento/ConfigurableProduct/Test/Mftf/Data/ConfigurableProductAttributeNameDesignData.xml+1 1 modified
    @@ -12,7 +12,7 @@
             <data key="productName" unique="prefix">Shoes</data>
             <data key="price">60</data>
             <data key="weight">100</data>
    -        <data key="defaultLabel">design</data>
    +        <data key="defaultLabel">design123</data>
             <data key="adminFieldRed">red</data>
             <data key="defaultStoreViewFieldRed">red123</data>
             <data key="adminFieldBlue">blue</data>
    
  • app/code/Magento/ConfigurableProduct/Test/Mftf/Section/ConfigurableProductAttributeNameDesignSection/CatalogProductsSection.xml+1 1 modified
    @@ -14,7 +14,7 @@
             <element name="searchDefaultLabelField" type="input" selector="//*[@id='attributeGrid_filter_frontend_label']"/>
             <element name="searchButton" type="button" selector="//div[@class='admin__filter-actions']//*[contains(text(), 'Search')]"/>
             <element name="storesProductItem" type="button" selector="//*[@data-ui-id='menu-magento-catalog-catalog-attributes-attributes']/a"/>
    -        <element name="createdAttributeItem" type="button" selector="//td[contains(@class, 'col-label') and normalize-space()='design']"/>
    +        <element name="createdAttributeItem" type="button" selector="//td[contains(@class, 'col-label') and normalize-space()='design123']"/>
             <element name="deleteAttributeItem" type="button" selector="//*[@id='delete']"/>
             <element name="okButton" type="button" selector="//footer[@class='modal-footer']//*[contains(text(),'OK')]"/>
             <element name="messageSuccessSavedProduct" type="button" selector="//div[@data-ui-id='messages-message-success']"/>
    
  • app/code/Magento/ConfigurableProduct/Test/Mftf/Section/ConfigurableProductAttributeNameDesignSection/ConfigurableProductSection.xml+1 1 modified
    @@ -12,7 +12,7 @@
             <element name="configProductItem" type="button" selector="//*[@id='add_new_product']//*[contains(text(),'Configurable Product')]"/>
             <element name="nextButton" type="button" selector="//div[@class='nav-bar-outer-actions']//*[contains(text(),'Next')]"/>
             <element name="generateConfigure" type="button" selector="//div[@class='nav-bar-outer-actions']//*[contains(text(),'Generate Products')]"/>
    -        <element name="selectCreatedAttribute" type="button" selector="//*[@class='admin__data-grid-wrap']//td[normalize-space()='design']/preceding-sibling::td"/>
    +        <element name="selectCreatedAttribute" type="button" selector="//*[@class='admin__data-grid-wrap']//td[normalize-space()='design123']/preceding-sibling::td"/>
             <element name="closeFrame" type="button" selector="//*[@class='modal-header']//*[contains(text(),'Create Product Configurations')]/following-sibling::button"/>
         </section>
     </sections>
    
  • app/code/Magento/Contact/composer.json+12 10 modified
    @@ -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 @@
             }
         }
     }
    +
    
  • app/code/Magento/Cookie/composer.json+10 8 modified
    @@ -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.5",
         "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 @@
             }
         }
     }
    +
    
  • app/code/Magento/Cron/composer.json+10 8 modified
    @@ -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.5",
         "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 @@
             }
         }
     }
    +
    
  • app/code/Magento/Csp/Block/Sri/Hashes.php+94 0 added
    @@ -0,0 +1,94 @@
    +<?php
    +/**
    + * Copyright © Magento, Inc. All rights reserved.
    + * See COPYING.txt for license details.
    + */
    +declare(strict_types=1);
    +
    +namespace Magento\Csp\Block\Sri;
    +
    +use Magento\Framework\UrlInterface;
    +use Magento\Deploy\Package\Package;
    +use Magento\Framework\App\ObjectManager;
    +use Magento\Framework\View\Element\Template;
    +use Magento\Framework\Exception\LocalizedException;
    +use Magento\Framework\Serialize\SerializerInterface;
    +use Magento\Framework\View\Element\Template\Context;
    +use Magento\Csp\Model\SubresourceIntegrityRepositoryPool;
    +
    +/**
    + * Block for Subresource Integrity hashes rendering.
    + *
    + * @api
    + */
    +class Hashes extends Template
    +{
    +    /**
    +     * @var SerializerInterface
    +     */
    +    private SerializerInterface $serializer;
    +
    +    /**
    +     * @var SubresourceIntegrityRepositoryPool
    +     */
    +    private SubresourceIntegrityRepositoryPool $integrityRepositoryPool;
    +
    +    /**
    +     * @param Context $context
    +     * @param array $data
    +     * @param SubresourceIntegrityRepositoryPool|null $integrityRepositoryPool
    +     * @param SerializerInterface|null $serializer
    +     */
    +    public function __construct(
    +        Context $context,
    +        array $data = [],
    +        ?SubresourceIntegrityRepositoryPool $integrityRepositoryPool = null,
    +        ?SerializerInterface $serializer = null
    +    ) {
    +        parent::__construct($context, $data);
    +
    +        $this->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);
    +    }
    +}
    
  • app/code/Magento/Csp/composer.json+11 7 modified
    @@ -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.4-p8",
         "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 @@
             }
         }
     }
    +
    
  • app/code/Magento/Csp/etc/acl.xml+22 0 added
    @@ -0,0 +1,22 @@
    +<?xml version="1.0"?>
    +<!--
    +/**
    + * Copyright © Magento, Inc. All rights reserved.
    + * See COPYING.txt for license details.
    + */
    +-->
    +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Acl/etc/acl.xsd">
    +    <acl>
    +        <resources>
    +            <resource id="Magento_Backend::admin">
    +                <resource id="Magento_Backend::stores">
    +                    <resource id="Magento_Backend::stores_settings">
    +                        <resource id="Magento_Config::config">
    +                            <resource id="Magento_Csp::config" title="CSP Configuration" translate="title" sortOrder="150" />
    +                        </resource>
    +                    </resource>
    +                </resource>
    +            </resource>
    +        </resources>
    +    </acl>
    +</config>
    
  • app/code/Magento/Csp/etc/adminhtml/system.xml+35 0 added
    @@ -0,0 +1,35 @@
    +<?xml version="1.0"?>
    +<!--
    +/**
    + * Copyright © Magento, Inc. All rights reserved.
    + * See COPYING.txt for license details.
    + */
    +-->
    +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd">
    +    <system>
    +        <section id="csp" translate="label" type="text" sortOrder="305" showInDefault="1" showInWebsite="1" showInStore="1">
    +            <label>Content Security Policy (CSP)</label>
    +            <tab>security</tab>
    +            <resource>Magento_Csp::config</resource>
    +            <group id="mode" translate="label" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="1">
    +                <label>Mode</label>
    +                <group id="storefront" translate="label" sortOrder="2" showInDefault="1" showInWebsite="1" showInStore="1">
    +                    <label>Storefront Default</label>
    +                    <field id="report_uri" translate="label" type="text" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="1">
    +                        <label>Report URI</label>
    +                        <comment>URI to report CSP violations on storefront. Used for all storefront pages that don't have own URI configured above.</comment>
    +                        <validate>validate-url</validate>
    +                    </field>
    +                </group>
    +                <group id="admin" translate="label" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="1">
    +                    <label>Admin Default</label>
    +                    <field id="report_uri" translate="label" type="text" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="1">
    +                        <label>Report URI</label>
    +                        <comment>URI to report CSP violations in admin area. Used for all admin pages that don't have own URI configured above.</comment>
    +                        <validate>validate-url</validate>
    +                    </field>
    +                </group>
    +            </group>
    +        </section>
    +    </system>
    +</config>
    
  • app/code/Magento/Csp/etc/di.xml+23 0 modified
    @@ -111,4 +111,27 @@
                 <argument name="cache" xsi:type="object">Magento\Csp\Model\BlockCache</argument>
             </arguments>
         </type>
    +    <type name="Magento\Deploy\Package\Package">
    +        <arguments>
    +            <argument name="postProcessors" xsi:type="array">
    +                <item name="integrity" xsi:type="object">Magento\Csp\Model\Deploy\Package\Processor\PostProcessor\Integrity</item>
    +            </argument>
    +        </arguments>
    +    </type>
    +    <type name="Magento\Framework\View\Asset\GroupedCollection">
    +        <plugin name="addDefaultPropertiesToGroup" type="Magento\Csp\Plugin\AddDefaultPropertiesToGroupPlugin" />
    +    </type>
    +    <type name="Magento\Deploy\Service\DeployStaticContent">
    +        <plugin name="removeAllAssetIntegrityHashes" type="Magento\Csp\Plugin\RemoveAllAssetIntegrityHashes" />
    +        <plugin name="storeAssetIntegrityHashes" type="Magento\Csp\Plugin\StoreAssetIntegrityHashes" />
    +    </type>
    +    <type name="Magento\RequireJs\Model\FileManager">
    +        <plugin name="addResourceIntegrityAfterAssetCreate" type="Magento\Csp\Plugin\GenerateAssetIntegrity"/>
    +    </type>
    +    <preference for="Magento\Deploy\Package\Processor\PostProcessor\Map" type="Magento\Csp\Model\Deploy\Package\Processor\PostProcessor\Map" />
    +    <type name="Magento\Csp\Model\Deploy\Package\Processor\PostProcessor\Map">
    +        <arguments>
    +            <argument name="driver" xsi:type="object">Magento\Framework\Filesystem\Driver\File</argument>
    +        </arguments>
    +    </type>
     </config>
    
  • app/code/Magento/Csp/Helper/CspNonceProvider.php+86 0 added
    @@ -0,0 +1,86 @@
    +<?php
    +/**
    + * Copyright © Magento, Inc. All rights reserved.
    + * See COPYING.txt for license details.
    + */
    +
    +declare(strict_types=1);
    +
    +namespace Magento\Csp\Helper;
    +
    +use Magento\Csp\Model\Collector\DynamicCollector;
    +use Magento\Csp\Model\Policy\FetchPolicy;
    +use Magento\Framework\Exception\LocalizedException;
    +use Magento\Framework\Math\Random;
    +
    +/**
    + * This helper class is used to provide nonce for CSP
    + *
    + * It also adds a nonce to the CSP header.
    + */
    +class CspNonceProvider
    +{
    +    /**
    +     * @var string
    +     */
    +    private const NONCE_LENGTH = 32;
    +
    +    /**
    +     * @var string
    +     */
    +    private string $nonce;
    +
    +    /**
    +     * @var Random
    +     */
    +    private Random $random;
    +
    +    /**
    +     * @var DynamicCollector
    +     */
    +    private DynamicCollector $dynamicCollector;
    +
    +    /**
    +     * @param Random $random
    +     * @param DynamicCollector $dynamicCollector
    +     */
    +    public function __construct(
    +        Random $random,
    +        DynamicCollector $dynamicCollector
    +    ) {
    +        $this->random = $random;
    +        $this->dynamicCollector = $dynamicCollector;
    +    }
    +
    +    /**
    +     * 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);
    +    }
    +}
    
  • app/code/Magento/Csp/Helper/InlineUtil.php+40 14 modified
    @@ -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())
    +                        )
    +                    );
    +                }
                 }
             }
     
    
  • app/code/Magento/Csp/Model/Collector/ConfigCollector.php+29 2 modified
    @@ -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'])
    
  • app/code/Magento/Csp/Model/Deploy/Package/Processor/PostProcessor/Integrity.php+89 0 added
    @@ -0,0 +1,89 @@
    +<?php
    +/**
    + * Copyright © Magento, Inc. All rights reserved.
    + * See COPYING.txt for license details.
    + */
    +declare(strict_types=1);
    +
    +namespace Magento\Csp\Model\Deploy\Package\Processor\PostProcessor;
    +
    +use Magento\Framework\Filesystem;
    +use Magento\Deploy\Package\Package;
    +use Magento\Csp\Model\SubresourceIntegrityFactory;
    +use Magento\Framework\App\Filesystem\DirectoryList;
    +use Magento\Csp\Model\SubresourceIntegrityCollector;
    +use Magento\Deploy\Package\Processor\ProcessorInterface;
    +use Magento\Csp\Model\SubresourceIntegrity\HashGenerator;
    +
    +/**
    + * Post-processor that generates integrity hashes after static content package deployed.
    + */
    +class Integrity implements ProcessorInterface
    +{
    +    /**
    +     * @var Filesystem
    +     */
    +    private Filesystem $filesystem;
    +
    +    /**
    +     * @var HashGenerator
    +     */
    +    private HashGenerator $hashGenerator;
    +
    +    /**
    +     * @var SubresourceIntegrityFactory
    +     */
    +    private SubresourceIntegrityFactory $integrityFactory;
    +
    +    /**
    +     * @var SubresourceIntegrityCollector
    +     */
    +    private SubresourceIntegrityCollector $integrityCollector;
    +
    +    /**
    +     * @param Filesystem $filesystem
    +     * @param HashGenerator $hashGenerator
    +     * @param SubresourceIntegrityFactory $integrityFactory
    +     * @param SubresourceIntegrityCollector $integrityCollector
    +     */
    +    public function __construct(
    +        Filesystem $filesystem,
    +        HashGenerator $hashGenerator,
    +        SubresourceIntegrityFactory $integrityFactory,
    +        SubresourceIntegrityCollector $integrityCollector
    +    ) {
    +        $this->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;
    +    }
    +}
    
  • app/code/Magento/Csp/Model/Deploy/Package/Processor/PostProcessor/Map.php+139 0 added
    @@ -0,0 +1,139 @@
    +<?php
    +/**
    + * Copyright © Magento, Inc. All rights reserved.
    + * See COPYING.txt for license details.
    + */
    +declare(strict_types=1);
    +
    +namespace Magento\Csp\Model\Deploy\Package\Processor\PostProcessor;
    +
    +use Magento\Deploy\Package\Package;
    +use Magento\Deploy\Package\PackageFileFactory;
    +use Magento\Deploy\Service\DeployStaticFile;
    +use Magento\Framework\App\DeploymentConfig\Writer\PhpFormatter;
    +use Magento\Framework\App\Filesystem\DirectoryList;
    +use Magento\Framework\Exception\FileSystemException;
    +use Magento\Framework\Filesystem;
    +use Magento\Framework\View\Asset\Minification;
    +use Magento\Framework\View\Asset\RepositoryMap;
    +use Magento\Csp\Model\SubresourceIntegrityFactory;
    +use Magento\Csp\Model\SubresourceIntegrity\HashGenerator;
    +use Magento\Framework\Filesystem\DriverInterface;
    +use Magento\Csp\Model\SubresourceIntegrityCollector;
    +
    +/**
    + * Class Adds Integrity attribute to requirejs-map.js asset
    + *
    + * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
    + */
    +class Map extends \Magento\Deploy\Package\Processor\PostProcessor\Map
    +{
    +
    +    /**
    +     * @var HashGenerator
    +     */
    +    private HashGenerator $hashGenerator;
    +
    +    /**
    +     * @var SubresourceIntegrityCollector
    +     */
    +    private SubresourceIntegrityCollector $integrityCollector;
    +
    +    /**
    +     * @var SubresourceIntegrityFactory
    +     */
    +    private SubresourceIntegrityFactory $integrityFactory;
    +
    +    /**
    +     * @var Minification
    +     */
    +    private Minification $minification;
    +
    +    /**
    +     * @var DriverInterface
    +     */
    +    private DriverInterface $driver;
    +
    +    /**
    +     * @var FileSystem
    +     */
    +    private FileSystem $filesystem;
    +
    +    /**
    +     * Constructor
    +     *
    +     * @param DeployStaticFile $deployStaticFile
    +     * @param PhpFormatter $formatter
    +     * @param PackageFileFactory $packageFileFactory
    +     * @param Minification $minification
    +     * @param SubresourceIntegrityFactory $integrityFactory
    +     * @param HashGenerator $hashGenerator
    +     * @param DriverInterface $driver
    +     * @param SubresourceIntegrityCollector $integrityCollector
    +     * @param FileSystem $filesystem
    +     */
    +    public function __construct(
    +        DeployStaticFile $deployStaticFile,
    +        PhpFormatter $formatter,
    +        PackageFileFactory $packageFileFactory,
    +        Minification $minification,
    +        SubresourceIntegrityFactory $integrityFactory,
    +        HashGenerator $hashGenerator,
    +        DriverInterface $driver,
    +        SubresourceIntegrityCollector $integrityCollector,
    +        Filesystem $filesystem
    +    ) {
    +        $this->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);
    +    }
    +}
    
  • app/code/Magento/Csp/Model/Mode/ConfigManager.php+55 7 modified
    @@ -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
    +        );
         }
     }
    
  • app/code/Magento/Csp/Model/SubresourceIntegrityCollector.php+49 0 added
    @@ -0,0 +1,49 @@
    +<?php
    +/**
    + * Copyright © Magento, Inc. All rights reserved.
    + * See COPYING.txt for license details.
    + */
    +declare(strict_types=1);
    +
    +namespace Magento\Csp\Model;
    +
    +/**
    + * Collector of Integrity objects.
    + */
    +class SubresourceIntegrityCollector
    +{
    +    /**
    +     * @var array
    +     */
    +    private array $data = [];
    +
    +    /**
    +     * @param array $data
    +     */
    +    public function __construct(array $data = [])
    +    {
    +        $this->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;
    +    }
    +}
    
  • app/code/Magento/Csp/Model/SubresourceIntegrity/HashGenerator.php+37 0 added
    @@ -0,0 +1,37 @@
    +<?php
    +/**
    + * Copyright © Magento, Inc. All rights reserved.
    + * See COPYING.txt for license details.
    + */
    +declare(strict_types=1);
    +
    +namespace Magento\Csp\Model\SubresourceIntegrity;
    +
    +/**
    + * Subresource Integrity hashes generator.
    + */
    +class HashGenerator
    +{
    +    /**
    +     * CHashing algorithm.
    +     *
    +     * @var string
    +     */
    +    private const ALGORITHM = 'sha256';
    +
    +    /**
    +     * Computes integrity hash for a given content.
    +     *
    +     * @param string $content
    +     *
    +     * @return string
    +     */
    +    public function generate(string $content): string
    +    {
    +        $base64Hash = base64_encode(
    +            hash(self::ALGORITHM, $content, true)
    +        );
    +
    +        return self::ALGORITHM . "-{$base64Hash}";
    +    }
    +}
    
  • app/code/Magento/Csp/Model/SubresourceIntegrity.php+34 0 added
    @@ -0,0 +1,34 @@
    +<?php
    +/**
    + * Copyright © Magento, Inc. All rights reserved.
    + * See COPYING.txt for license details.
    + */
    +declare(strict_types=1);
    +
    +namespace Magento\Csp\Model;
    +
    +/**
    + * Subresource Integrity data model.
    + */
    +class SubresourceIntegrity extends \Magento\Framework\DataObject
    +{
    +    /**
    +     * Gets an integrity Path.
    +     *
    +     * @return string|null
    +     */
    +    public function getPath(): ?string
    +    {
    +        return $this->getData("path");
    +    }
    +
    +    /**
    +     * Gets an integrity hash.
    +     *
    +     * @return string|null
    +     */
    +    public function getHash(): ?string
    +    {
    +        return $this->getData("hash");
    +    }
    +}
    
  • app/code/Magento/Csp/Model/SubresourceIntegrityRepository.php+207 0 added
    @@ -0,0 +1,207 @@
    +<?php
    +/**
    + * Copyright © Magento, Inc. All rights reserved.
    + * See COPYING.txt for license details.
    + */
    +declare(strict_types=1);
    +
    +namespace Magento\Csp\Model;
    +
    +use Magento\Framework\App\CacheInterface;
    +use Magento\Framework\Serialize\SerializerInterface;
    +
    +/**
    + * Class contains methods equivalent to repository design to manage SRI hashes in cache.
    + */
    +class SubresourceIntegrityRepository
    +{
    +    /**
    +     * Cache prefix.
    +     *
    +     * @var string
    +     */
    +    private const CACHE_PREFIX = 'INTEGRITY';
    +
    +    /**
    +     * @var array|null
    +     */
    +    private ?array $data = null;
    +
    +    /**
    +     * @var string|null
    +     */
    +    private ?string $context;
    +
    +    /**
    +     * @var CacheInterface
    +     */
    +    private CacheInterface $cache;
    +
    +    /**
    +     * @var SerializerInterface
    +     */
    +    private SerializerInterface $serializer;
    +
    +    /**
    +     * @var SubresourceIntegrityFactory
    +     */
    +    private SubresourceIntegrityFactory $integrityFactory;
    +
    +    /**
    +     * @param CacheInterface $cache
    +     * @param SerializerInterface $serializer
    +     * @param SubresourceIntegrityFactory $integrityFactory
    +     * @param string|null $context
    +     */
    +    public function __construct(
    +        CacheInterface $cache,
    +        SerializerInterface $serializer,
    +        SubresourceIntegrityFactory $integrityFactory,
    +        ?string $context = null
    +    ) {
    +        $this->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;
    +    }
    +}
    
  • app/code/Magento/Csp/Model/SubresourceIntegrityRepositoryPool.php+53 0 added
    @@ -0,0 +1,53 @@
    +<?php
    +/**
    + * Copyright © Magento, Inc. All rights reserved.
    + * See COPYING.txt for license details.
    + */
    +declare(strict_types=1);
    +
    +namespace Magento\Csp\Model;
    +
    +/**
    + * Pool of subresource integrity repositories.
    + */
    +class SubresourceIntegrityRepositoryPool
    +{
    +    /**
    +     * @var array
    +     */
    +    private array $repositories = [];
    +
    +    /**
    +     * @var SubresourceIntegrityRepositoryFactory
    +     */
    +    private SubresourceIntegrityRepositoryFactory $integrityRepositoryFactory;
    +
    +    /**
    +     * @param SubresourceIntegrityRepositoryFactory $integrityRepositoryFactory
    +     */
    +    public function __construct(
    +        SubresourceIntegrityRepositoryFactory $integrityRepositoryFactory
    +    ) {
    +        $this->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];
    +    }
    +}
    
  • app/code/Magento/Csp/Plugin/AddDefaultPropertiesToGroupPlugin.php+81 0 added
    @@ -0,0 +1,81 @@
    +<?php
    +/**
    + * Copyright © Magento, Inc. All rights reserved.
    + * See COPYING.txt for license details.
    + */
    +declare(strict_types=1);
    +
    +namespace Magento\Csp\Plugin;
    +
    +use Magento\Framework\App\State;
    +use Magento\Deploy\Package\Package;
    +use Magento\Framework\View\Asset\AssetInterface;
    +use Magento\Framework\View\Asset\LocalInterface;
    +use Magento\Framework\View\Asset\GroupedCollection;
    +use Magento\Csp\Model\SubresourceIntegrityRepositoryPool;
    +
    +/**
    + * Plugin to add integrity to assets on page load.
    + */
    +class AddDefaultPropertiesToGroupPlugin
    +{
    +    /**
    +     * @var State
    +     */
    +    private State $state;
    +
    +    /**
    +     * @var SubresourceIntegrityRepositoryPool
    +     */
    +    private SubresourceIntegrityRepositoryPool $integrityRepositoryPool;
    +
    +    /**
    +     * @param State $state
    +     * @param SubresourceIntegrityRepositoryPool $integrityRepositoryPool
    +     */
    +    public function __construct(
    +        State $state,
    +        SubresourceIntegrityRepositoryPool $integrityRepositoryPool
    +    ) {
    +        $this->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];
    +    }
    +}
    
  • app/code/Magento/Csp/Plugin/GenerateAssetIntegrity.php+91 0 added
    @@ -0,0 +1,91 @@
    +<?php
    +/**
    + * Copyright © Magento, Inc. All rights reserved.
    + * See COPYING.txt for license details.
    + */
    +declare(strict_types=1);
    +
    +namespace Magento\Csp\Plugin;
    +
    +use Magento\Framework\View\Asset\File;
    +use Magento\RequireJs\Model\FileManager;
    +use Magento\Csp\Model\SubresourceIntegrityFactory;
    +use Magento\Csp\Model\SubresourceIntegrityCollector;
    +use Magento\Csp\Model\SubresourceIntegrity\HashGenerator;
    +
    +/**
    + * Plugin to add asset integrity value after static content deploy.
    + */
    +class GenerateAssetIntegrity
    +{
    +    /**
    +     * Supported content types.
    +     *
    +     * @var array
    +     */
    +    private const CONTENT_TYPES = ["js"];
    +
    +    /**
    +     * @var HashGenerator
    +     */
    +    private HashGenerator $hashGenerator;
    +
    +    /**
    +     * @var SubresourceIntegrityFactory
    +     */
    +    private SubresourceIntegrityFactory $integrityFactory;
    +
    +    /**
    +     * @var SubresourceIntegrityCollector
    +     */
    +    private SubresourceIntegrityCollector $integrityCollector;
    +
    +    /**
    +     * @param HashGenerator $hashGenerator
    +     * @param SubresourceIntegrityFactory $integrityFactory
    +     * @param SubresourceIntegrityCollector $integrityCollector
    +     */
    +    public function __construct(
    +        HashGenerator $hashGenerator,
    +        SubresourceIntegrityFactory $integrityFactory,
    +        SubresourceIntegrityCollector $integrityCollector
    +    ) {
    +        $this->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;
    +    }
    +}
    
  • app/code/Magento/Csp/Plugin/RemoveAllAssetIntegrityHashes.php+69 0 added
    @@ -0,0 +1,69 @@
    +<?php
    +/**
    + * Copyright © Magento, Inc. All rights reserved.
    + * See COPYING.txt for license details.
    + */
    +declare(strict_types=1);
    +
    +namespace Magento\Csp\Plugin;
    +
    +use Magento\Framework\App\Area;
    +use Magento\Deploy\Package\Package;
    +use Magento\Deploy\Console\DeployStaticOptions;
    +use Magento\Deploy\Service\DeployStaticContent;
    +use Magento\Csp\Model\SubresourceIntegrityRepositoryPool;
    +
    +/**
    + * Plugin that removes existing integrity hashes for all assets.
    + */
    +class RemoveAllAssetIntegrityHashes
    +{
    +    /**
    +     * @var SubresourceIntegrityRepositoryPool
    +     */
    +    private SubresourceIntegrityRepositoryPool $integrityRepositoryPool;
    +
    +    /**
    +     * @param SubresourceIntegrityRepositoryPool $integrityRepositoryPool
    +     */
    +    public function __construct(
    +        SubresourceIntegrityRepositoryPool $integrityRepositoryPool
    +    ) {
    +        $this->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];
    +    }
    +}
    
  • app/code/Magento/Csp/Plugin/StoreAssetIntegrityHashes.php+70 0 added
    @@ -0,0 +1,70 @@
    +<?php
    +/**
    + * Copyright © Magento, Inc. All rights reserved.
    + * See COPYING.txt for license details.
    + */
    +declare(strict_types=1);
    +
    +namespace Magento\Csp\Plugin;
    +
    +use Magento\Deploy\Service\DeployStaticContent;
    +use Magento\Csp\Model\SubresourceIntegrityCollector;
    +use Magento\Csp\Model\SubresourceIntegrityRepositoryPool;
    +
    +/**
    + * Plugin that stores generated integrity hashes for all assets.
    + */
    +class StoreAssetIntegrityHashes
    +{
    +    /**
    +     * @var SubresourceIntegrityCollector
    +     */
    +    private SubresourceIntegrityCollector $integrityCollector;
    +
    +    /**
    +     * @var SubresourceIntegrityRepositoryPool
    +     */
    +    private SubresourceIntegrityRepositoryPool $integrityRepositoryPool;
    +
    +    /**
    +     * @param SubresourceIntegrityCollector $integrityCollector
    +     * @param SubresourceIntegrityRepositoryPool $integrityRepositoryPool
    +     */
    +    public function __construct(
    +        SubresourceIntegrityCollector $integrityCollector,
    +        SubresourceIntegrityRepositoryPool $integrityRepositoryPool
    +    ) {
    +        $this->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);
    +        }
    +    }
    +}
    
  • app/code/Magento/Csp/Test/Unit/Model/Mode/ConfigManagerTest.php+79 1 modified
    @@ -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');
    +    }
     }
    
  • app/code/Magento/Csp/Test/Unit/Model/SubresourceIntegrityRepositoryTest.php+129 0 added
    @@ -0,0 +1,129 @@
    +<?php
    +/**
    + * Copyright © Magento, Inc. All rights reserved.
    + * See COPYING.txt for license details.
    + */
    +declare(strict_types=1);
    +
    +namespace Magento\Csp\Test\Unit\Model;
    +
    +use Magento\Framework\App\CacheInterface;
    +use Magento\Framework\Serialize\SerializerInterface;
    +use PHPUnit\Framework\MockObject\MockObject;
    +use PHPUnit\Framework\TestCase;
    +use Magento\Csp\Model\SubresourceIntegrity;
    +use Magento\Csp\Model\SubresourceIntegrityRepository;
    +use Magento\Csp\Model\SubresourceIntegrityFactory;
    +
    +/**
    + * Unit Test for Class @see Magento\Csp\Model\SubresourceIntegrityRepository
    + *
    + */
    +class SubresourceIntegrityRepositoryTest extends TestCase
    +{
    +
    +    /**
    +     * @var MockObject
    +     */
    +    private MockObject $cacheMock;
    +
    +    /**
    +     * @var MockObject
    +     */
    +    private MockObject $serializerMock;
    +
    +    /**
    +     * @var MockObject
    +     */
    +    private MockObject $integrityFactoryMock;
    +
    +    /**
    +     * @var SubresourceIntegrityRepository|null
    +     */
    +    private ?SubresourceIntegrityRepository $subresourceIntegrityRepository = null;
    +
    +    /**
    +     * Initialize dependencies
    +     *
    +     * @return void
    +     */
    +    protected function setUp(): void
    +    {
    +        parent::setUp();
    +        $this->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));
    +    }
    +}
    
  • app/code/Magento/Csp/Test/Unit/Plugin/AddDefaultPropertiesToGroupPluginTest.php+113 0 added
    @@ -0,0 +1,113 @@
    +<?php
    +/**
    + * Copyright © Magento, Inc. All rights reserved.
    + * See COPYING.txt for license details.
    + */
    +declare(strict_types=1);
    +
    +namespace Magento\Csp\Test\Unit\Plugin;
    +
    +use Magento\Csp\Model\SubresourceIntegrity;
    +use Magento\Csp\Model\SubresourceIntegrityRepository;
    +use Magento\Csp\Model\SubresourceIntegrityRepositoryPool;
    +use PHPUnit\Framework\MockObject\MockObject;
    +use PHPUnit\Framework\TestCase;
    +use Magento\Csp\Plugin\AddDefaultPropertiesToGroupPlugin;
    +use Magento\Framework\View\Asset\File;
    +use Magento\Framework\View\Asset\GroupedCollection;
    +use Magento\Framework\App\State;
    +
    +/**
    + * Test for class Magento\Csp\Plugin\AddDefaultPropertiesToGroupPlugin
    + *
    + */
    +class AddDefaultPropertiesToGroupPluginTest extends TestCase
    +{
    +
    +    /**
    +     * @var MockObject
    +     */
    +    private MockObject $assetInterfaceMock;
    +
    +    /**
    +     * @var MockObject
    +     */
    +    private MockObject $integrityRepositoryPoolMock;
    +
    +    /**
    +     * @var MockObject
    +     */
    +    private MockObject $stateMock;
    +
    +    /**
    +     * @var AddDefaultPropertiesToGroupPlugin
    +     */
    +    private AddDefaultPropertiesToGroupPlugin $plugin;
    +
    +    /**
    +     * Initialize Dependencies
    +     *
    +     * @return void
    +     */
    +    protected function setUp(): void
    +    {
    +        parent::setUp();
    +        $this->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
    +            )
    +        );
    +    }
    +}
    
  • app/code/Magento/Csp/Test/Unit/Plugin/StoreAssetIntegrityHashesTest.php+96 0 added
    @@ -0,0 +1,96 @@
    +<?php
    +/**
    + * Copyright © Magento, Inc. All rights reserved.
    + * See COPYING.txt for license details.
    + */
    +declare(strict_types=1);
    +
    +namespace Magento\Csp\Test\Unit\Plugin;
    +
    +use Magento\Csp\Model\SubresourceIntegrity;
    +use Magento\Csp\Model\SubresourceIntegrityRepository;
    +use Magento\Csp\Model\SubresourceIntegrityRepositoryPool;
    +use Magento\Deploy\Service\DeployStaticContent;
    +use PHPUnit\Framework\MockObject\MockObject;
    +use Magento\Csp\Plugin\StoreAssetIntegrityHashes;
    +use Magento\Csp\Model\SubresourceIntegrityCollector;
    +use PHPUnit\Framework\TestCase;
    +
    +/**
    + * Plugin that removes existing integrity hashes for all assets.
    + */
    +class StoreAssetIntegrityHashesTest extends TestCase
    +{
    +    /**
    +     * @var MockObject
    +     */
    +    private MockObject $integrityRepositoryPoolMock;
    +
    +    /**
    +     * @var MockObject
    +     */
    +    private MockObject $integrityCollectorMock;
    +
    +    /**
    +     * @var StoreAssetIntegrityHashes
    +     */
    +    private StoreAssetIntegrityHashes $plugin;
    +
    +    /**
    +     * Initialize Dependencies
    +     *
    +     * @return void
    +     */
    +    protected function setUp(): void
    +    {
    +        parent::setUp();
    +        $this->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, []);
    +    }
    +}
    
  • app/code/Magento/Csp/view/adminhtml/layout/sales_order_create_index.xml+17 0 added
    @@ -0,0 +1,17 @@
    +<?xml version="1.0"?>
    +<!--
    +/**
    + * Copyright © Magento, Inc. All rights reserved.
    + * See COPYING.txt for license details.
    + */
    +-->
    +<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    +    <head>
    +        <link src="Magento_Csp::js/sri.js"/>
    +    </head>
    +    <body>
    +        <referenceBlock name="head.additional">
    +            <block class="Magento\Csp\Block\Sri\Hashes" name="admin.sri.hashes" template="Magento_Csp::sri/hashes.phtml"/>
    +        </referenceBlock>
    +    </body>
    +</page>
    
  • app/code/Magento/Csp/view/base/templates/sri/hashes.phtml+18 0 added
    @@ -0,0 +1,18 @@
    +<?php
    +/**
    + * Copyright © Magento, Inc. All rights reserved.
    + * See COPYING.txt for license details.
    + */
    +
    +/** @var \Magento\Csp\Block\Sri\Hashes $block */
    +/** @var \Magento\Framework\View\Helper\SecureHtmlRenderer $secureRenderer */
    +?>
    +
    +<?php $sriHashes = /* @noEscape */ $block->getSerialized();
    +$scriptString = <<<script
    +        window.sriHashes = {$sriHashes};
    +script;
    +?>
    +
    +<?= /* @noEscape */ $secureRenderer->renderTag('script', [], $scriptString, false) ?>
    +
    
  • app/code/Magento/Csp/view/base/web/js/sri.js+13 0 added
    @@ -0,0 +1,13 @@
    +/**
    + * Copyright © Magento, Inc. All rights reserved.
    + * See COPYING.txt for license details.
    + */
    +require.config({
    +    onNodeCreated: function (node, config, moduleName, url) {
    +        'use strict';
    +        if ('sriHashes' in window && url in window.sriHashes) {
    +            node.setAttribute('integrity', window.sriHashes[url]);
    +            node.setAttribute('crossorigin', 'anonymous');
    +        }
    +    }
    +});
    
  • app/code/Magento/Csp/view/frontend/layout/checkout_index_index.xml+17 0 added
    @@ -0,0 +1,17 @@
    +<?xml version="1.0"?>
    +<!--
    +/**
    + * Copyright © Magento, Inc. All rights reserved.
    + * See COPYING.txt for license details.
    + */
    +-->
    +<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="checkout" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    +    <head>
    +        <link src="Magento_Csp::js/sri.js"/>
    +    </head>
    +    <body>
    +        <referenceBlock name="head.additional">
    +            <block class="Magento\Csp\Block\Sri\Hashes" name="csp.sri.hashes" template="Magento_Csp::sri/hashes.phtml"/>
    +        </referenceBlock>
    +    </body>
    +</page>
    
  • app/code/Magento/CurrencySymbol/composer.json+13 11 modified
    @@ -1,23 +1,24 @@
     {
         "name": "magento/module-currency-symbol",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.3-p5",
         "require": {
             "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    -        "magento/module-backend": "*",
    -        "magento/module-config": "*",
    -        "magento/module-directory": "*",
    -        "magento/module-page-cache": "*",
    -        "magento/module-store": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-config": "101.2.*",
    +        "magento/module-directory": "100.4.*",
    +        "magento/module-page-cache": "100.4.*",
    +        "magento/module-store": "101.1.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -27,3 +28,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/CurrencySymbol/Model/System/Currencysymbol.php+10 10 modified
    @@ -27,14 +27,14 @@ class Currencysymbol
         protected $_symbolsData = [];
     
         /**
    -     * Store id
    +     * Current store id
          *
          * @var string|null
          */
         protected $_storeId;
     
         /**
    -     * Website id
    +     * Current website id
          *
          * @var string|null
          */
    @@ -55,19 +55,19 @@ class Currencysymbol
         /**
          * Config path to custom currency symbol value
          */
    -    const XML_PATH_CUSTOM_CURRENCY_SYMBOL = 'currency/options/customsymbol';
    +    public const XML_PATH_CUSTOM_CURRENCY_SYMBOL = 'currency/options/customsymbol';
     
    -    const XML_PATH_ALLOWED_CURRENCIES = \Magento\Directory\Model\Currency::XML_PATH_CURRENCY_ALLOW;
    +    public const XML_PATH_ALLOWED_CURRENCIES = \Magento\Directory\Model\Currency::XML_PATH_CURRENCY_ALLOW;
     
         /*
          * Separator used in config in allowed currencies list
          */
    -    const ALLOWED_CURRENCIES_CONFIG_SEPARATOR = ',';
    +    public const ALLOWED_CURRENCIES_CONFIG_SEPARATOR = ',';
     
         /**
          * Config currency section
          */
    -    const CONFIG_SECTION = 'currency';
    +    public const CONFIG_SECTION = 'currency';
     
         /**
          * Core event manager proxy
    @@ -174,11 +174,11 @@ public function getCurrencySymbolsData()
     
                 if (isset($currentSymbols[$code]) && !empty($currentSymbols[$code])) {
                     $this->_symbolsData[$code]['displaySymbol'] = $currentSymbols[$code];
    +                $this->_symbolsData[$code]['inherited'] = false;
                 } else {
                     $this->_symbolsData[$code]['displaySymbol'] = $this->_symbolsData[$code]['parentSymbol'];
    +                $this->_symbolsData[$code]['inherited'] = true;
                 }
    -            $this->_symbolsData[$code]['inherited'] =
    -                ($this->_symbolsData[$code]['parentSymbol'] == $this->_symbolsData[$code]['displaySymbol']);
             }
     
             return $this->_symbolsData;
    @@ -193,8 +193,8 @@ public function getCurrencySymbolsData()
         public function setCurrencySymbolsData($symbols = [])
         {
             if (!$this->_storeManager->isSingleStoreMode()) {
    -            foreach ($this->getCurrencySymbolsData() as $code => $values) {
    -                if (isset($symbols[$code]) && ($symbols[$code] == $values['parentSymbol'] || empty($symbols[$code]))) {
    +            foreach (array_keys($this->getCurrencySymbolsData()) as $code) {
    +                if (isset($symbols[$code]) && empty($symbols[$code])) {
                         unset($symbols[$code]);
                     }
                 }
    
  • app/code/Magento/CurrencySymbol/Test/Mftf/Test/AdminCheckCurrencyConverterApiConfigurationTest.xml+2 0 modified
    @@ -18,6 +18,8 @@
                 <testCaseId value="MC-28786"/>
                 <useCaseId value="MAGETWO-94919"/>
                 <group value="currency"/>
    +            <!--      Remove this group when Subscription is finalized or Mocking is enabled      -->
    +            <group value="pr_exclude" />
             </annotations>
             <before>
                 <!--Set currency configuration-->
    
  • app/code/Magento/CustomerAnalytics/composer.json+8 6 modified
    @@ -1,17 +1,18 @@
     {
         "name": "magento/module-customer-analytics",
         "description": "N/A",
    -    "require": {
    -        "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    -        "magento/module-customer": "*",
    -        "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-customer": "103.0.*",
    +        "magento/module-analytics": "100.4.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -21,3 +22,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Customer/Api/AccountManagementInterface.php+2 1 modified
    @@ -8,6 +8,7 @@
     namespace Magento\Customer\Api;
     
     use Magento\Framework\Exception\InputException;
    +use Magento\Framework\Exception\LocalizedException;
     
     /**
      * Interface for managing customers accounts.
    @@ -194,7 +195,7 @@ public function resendConfirmation($email, $websiteId, $redirectUrl = '');
          * Check if given email is associated with a customer account in given website.
          *
          * @param string $customerEmail
    -     * @param int $websiteId If not set, will use the current websiteId
    +     * @param int|null $websiteId If not set, will use the current websiteId
          * @return bool
          * @throws \Magento\Framework\Exception\LocalizedException
          */
    
  • app/code/Magento/Customer/composer.json+29 27 modified
    @@ -1,41 +1,42 @@
     {
         "name": "magento/module-customer",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "103.0.5-p8",
         "require": {
             "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    -        "magento/module-authorization": "*",
    -        "magento/module-backend": "*",
    -        "magento/module-catalog": "*",
    -        "magento/module-checkout": "*",
    -        "magento/module-config": "*",
    -        "magento/module-directory": "*",
    -        "magento/module-eav": "*",
    -        "magento/module-integration": "*",
    -        "magento/module-media-storage": "*",
    -        "magento/module-newsletter": "*",
    -        "magento/module-page-cache": "*",
    -        "magento/module-quote": "*",
    -        "magento/module-sales": "*",
    -        "magento/module-store": "*",
    -        "magento/module-tax": "*",
    -        "magento/module-theme": "*",
    -        "magento/module-ui": "*",
    -        "magento/module-wishlist": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-authorization": "100.4.*",
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-checkout": "100.4.*",
    +        "magento/module-config": "101.2.*",
    +        "magento/module-directory": "100.4.*",
    +        "magento/module-eav": "102.1.*",
    +        "magento/module-integration": "100.4.*",
    +        "magento/module-media-storage": "100.4.*",
    +        "magento/module-newsletter": "100.4.*",
    +        "magento/module-page-cache": "100.4.*",
    +        "magento/module-quote": "101.2.*",
    +        "magento/module-sales": "103.0.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/module-tax": "100.4.*",
    +        "magento/module-theme": "101.1.*",
    +        "magento/module-ui": "101.2.*",
    +        "magento/module-wishlist": "101.2.*"
         },
         "suggest": {
    -        "magento/module-cookie": "*",
    -        "magento/module-customer-sample-data": "*",
    -        "magento/module-webapi": "*"
    +        "magento/module-cookie": "100.4.*",
    +        "magento/module-customer-sample-data": "Sample Data version: 100.4.*",
    +        "magento/module-webapi": "100.4.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -45,3 +46,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Customer/Controller/Account/Confirm.php+50 25 modified
    @@ -1,9 +1,10 @@
     <?php
     /**
    - *
      * Copyright © Magento, Inc. All rights reserved.
      * See COPYING.txt for license details.
      */
    +declare(strict_types=1);
    +
     namespace Magento\Customer\Controller\Account;
     
     use Magento\Customer\Api\AccountManagementInterface;
    @@ -15,11 +16,15 @@
     use Magento\Framework\App\Action\Context;
     use Magento\Framework\App\Action\HttpGetActionInterface as HttpGetActionInterface;
     use Magento\Framework\App\Config\ScopeConfigInterface;
    +use Magento\Framework\App\ObjectManager;
     use Magento\Framework\Controller\ResultFactory;
    +use Magento\Framework\Exception\NoSuchEntityException;
    +use Magento\Framework\Phrase;
     use Magento\Framework\UrlFactory;
     use Magento\Framework\Exception\StateException;
     use Magento\Store\Model\ScopeInterface;
     use Magento\Store\Model\StoreManagerInterface;
    +use Magento\Customer\Model\Logger as CustomerLogger;
     
     /**
      * Class Confirm
    @@ -75,6 +80,11 @@ class Confirm extends AbstractAccount implements HttpGetActionInterface
          */
         private $cookieMetadataManager;
     
    +    /**
    +     * @var CustomerLogger
    +     */
    +    private CustomerLogger $customerLogger;
    +
         /**
          * @param Context $context
          * @param Session $customerSession
    @@ -84,6 +94,7 @@ class Confirm extends AbstractAccount implements HttpGetActionInterface
          * @param CustomerRepositoryInterface $customerRepository
          * @param Address $addressHelper
          * @param UrlFactory $urlFactory
    +     * @param CustomerLogger|null $customerLogger
          */
         public function __construct(
             Context $context,
    @@ -93,7 +104,8 @@ public function __construct(
             AccountManagementInterface $customerAccountManagement,
             CustomerRepositoryInterface $customerRepository,
             Address $addressHelper,
    -        UrlFactory $urlFactory
    +        UrlFactory $urlFactory,
    +        ?CustomerLogger $customerLogger = null
         ) {
             $this->session = $customerSession;
             $this->scopeConfig = $scopeConfig;
    @@ -102,13 +114,13 @@ public function __construct(
             $this->customerRepository = $customerRepository;
             $this->addressHelper = $addressHelper;
             $this->urlModel = $urlFactory->create();
    +        $this->customerLogger = $customerLogger ?? ObjectManager::getInstance()->get(CustomerLogger::class);
             parent::__construct($context);
         }
     
         /**
          * Retrieve cookie manager
          *
    -     * @deprecated 101.0.0
          * @return \Magento\Framework\Stdlib\Cookie\PhpCookieManager
          */
         private function getCookieManager()
    @@ -124,7 +136,6 @@ private function getCookieManager()
         /**
          * Retrieve cookie metadata factory
          *
    -     * @deprecated 101.0.0
          * @return \Magento\Framework\Stdlib\Cookie\CookieMetadataFactory
          */
         private function getCookieMetadataFactory()
    @@ -152,7 +163,7 @@ public function execute()
                 return $resultRedirect;
             }
     
    -        $customerId = $this->getRequest()->getParam('id', false);
    +        $customerId = $this->getCustomerId();
             $key = $this->getRequest()->getParam('key', false);
             if (empty($customerId) || empty($key)) {
                 $this->messageManager->addErrorMessage(__('Bad request.'));
    @@ -164,13 +175,19 @@ public function execute()
                 // log in and send greeting email
                 $customerEmail = $this->customerRepository->getById($customerId)->getEmail();
                 $customer = $this->customerAccountManagement->activate($customerEmail, $key);
    +            $successMessage = $this->getSuccessMessage();
                 $this->session->setCustomerDataAsLoggedIn($customer);
    +
                 if ($this->getCookieManager()->getCookie('mage-cache-sessid')) {
                     $metadata = $this->getCookieMetadataFactory()->createCookieMetadata();
                     $metadata->setPath('/');
                     $this->getCookieManager()->deleteCookie('mage-cache-sessid', $metadata);
                 }
    -            $this->messageManager->addSuccess($this->getSuccessMessage());
    +
    +            if ($successMessage) {
    +                $this->messageManager->addSuccess($successMessage);
    +            }
    +
                 $resultRedirect->setUrl($this->getSuccessRedirect());
                 return $resultRedirect;
             } catch (StateException $e) {
    @@ -183,33 +200,41 @@ public function execute()
             return $resultRedirect->setUrl($this->_redirect->error($url));
         }
     
    +    /**
    +     * Returns customer id from request
    +     *
    +     * @return int
    +     */
    +    private function getCustomerId(): int
    +    {
    +        return (int)$this->getRequest()->getParam('id', 0);
    +    }
    +
         /**
          * Retrieve success message
          *
    -     * @return string
    +     * @return Phrase|null
    +     * @throws NoSuchEntityException
          */
         protected function getSuccessMessage()
         {
             if ($this->addressHelper->isVatValidationEnabled()) {
    -            if ($this->addressHelper->getTaxCalculationAddressType() == Address::TYPE_SHIPPING) {
    -                // @codingStandardsIgnoreStart
    -                $message = __(
    -                    'If you are a registered VAT customer, please click <a href="%1">here</a> to enter your shipping address for proper VAT calculation.',
    -                    $this->urlModel->getUrl('customer/address/edit')
    -                );
    -                // @codingStandardsIgnoreEnd
    -            } else {
    -                // @codingStandardsIgnoreStart
    -                $message = __(
    -                    'If you are a registered VAT customer, please click <a href="%1">here</a> to enter your billing address for proper VAT calculation.',
    -                    $this->urlModel->getUrl('customer/address/edit')
    -                );
    -                // @codingStandardsIgnoreEnd
    -            }
    -        } else {
    -            $message = __('Thank you for registering with %1.', $this->storeManager->getStore()->getFrontendName());
    +            return __(
    +                $this->addressHelper->getTaxCalculationAddressType() == Address::TYPE_SHIPPING
    +                    ? 'If you are a registered VAT customer, please click <a href="%1">here</a> to enter your '
    +                    .'shipping address for proper VAT calculation.'
    +                    :'If you are a registered VAT customer, please click <a href="%1">here</a> to enter your '
    +                    .'billing address for proper VAT calculation.',
    +                $this->urlModel->getUrl('customer/address/edit')
    +            );
             }
    -        return $message;
    +
    +        $customerId = $this->getCustomerId();
    +        if ($customerId && $this->customerLogger->get($customerId)->getLastLoginAt()) {
    +            return null;
    +        }
    +
    +        return __('Thank you for registering with %1.', $this->storeManager->getStore()->getFrontendName());
         }
     
         /**
    
  • app/code/Magento/Customer/Controller/Account/EditPost.php+73 35 modified
    @@ -1,6 +1,5 @@
     <?php
     /**
    - *
      * Copyright © Magento, Inc. All rights reserved.
      * See COPYING.txt for license details.
      */
    @@ -10,7 +9,9 @@
     
     use Magento\Customer\Api\Data\CustomerInterface;
     use Magento\Customer\Api\SessionCleanerInterface;
    +use Magento\Customer\Model\AccountConfirmation;
     use Magento\Customer\Model\AddressRegistry;
    +use Magento\Customer\Model\Url;
     use Magento\Framework\App\Action\HttpPostActionInterface as HttpPostActionInterface;
     use Magento\Customer\Model\AuthenticationInterface;
     use Magento\Customer\Model\Customer\Mapper;
    @@ -27,10 +28,12 @@
     use Magento\Customer\Model\Session;
     use Magento\Framework\App\Action\Context;
     use Magento\Framework\Escaper;
    +use Magento\Framework\Exception\FileSystemException;
     use Magento\Framework\Exception\InputException;
     use Magento\Framework\Exception\InvalidEmailOrPasswordException;
     use Magento\Framework\Exception\LocalizedException;
     use Magento\Framework\Exception\NoSuchEntityException;
    +use Magento\Framework\Exception\SessionException;
     use Magento\Framework\Exception\State\UserLockedException;
     use Magento\Customer\Controller\AbstractAccount;
     use Magento\Framework\Phrase;
    @@ -41,18 +44,19 @@
      * Customer edit account information controller
      *
      * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
    + * @SuppressWarnings(PHPMD.ExcessiveParameterList)
      */
     class EditPost extends AbstractAccount implements CsrfAwareActionInterface, HttpPostActionInterface
     {
         /**
          * Form code for data extractor
          */
    -    const FORM_DATA_EXTRACTOR_CODE = 'customer_account_edit';
    +    public const FORM_DATA_EXTRACTOR_CODE = 'customer_account_edit';
     
         /**
          * @var AccountManagementInterface
          */
    -    protected $customerAccountManagement;
    +    protected $accountManagement;
     
         /**
          * @var CustomerRepositoryInterface
    @@ -105,44 +109,61 @@ class EditPost extends AbstractAccount implements CsrfAwareActionInterface, Http
         private $filesystem;
     
         /**
    -     * @var SessionCleanerInterface|null
    +     * @var SessionCleanerInterface
          */
         private $sessionCleaner;
     
    +    /**
    +     * @var AccountConfirmation
    +     */
    +    private $accountConfirmation;
    +
    +    /**
    +     * @var Url
    +     */
    +    private Url $customerUrl;
    +
         /**
          * @param Context $context
          * @param Session $customerSession
    -     * @param AccountManagementInterface $customerAccountManagement
    +     * @param AccountManagementInterface $accountManagement
          * @param CustomerRepositoryInterface $customerRepository
          * @param Validator $formKeyValidator
          * @param CustomerExtractor $customerExtractor
          * @param Escaper|null $escaper
          * @param AddressRegistry|null $addressRegistry
    -     * @param Filesystem $filesystem
    +     * @param Filesystem|null $filesystem
          * @param SessionCleanerInterface|null $sessionCleaner
    +     * @param AccountConfirmation|null $accountConfirmation
    +     * @param Url|null $customerUrl
          */
         public function __construct(
             Context $context,
             Session $customerSession,
    -        AccountManagementInterface $customerAccountManagement,
    +        AccountManagementInterface $accountManagement,
             CustomerRepositoryInterface $customerRepository,
             Validator $formKeyValidator,
             CustomerExtractor $customerExtractor,
             ?Escaper $escaper = null,
    -        AddressRegistry $addressRegistry = null,
    -        Filesystem $filesystem = null,
    -        ?SessionCleanerInterface $sessionCleaner = null
    +        ?AddressRegistry $addressRegistry = null,
    +        ?Filesystem $filesystem = null,
    +        ?SessionCleanerInterface $sessionCleaner = null,
    +        ?AccountConfirmation $accountConfirmation = null,
    +        ?Url $customerUrl = null
         ) {
             parent::__construct($context);
             $this->session = $customerSession;
    -        $this->customerAccountManagement = $customerAccountManagement;
    +        $this->accountManagement = $accountManagement;
             $this->customerRepository = $customerRepository;
             $this->formKeyValidator = $formKeyValidator;
             $this->customerExtractor = $customerExtractor;
             $this->escaper = $escaper ?: ObjectManager::getInstance()->get(Escaper::class);
             $this->addressRegistry = $addressRegistry ?: ObjectManager::getInstance()->get(AddressRegistry::class);
             $this->filesystem = $filesystem ?: ObjectManager::getInstance()->get(Filesystem::class);
             $this->sessionCleaner = $sessionCleaner ?: ObjectManager::getInstance()->get(SessionCleanerInterface::class);
    +        $this->accountConfirmation = $accountConfirmation ?: ObjectManager::getInstance()
    +            ->get(AccountConfirmation::class);
    +        $this->customerUrl = $customerUrl ?: ObjectManager::getInstance()->get(Url::class);
         }
     
         /**
    @@ -164,7 +185,6 @@ private function getAuthentication()
          * Get email notification
          *
          * @return EmailNotificationInterface
    -     * @deprecated 100.1.0
          */
         private function getEmailNotification()
         {
    @@ -180,7 +200,6 @@ private function getEmailNotification()
          */
         public function createCsrfValidationException(RequestInterface $request): ?InvalidRequestException
         {
    -        /** @var Redirect $resultRedirect */
             $resultRedirect = $this->resultRedirectFactory->create();
             $resultRedirect->setPath('*/*/edit');
     
    @@ -203,50 +222,49 @@ public function validateForCsrf(RequestInterface $request): ?bool
          *
          * @return Redirect
          * @SuppressWarnings(PHPMD.CyclomaticComplexity)
    +     * @throws SessionException
          */
         public function execute()
         {
    -        /** @var Redirect $resultRedirect */
             $resultRedirect = $this->resultRedirectFactory->create();
             $validFormKey = $this->formKeyValidator->validate($this->getRequest());
     
             if ($validFormKey && $this->getRequest()->isPost()) {
    -            $currentCustomerDataObject = $this->getCustomerDataObject($this->session->getCustomerId());
    -            $customerCandidateDataObject = $this->populateNewCustomerDataObject(
    -                $this->_request,
    -                $currentCustomerDataObject
    -            );
    +            $customer = $this->getCustomerDataObject($this->session->getCustomerId());
    +            $customerCandidate = $this->populateNewCustomerDataObject($this->_request, $customer);
     
                 $attributeToDelete = $this->_request->getParam('delete_attribute_value');
                 if ($attributeToDelete !== null) {
    -                $this->deleteCustomerFileAttribute(
    -                    $customerCandidateDataObject,
    -                    $attributeToDelete
    -                );
    +                $this->deleteCustomerFileAttribute($customerCandidate, $attributeToDelete);
                 }
     
                 try {
                     // whether a customer enabled change email option
    -                $isEmailChanged = $this->processChangeEmailRequest($currentCustomerDataObject);
    +                $isEmailChanged = $this->processChangeEmailRequest($customer);
     
                     // whether a customer enabled change password option
    -                $isPasswordChanged = $this->changeCustomerPassword($currentCustomerDataObject->getEmail());
    +                $isPasswordChanged = $this->changeCustomerPassword($customer->getEmail());
     
                     // No need to validate customer address while editing customer profile
    -                $this->disableAddressValidation($customerCandidateDataObject);
    +                $this->disableAddressValidation($customerCandidate);
    +
    +                $this->customerRepository->save($customerCandidate);
    +                $updatedCustomer = $this->customerRepository->getById($customerCandidate->getId());
     
    -                $this->customerRepository->save($customerCandidateDataObject);
                     $this->getEmailNotification()->credentialsChanged(
    -                    $customerCandidateDataObject,
    -                    $currentCustomerDataObject->getEmail(),
    +                    $updatedCustomer,
    +                    $customer->getEmail(),
                         $isPasswordChanged
                     );
    -                $this->dispatchSuccessEvent($customerCandidateDataObject);
    +
    +                $this->dispatchSuccessEvent($updatedCustomer);
                     $this->messageManager->addSuccessMessage(__('You saved the account information.'));
                     // logout from current session if password or email changed.
                     if ($isPasswordChanged || $isEmailChanged) {
                         $this->session->logout();
                         $this->session->start();
    +                    $this->addComplexSuccessMessage($customer, $updatedCustomer);
    +
                         return $resultRedirect->setPath('customer/account/login');
                     }
                     return $resultRedirect->setPath('customer/account');
    @@ -276,13 +294,32 @@ public function execute()
                 $this->session->setCustomerFormData($this->getRequest()->getPostValue());
             }
     
    -        /** @var Redirect $resultRedirect */
             $resultRedirect = $this->resultRedirectFactory->create();
             $resultRedirect->setPath('*/*/edit');
     
             return $resultRedirect;
         }
     
    +    /**
    +     * Adds a complex success message if email confirmation is required
    +     *
    +     * @param CustomerInterface $outdatedCustomer
    +     * @param CustomerInterface $updatedCustomer
    +     * @throws LocalizedException
    +     */
    +    private function addComplexSuccessMessage(
    +        CustomerInterface $outdatedCustomer,
    +        CustomerInterface $updatedCustomer
    +    ): void {
    +        if (($outdatedCustomer->getEmail() !== $updatedCustomer->getEmail())
    +            && $this->accountConfirmation->isCustomerEmailChangedConfirmRequired($updatedCustomer)) {
    +            $this->messageManager->addComplexSuccessMessage(
    +                'confirmAccountSuccessMessage',
    +                ['url' => $this->customerUrl->getEmailConfirmationUrl($updatedCustomer->getEmail())]
    +            );
    +        }
    +    }
    +
         /**
          * Account editing action completed successfully event
          *
    @@ -303,6 +340,8 @@ private function dispatchSuccessEvent(CustomerInterface $customerCandidateDataOb
          * @param int $customerId
          *
          * @return CustomerInterface
    +     * @throws LocalizedException
    +     * @throws NoSuchEntityException
          */
         private function getCustomerDataObject($customerId)
         {
    @@ -342,7 +381,7 @@ private function populateNewCustomerDataObject(
          *
          * @param string $email
          * @return boolean
    -     * @throws InvalidEmailOrPasswordException|InputException
    +     * @throws InvalidEmailOrPasswordException|InputException|LocalizedException
          */
         protected function changeCustomerPassword($email)
         {
    @@ -355,7 +394,7 @@ protected function changeCustomerPassword($email)
                     throw new InputException(__('Password confirmation doesn\'t match entered password.'));
                 }
     
    -            $isPasswordChanged = $this->customerAccountManagement->changePassword($email, $currPass, $newPass);
    +            $isPasswordChanged = $this->accountManagement->changePassword($email, $currPass, $newPass);
             }
     
             return $isPasswordChanged;
    @@ -393,8 +432,6 @@ private function processChangeEmailRequest(CustomerInterface $currentCustomerDat
          * Get Customer Mapper instance
          *
          * @return Mapper
    -     *
    -     * @deprecated 100.1.3
          */
         private function getCustomerMapper()
         {
    @@ -424,6 +461,7 @@ private function disableAddressValidation($customer)
          * @param CustomerInterface $customerCandidateDataObject
          * @param string $attributeToDelete
          * @return void
    +     * @throws FileSystemException
          */
         private function deleteCustomerFileAttribute(
             CustomerInterface $customerCandidateDataObject,
    
  • app/code/Magento/CustomerDownloadableGraphQl/composer.json+10 8 modified
    @@ -2,19 +2,20 @@
         "name": "magento/module-customer-downloadable-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/module-downloadable-graph-ql": "*",
    -        "magento/module-graph-ql": "*",
    -        "magento/framework": "*"
    +        "magento/module-downloadable-graph-ql": "100.4.*",
    +        "magento/module-graph-ql": "100.4.*",
    +        "magento/framework": "103.0.*"
         },
         "suggest": {
    -        "magento/module-catalog-graph-ql": "*"
    +        "magento/module-catalog-graph-ql": "100.4.*"
         },
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -24,3 +25,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Customer/etc/adminhtml/system.xml+4 0 modified
    @@ -193,6 +193,10 @@
                         <comment>Email template chosen based on theme fallback when "Default" option is selected.</comment>
                         <source_model>Magento\Config\Model\Config\Source\Email\Template</source_model>
                     </field>
    +                <field id="confirm" translate="label" type="select" sortOrder="30" showInDefault="1" showInWebsite="1" canRestore="1">
    +                    <label>Require email confirmation if email has been changed</label>
    +                    <source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
    +                </field>
                 </group>
                 <group id="address" translate="label" sortOrder="40" showInDefault="1" showInWebsite="1" showInStore="1">
                     <label>Name and Address Options</label>
    
  • app/code/Magento/Customer/etc/config.xml+1 0 modified
    @@ -32,6 +32,7 @@
                 <account_information>
                     <change_email_template>customer_account_information_change_email_template</change_email_template>
                     <change_email_and_password_template>customer_account_information_change_email_and_password_template</change_email_and_password_template>
    +                <confirm>0</confirm>
                 </account_information>
                 <password>
                     <forgot_email_identity>support</forgot_email_identity>
    
  • app/code/Magento/Customer/etc/frontend/di.xml+3 0 modified
    @@ -127,4 +127,7 @@
                 </argument>
             </arguments>
         </type>
    +    <type name="Magento\Customer\Model\Session">
    +        <plugin name="afterLogout" type="Magento\Customer\Model\Plugin\ClearSessionsAfterLogoutPlugin"/>
    +    </type>
     </config>
    
  • app/code/Magento/CustomerGraphQl/composer.json+16 14 modified
    @@ -2,24 +2,25 @@
         "name": "magento/module-customer-graph-ql",
         "description": "N/A",
         "type": "magento2-module",
    -    "require": {
    -        "php": "~7.4.0||~8.1.0",
    -        "magento/module-authorization": "*",
    -        "magento/module-customer": "*",
    -        "magento/module-eav": "*",
    -        "magento/module-graph-ql": "*",
    -        "magento/module-newsletter": "*",
    -        "magento/module-integration": "*",
    -        "magento/module-store": "*",
    -        "magento/framework": "*",
    -        "magento/module-directory": "*",
    -        "magento/module-tax": "*",
    -        "magento/module-graph-ql-cache": "*"
    -    },
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "100.4.5",
    +    "require": {
    +        "php": "~7.4.0||~8.1.0",
    +        "magento/module-authorization": "100.4.*",
    +        "magento/module-customer": "103.0.*",
    +        "magento/module-eav": "102.1.*",
    +        "magento/module-graph-ql": "100.4.*",
    +        "magento/module-newsletter": "100.4.*",
    +        "magento/module-integration": "100.4.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/framework": "103.0.*",
    +        "magento/module-directory": "100.4.*",
    +        "magento/module-tax": "100.4.*",
    +        "magento/module-graph-ql-cache": "100.4.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -29,3 +30,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/CustomerImportExport/composer.json+14 12 modified
    @@ -1,24 +1,25 @@
     {
         "name": "magento/module-customer-import-export",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.5",
         "require": {
             "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    -        "magento/module-backend": "*",
    -        "magento/module-customer": "*",
    -        "magento/module-directory": "*",
    -        "magento/module-eav": "*",
    -        "magento/module-import-export": "*",
    -        "magento/module-store": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-customer": "103.0.*",
    +        "magento/module-directory": "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 @@
             }
         }
     }
    +
    
  • app/code/Magento/Customer/Model/AccountConfirmation.php+72 1 modified
    @@ -3,8 +3,11 @@
      * Copyright © Magento, Inc. All rights reserved.
      * See COPYING.txt for license details.
      */
    +declare(strict_types=1);
    +
     namespace Magento\Customer\Model;
     
    +use Magento\Customer\Api\Data\CustomerInterface;
     use Magento\Store\Model\ScopeInterface;
     use Magento\Framework\App\Config\ScopeConfigInterface;
     use Magento\Framework\Registry;
    @@ -15,10 +18,30 @@
     class AccountConfirmation
     {
         /**
    -     * Configuration path for email confirmation.
    +     * Configuration path for email confirmation when creating a new customer
          */
         public const XML_PATH_IS_CONFIRM = 'customer/create_account/confirm';
     
    +    /**
    +     * Configuration path for email confirmation when updating an existing customer's email
    +     */
    +    public const XML_PATH_IS_CONFIRM_EMAIL_CHANGED = 'customer/account_information/confirm';
    +
    +    /**
    +     * Constant for confirmed status
    +     */
    +    private const ACCOUNT_CONFIRMED = 'account_confirmed';
    +
    +    /**
    +     * Constant for confirmation required status
    +     */
    +    private const ACCOUNT_CONFIRMATION_REQUIRED = 'account_confirmation_required';
    +
    +    /**
    +     * Constant for confirmation not required status
    +     */
    +    private const ACCOUNT_CONFIRMATION_NOT_REQUIRED = 'account_confirmation_not_required';
    +
         /**
          * @var ScopeConfigInterface
          */
    @@ -64,6 +87,54 @@ public function isConfirmationRequired($websiteId, $customerId, $customerEmail):
             );
         }
     
    +    /**
    +     * Check if accounts confirmation is required if email has been changed
    +     *
    +     * @param int|null $websiteId
    +     * @param int|null $customerId
    +     * @param string|null $customerEmail
    +     * @return bool
    +     */
    +    public function isEmailChangedConfirmationRequired($websiteId, $customerId, $customerEmail): bool
    +    {
    +        return !$this->canSkipConfirmation($customerId, $customerEmail)
    +            && $this->scopeConfig->isSetFlag(
    +                self::XML_PATH_IS_CONFIRM_EMAIL_CHANGED,
    +                ScopeInterface::SCOPE_WEBSITES,
    +                $websiteId
    +            );
    +    }
    +
    +    /**
    +     * Returns an email confirmation status if email has been changed
    +     *
    +     * @param CustomerInterface $customer
    +     * @return string
    +     */
    +    private function getEmailChangedConfirmStatus(CustomerInterface $customer): string
    +    {
    +        $isEmailChangedConfirmationRequired = $this->isEmailChangedConfirmationRequired(
    +            (int)$customer->getWebsiteId(),
    +            (int)$customer->getId(),
    +            $customer->getEmail()
    +        );
    +
    +        return $isEmailChangedConfirmationRequired
    +            ? $customer->getConfirmation() ? self::ACCOUNT_CONFIRMATION_REQUIRED : self::ACCOUNT_CONFIRMED
    +            : self::ACCOUNT_CONFIRMATION_NOT_REQUIRED;
    +    }
    +
    +    /**
    +     * Checks if email confirmation is required for the customer
    +     *
    +     * @param CustomerInterface $customer
    +     * @return bool
    +     */
    +    public function isCustomerEmailChangedConfirmRequired(CustomerInterface $customer):bool
    +    {
    +        return $this->getEmailChangedConfirmStatus($customer) === self::ACCOUNT_CONFIRMATION_REQUIRED;
    +    }
    +
         /**
          * Check whether confirmation may be skipped when registering using certain email address.
          *
    
  • app/code/Magento/Customer/Model/AccountManagement.php+81 25 modified
    @@ -3,6 +3,7 @@
      * Copyright © Magento, Inc. All rights reserved.
      * See COPYING.txt for license details.
      */
    +declare(strict_types=1);
     
     namespace Magento\Customer\Model;
     
    @@ -56,6 +57,7 @@
     use Magento\Store\Model\ScopeInterface;
     use Magento\Store\Model\StoreManagerInterface;
     use Psr\Log\LoggerInterface as PsrLogger;
    +use Magento\Customer\Model\Logger as CustomerLogger;
     
     /**
      * Handle various customer account actions
    @@ -67,6 +69,11 @@
      */
     class AccountManagement implements AccountManagementInterface
     {
    +    /**
    +     * System Configuration Path for Enable/Disable Login at Guest Checkout
    +     */
    +    public const GUEST_CHECKOUT_LOGIN_OPTION_SYS_CONFIG = 'checkout/options/enable_guest_checkout_login';
    +
         /**
          * Configuration paths for create account email template
          *
    @@ -219,7 +226,7 @@ class AccountManagement implements AccountManagementInterface
         private $customerFactory;
     
         /**
    -     * @var \Magento\Customer\Api\Data\ValidationResultsInterfaceFactory
    +     * @var ValidationResultsInterfaceFactory
          */
         private $validationResultsDataFactory;
     
    @@ -229,7 +236,7 @@ class AccountManagement implements AccountManagementInterface
         private $eventManager;
     
         /**
    -     * @var \Magento\Store\Model\StoreManagerInterface
    +     * @var StoreManagerInterface
          */
         private $storeManager;
     
    @@ -299,7 +306,7 @@ class AccountManagement implements AccountManagementInterface
         protected $dataProcessor;
     
         /**
    -     * @var \Magento\Framework\Registry
    +     * @var Registry
          */
         protected $registry;
     
    @@ -319,7 +326,7 @@ class AccountManagement implements AccountManagementInterface
         protected $objectFactory;
     
         /**
    -     * @var \Magento\Framework\Api\ExtensibleDataObjectConverter
    +     * @var ExtensibleDataObjectConverter
          */
         protected $extensibleDataObjectConverter;
     
    @@ -339,7 +346,7 @@ class AccountManagement implements AccountManagementInterface
         private $emailNotification;
     
         /**
    -     * @var \Magento\Eav\Model\Validator\Attribute\Backend
    +     * @var Backend
          */
         private $eavValidator;
     
    @@ -388,6 +395,11 @@ class AccountManagement implements AccountManagementInterface
          */
         private $authorization;
     
    +    /**
    +     * @var CustomerLogger
    +     */
    +    private CustomerLogger $customerLogger;
    +
         /**
          * @param CustomerFactory $customerFactory
          * @param ManagerInterface $eventManager
    @@ -426,6 +438,7 @@ class AccountManagement implements AccountManagementInterface
          * @param AuthorizationInterface|null $authorization
          * @param AuthenticationInterface|null $authentication
          * @param Backend|null $eavValidator
    +     * @param CustomerLogger|null $customerLogger
          * @SuppressWarnings(PHPMD.CyclomaticComplexity)
          * @SuppressWarnings(PHPMD.ExcessiveParameterList)
          * @SuppressWarnings(PHPMD.NPathComplexity)
    @@ -469,7 +482,8 @@ public function __construct(
             SessionCleanerInterface $sessionCleaner = null,
             AuthorizationInterface $authorization = null,
             AuthenticationInterface $authentication = null,
    -        Backend $eavValidator = null
    +        Backend $eavValidator = null,
    +        ?CustomerLogger $customerLogger = null
         ) {
             $this->customerFactory = $customerFactory;
             $this->eventManager = $eventManager;
    @@ -512,6 +526,7 @@ public function __construct(
             $this->authorization = $authorization ?? $objectManager->get(AuthorizationInterface::class);
             $this->authentication = $authentication ?? $objectManager->get(AuthenticationInterface::class);
             $this->eavValidator = $eavValidator ?? $objectManager->get(Backend::class);
    +        $this->customerLogger = $customerLogger ?? $objectManager->get(CustomerLogger::class);
         }
     
         /**
    @@ -562,9 +577,9 @@ public function activateById($customerId, $confirmationKey)
         /**
          * Activate a customer account using a key that was sent in a confirmation email.
          *
    -     * @param \Magento\Customer\Api\Data\CustomerInterface $customer
    +     * @param CustomerInterface $customer
          * @param string $confirmationKey
    -     * @return \Magento\Customer\Api\Data\CustomerInterface
    +     * @return CustomerInterface
          * @throws InputException
          * @throws InputMismatchException
          * @throws InvalidTransitionException
    @@ -586,12 +601,17 @@ private function activateCustomer($customer, $confirmationKey)
             // No need to validate customer and customer address while activating customer
             $this->setIgnoreValidationFlag($customer);
             $this->customerRepository->save($customer);
    -        $this->getEmailNotification()->newAccount(
    -            $customer,
    -            'confirmed',
    -            '',
    -            $this->storeManager->getStore()->getId()
    -        );
    +
    +        $customerLastLoginAt = $this->customerLogger->get((int)$customer->getId())->getLastLoginAt();
    +        if (!$customerLastLoginAt) {
    +            $this->getEmailNotification()->newAccount(
    +                $customer,
    +                'confirmed',
    +                '',
    +                $this->storeManager->getStore()->getId()
    +            );
    +        }
    +
             return $customer;
         }
     
    @@ -615,7 +635,9 @@ public function authenticate($username, $password)
             } catch (InvalidEmailOrPasswordException $e) {
                 throw new InvalidEmailOrPasswordException(__('Invalid login or password.'));
             }
    -        if ($customer->getConfirmation() && $this->isConfirmationRequired($customer)) {
    +
    +        if ($customer->getConfirmation()
    +            && ($this->isConfirmationRequired($customer) || $this->isEmailChangedConfirmationRequired($customer))) {
                 throw new EmailNotConfirmedException(__("This account isn't confirmed. Verify and try again."));
             }
     
    @@ -630,6 +652,21 @@ public function authenticate($username, $password)
             return $customer;
         }
     
    +    /**
    +     * Checks if account confirmation is required if the email address has been changed
    +     *
    +     * @param CustomerInterface $customer
    +     * @return bool
    +     */
    +    private function isEmailChangedConfirmationRequired(CustomerInterface $customer): bool
    +    {
    +        return $this->accountConfirmation->isEmailChangedConfirmationRequired(
    +            (int)$customer->getWebsiteId(),
    +            (int)$customer->getId(),
    +            $customer->getEmail()
    +        );
    +    }
    +
         /**
          * @inheritdoc
          */
    @@ -715,7 +752,7 @@ public function resetPassword($email, $resetToken, $newPassword)
             $this->setIgnoreValidationFlag($customer);
     
             //Validate Token and new password strength
    -        $this->validateResetPasswordToken($customer->getId(), $resetToken);
    +        $this->validateResetPasswordToken((int)$customer->getId(), $resetToken);
             $this->credentialsValidator->checkPasswordDifferentFromEmail(
                 $email,
                 $newPassword
    @@ -829,13 +866,10 @@ public function getConfirmationStatus($customerId)
         {
             // load customer by id
             $customer = $this->customerRepository->getById($customerId);
    -        if ($this->isConfirmationRequired($customer)) {
    -            if (!$customer->getConfirmation()) {
    -                return self::ACCOUNT_CONFIRMED;
    -            }
    -            return self::ACCOUNT_CONFIRMATION_REQUIRED;
    -        }
    -        return self::ACCOUNT_CONFIRMATION_NOT_REQUIRED;
    +
    +        return $this->isConfirmationRequired($customer)
    +            ? $customer->getConfirmation() ? self::ACCOUNT_CONFIRMATION_REQUIRED : self::ACCOUNT_CONFIRMED
    +            : self::ACCOUNT_CONFIRMATION_NOT_REQUIRED;
         }
     
         /**
    @@ -1105,9 +1139,24 @@ public function validate(CustomerInterface $customer)
     
         /**
          * @inheritdoc
    +     *
    +     * @param string $customerEmail
    +     * @param int|null $websiteId
    +     * @return bool
    +     * @throws LocalizedException
          */
         public function isEmailAvailable($customerEmail, $websiteId = null)
         {
    +        $guestLoginConfig = $this->scopeConfig->getValue(
    +            self::GUEST_CHECKOUT_LOGIN_OPTION_SYS_CONFIG,
    +            ScopeInterface::SCOPE_WEBSITE,
    +            $websiteId
    +        );
    +
    +        if (!$guestLoginConfig) {
    +            return true;
    +        }
    +
             try {
                 if ($websiteId === null) {
                     $websiteId = $this->storeManager->getStore()->getWebsiteId();
    @@ -1216,6 +1265,7 @@ public function isReadonly($customerId)
          * @return $this
          * @throws LocalizedException
          * @deprecated 100.1.0
    +     * @see EmailNotification::newAccount()
          */
         protected function sendNewAccountEmail(
             $customer,
    @@ -1259,6 +1309,7 @@ protected function sendNewAccountEmail(
          * @throws LocalizedException
          * @throws NoSuchEntityException
          * @deprecated 100.1.0
    +     * @see EmailNotification::credentialsChanged()
          */
         protected function sendPasswordResetNotificationEmail($customer)
         {
    @@ -1272,6 +1323,7 @@ protected function sendPasswordResetNotificationEmail($customer)
          * @param int|string|null $defaultStoreId
          * @return int
          * @deprecated 100.1.0
    +     * @see StoreManagerInterface::getWebsite()
          * @throws LocalizedException
          */
         protected function getWebsiteStoreId($customer, $defaultStoreId = null)
    @@ -1289,6 +1341,7 @@ protected function getWebsiteStoreId($customer, $defaultStoreId = null)
          *
          * @return array
          * @deprecated 100.1.0
    +     * @see EmailNotification::TEMPLATE_TYPES
          */
         protected function getTemplateTypes()
         {
    @@ -1322,6 +1375,7 @@ protected function getTemplateTypes()
          * @return $this
          * @throws MailException
          * @deprecated 100.1.0
    +     * @see EmailNotification::sendEmailTemplate()
          */
         protected function sendEmailTemplate(
             $customer,
    @@ -1476,6 +1530,7 @@ public function changeResetPasswordLinkToken(CustomerInterface $customer, string
          * @throws LocalizedException
          * @throws NoSuchEntityException
          * @deprecated 100.1.0
    +     * @see EmailNotification::passwordReminder()
          */
         public function sendPasswordReminderEmail($customer)
         {
    @@ -1505,6 +1560,7 @@ public function sendPasswordReminderEmail($customer)
          * @throws LocalizedException
          * @throws NoSuchEntityException
          * @deprecated 100.1.0
    +     * @see EmailNotification::passwordResetConfirmation()
          */
         public function sendPasswordResetConfirmationEmail($customer)
         {
    @@ -1550,6 +1606,7 @@ protected function getAddressById(CustomerInterface $customer, $addressId)
          * @return Data\CustomerSecure
          * @throws NoSuchEntityException
          * @deprecated 100.1.0
    +     * @see EmailNotification::getFullCustomerObject()
          */
         protected function getFullCustomerObject($customer)
         {
    @@ -1558,7 +1615,7 @@ protected function getFullCustomerObject($customer)
             $mergedCustomerData = $this->customerRegistry->retrieveSecureData($customer->getId());
             $customerData = $this->dataProcessor->buildOutputDataArray(
                 $customer,
    -            \Magento\Customer\Api\Data\CustomerInterface::class
    +            CustomerInterface::class
             );
             $mergedCustomerData->addData($customerData);
             $mergedCustomerData->setData('name', $this->customerViewHelper->getCustomerName($customer));
    @@ -1594,7 +1651,6 @@ private function disableAddressValidation($customer)
          * Get email notification
          *
          * @return EmailNotificationInterface
    -     * @deprecated 100.1.0
          */
         private function getEmailNotification()
         {
    
  • app/code/Magento/Customer/Model/EmailNotification.php+58 15 modified
    @@ -9,6 +9,8 @@
     
     use Magento\Framework\App\Config\ScopeConfigInterface;
     use Magento\Framework\App\ObjectManager;
    +use Magento\Framework\Exception\MailException;
    +use Magento\Framework\Exception\NoSuchEntityException;
     use Magento\Framework\Mail\Template\SenderResolverInterface;
     use Magento\Store\Model\App\Emulation;
     use Magento\Store\Model\StoreManagerInterface;
    @@ -30,28 +32,28 @@ class EmailNotification implements EmailNotificationInterface
         /**#@+
          * Configuration paths for email templates and identities
          */
    -    const XML_PATH_FORGOT_EMAIL_IDENTITY = 'customer/password/forgot_email_identity';
    +    public const XML_PATH_FORGOT_EMAIL_IDENTITY = 'customer/password/forgot_email_identity';
     
    -    const XML_PATH_RESET_PASSWORD_TEMPLATE = 'customer/password/reset_password_template';
    +    public const XML_PATH_RESET_PASSWORD_TEMPLATE = 'customer/password/reset_password_template';
     
    -    const XML_PATH_CHANGE_EMAIL_TEMPLATE = 'customer/account_information/change_email_template';
    +    public const XML_PATH_CHANGE_EMAIL_TEMPLATE = 'customer/account_information/change_email_template';
     
    -    const XML_PATH_CHANGE_EMAIL_AND_PASSWORD_TEMPLATE =
    +    public const XML_PATH_CHANGE_EMAIL_AND_PASSWORD_TEMPLATE =
             'customer/account_information/change_email_and_password_template';
     
    -    const XML_PATH_FORGOT_EMAIL_TEMPLATE = 'customer/password/forgot_email_template';
    +    public const XML_PATH_FORGOT_EMAIL_TEMPLATE = 'customer/password/forgot_email_template';
     
    -    const XML_PATH_REMIND_EMAIL_TEMPLATE = 'customer/password/remind_email_template';
    +    public const XML_PATH_REMIND_EMAIL_TEMPLATE = 'customer/password/remind_email_template';
     
    -    const XML_PATH_REGISTER_EMAIL_IDENTITY = 'customer/create_account/email_identity';
    +    public const XML_PATH_REGISTER_EMAIL_IDENTITY = 'customer/create_account/email_identity';
     
    -    const XML_PATH_REGISTER_EMAIL_TEMPLATE = 'customer/create_account/email_template';
    +    public const XML_PATH_REGISTER_EMAIL_TEMPLATE = 'customer/create_account/email_template';
     
    -    const XML_PATH_REGISTER_NO_PASSWORD_EMAIL_TEMPLATE = 'customer/create_account/email_no_password_template';
    +    public const XML_PATH_REGISTER_NO_PASSWORD_EMAIL_TEMPLATE = 'customer/create_account/email_no_password_template';
     
    -    const XML_PATH_CONFIRM_EMAIL_TEMPLATE = 'customer/create_account/email_confirmation_template';
    +    public const XML_PATH_CONFIRM_EMAIL_TEMPLATE = 'customer/create_account/email_confirmation_template';
     
    -    const XML_PATH_CONFIRMED_EMAIL_TEMPLATE = 'customer/create_account/email_confirmed_template';
    +    public const XML_PATH_CONFIRMED_EMAIL_TEMPLATE = 'customer/create_account/email_confirmed_template';
     
         /**
          * self::NEW_ACCOUNT_EMAIL_REGISTERED               welcome email, when confirmation is disabled
    @@ -62,7 +64,7 @@ class EmailNotification implements EmailNotificationInterface
          *                                                  and password is set
          * self::NEW_ACCOUNT_EMAIL_CONFIRMATION             email with confirmation link
          */
    -    const TEMPLATE_TYPES = [
    +    public const TEMPLATE_TYPES = [
             self::NEW_ACCOUNT_EMAIL_REGISTERED => self::XML_PATH_REGISTER_EMAIL_TEMPLATE,
             self::NEW_ACCOUNT_EMAIL_REGISTERED_NO_PASSWORD => self::XML_PATH_REGISTER_NO_PASSWORD_EMAIL_TEMPLATE,
             self::NEW_ACCOUNT_EMAIL_CONFIRMED => self::XML_PATH_CONFIRMED_EMAIL_TEMPLATE,
    @@ -71,7 +73,9 @@ class EmailNotification implements EmailNotificationInterface
     
         /**#@-*/
     
    -    /**#@-*/
    +    /**
    +     * @var CustomerRegistry
    +     */
         private $customerRegistry;
     
         /**
    @@ -109,6 +113,11 @@ class EmailNotification implements EmailNotificationInterface
          */
         private $emulation;
     
    +    /**
    +     * @var AccountConfirmation
    +     */
    +    private AccountConfirmation $accountConfirmation;
    +
         /**
          * @param CustomerRegistry $customerRegistry
          * @param StoreManagerInterface $storeManager
    @@ -118,6 +127,7 @@ class EmailNotification implements EmailNotificationInterface
          * @param ScopeConfigInterface $scopeConfig
          * @param SenderResolverInterface|null $senderResolver
          * @param Emulation|null $emulation
    +     * @param AccountConfirmation|null $accountConfirmation
          */
         public function __construct(
             CustomerRegistry $customerRegistry,
    @@ -127,7 +137,8 @@ public function __construct(
             DataObjectProcessor $dataProcessor,
             ScopeConfigInterface $scopeConfig,
             SenderResolverInterface $senderResolver = null,
    -        Emulation $emulation =null
    +        Emulation $emulation = null,
    +        ?AccountConfirmation $accountConfirmation = null
         ) {
             $this->customerRegistry = $customerRegistry;
             $this->storeManager = $storeManager;
    @@ -137,6 +148,8 @@ public function __construct(
             $this->scopeConfig = $scopeConfig;
             $this->senderResolver = $senderResolver ?? ObjectManager::getInstance()->get(SenderResolverInterface::class);
             $this->emulation = $emulation ?? ObjectManager::getInstance()->get(Emulation::class);
    +        $this->accountConfirmation = $accountConfirmation ?? ObjectManager::getInstance()
    +                ->get(AccountConfirmation::class);
         }
     
         /**
    @@ -146,13 +159,15 @@ public function __construct(
          * @param string $origCustomerEmail
          * @param bool $isPasswordChanged
          * @return void
    +     * @throws LocalizedException
          */
         public function credentialsChanged(
             CustomerInterface $savedCustomer,
             $origCustomerEmail,
             $isPasswordChanged = false
         ): void {
             if ($origCustomerEmail != $savedCustomer->getEmail()) {
    +            $this->emailChangedConfirmation($savedCustomer);
                 if ($isPasswordChanged) {
                     $this->emailAndPasswordChanged($savedCustomer, $origCustomerEmail);
                     $this->emailAndPasswordChanged($savedCustomer, $savedCustomer->getEmail());
    @@ -175,6 +190,8 @@ public function credentialsChanged(
          * @param CustomerInterface $customer
          * @param string $email
          * @return void
    +     * @throws MailException
    +     * @throws NoSuchEntityException|LocalizedException
          */
         private function emailAndPasswordChanged(CustomerInterface $customer, $email): void
         {
    @@ -201,6 +218,8 @@ private function emailAndPasswordChanged(CustomerInterface $customer, $email): v
          * @param CustomerInterface $customer
          * @param string $email
          * @return void
    +     * @throws MailException
    +     * @throws NoSuchEntityException|LocalizedException
          */
         private function emailChanged(CustomerInterface $customer, $email): void
         {
    @@ -226,6 +245,8 @@ private function emailChanged(CustomerInterface $customer, $email): void
          *
          * @param CustomerInterface $customer
          * @return void
    +     * @throws MailException
    +     * @throws NoSuchEntityException|LocalizedException
          */
         private function passwordReset(CustomerInterface $customer): void
         {
    @@ -255,7 +276,7 @@ private function passwordReset(CustomerInterface $customer): void
          * @param int|null $storeId
          * @param string $email
          * @return void
    -     * @throws \Magento\Framework\Exception\MailException
    +     * @throws MailException|LocalizedException
          */
         private function sendEmailTemplate(
             $customer,
    @@ -293,6 +314,7 @@ private function sendEmailTemplate(
          *
          * @param CustomerInterface $customer
          * @return CustomerSecure
    +     * @throws NoSuchEntityException
          */
         private function getFullCustomerObject($customer): CustomerSecure
         {
    @@ -312,6 +334,7 @@ private function getFullCustomerObject($customer): CustomerSecure
          * @param CustomerInterface $customer
          * @param int|string|null $defaultStoreId
          * @return int
    +     * @throws LocalizedException
          */
         private function getWebsiteStoreId($customer, $defaultStoreId = null): int
         {
    @@ -327,6 +350,9 @@ private function getWebsiteStoreId($customer, $defaultStoreId = null): int
          *
          * @param CustomerInterface $customer
          * @return void
    +     * @throws LocalizedException
    +     * @throws MailException
    +     * @throws NoSuchEntityException
          */
         public function passwordReminder(CustomerInterface $customer): void
         {
    @@ -351,6 +377,9 @@ public function passwordReminder(CustomerInterface $customer): void
          *
          * @param CustomerInterface $customer
          * @return void
    +     * @throws LocalizedException
    +     * @throws MailException
    +     * @throws NoSuchEntityException
          */
         public function passwordResetConfirmation(CustomerInterface $customer): void
         {
    @@ -412,4 +441,18 @@ public function newAccount(
                 $storeId
             );
         }
    +
    +    /**
    +     * Sending an email to confirm the email address in case the email address has been changed
    +     *
    +     * @param CustomerInterface $customer
    +     * @throws LocalizedException
    +     */
    +    private function emailChangedConfirmation(CustomerInterface $customer): void
    +    {
    +        if (!$this->accountConfirmation->isCustomerEmailChangedConfirmRequired($customer)) {
    +            return;
    +        }
    +        $this->newAccount($customer, self::NEW_ACCOUNT_EMAIL_CONFIRMATION, null, $customer->getStoreId());
    +    }
     }
    
  • app/code/Magento/Customer/Model/Plugin/ClearSessionsAfterLogoutPlugin.php+108 0 added
    @@ -0,0 +1,108 @@
    +<?php
    +/**
    + * Copyright © Magento, Inc. All rights reserved.
    + * See COPYING.txt for license details.
    + */
    +declare(strict_types=1);
    +
    +namespace Magento\Customer\Model\Plugin;
    +
    +use Magento\Customer\Model\Session;
    +use Magento\Framework\App\Area;
    +use Magento\Framework\App\State;
    +use Magento\Framework\Session\SaveHandlerInterface;
    +use Magento\Framework\Session\StorageInterface;
    +use Magento\Framework\Exception\SessionException;
    +use Psr\Log\LoggerInterface;
    +use Magento\Framework\Exception\LocalizedException;
    +
    +/**
    + * Clears previous active sessions after logout
    + *
    + * @SuppressWarnings(PHPMD.CookieAndSessionMisuse)
    + */
    +class ClearSessionsAfterLogoutPlugin
    +{
    +    /**
    +     * Array key for all active previous session ids.
    +     */
    +    private const PREVIOUS_ACTIVE_SESSIONS = 'previous_active_sessions';
    +
    +    /**
    +     * @var Session
    +     */
    +    private Session $session;
    +
    +    /**
    +     * @var SaveHandlerInterface
    +     */
    +    private SaveHandlerInterface $saveHandler;
    +
    +    /**
    +     * @var StorageInterface
    +     */
    +    private StorageInterface $storage;
    +
    +    /**
    +     * @var State
    +     */
    +    private State $state;
    +
    +    /**
    +     * @var LoggerInterface
    +     */
    +    private LoggerInterface $logger;
    +
    +    /**
    +     * Initialize Dependencies
    +     *
    +     * @param Session $customerSession
    +     * @param SaveHandlerInterface $saveHandler
    +     * @param StorageInterface $storage
    +     * @param State $state
    +     * @param LoggerInterface $logger
    +     */
    +    public function __construct(
    +        Session $customerSession,
    +        SaveHandlerInterface $saveHandler,
    +        StorageInterface $storage,
    +        State $state,
    +        LoggerInterface $logger
    +    ) {
    +        $this->session = $customerSession;
    +        $this->saveHandler = $saveHandler;
    +        $this->storage = $storage;
    +        $this->state = $state;
    +        $this->logger = $logger;
    +    }
    +
    +    /**
    +     * Plugin to clear session after logout
    +     *
    +     * @param Session $subject
    +     * @param Session $result
    +     * @return Session
    +     * @throws LocalizedException
    +     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
    +     */
    +    public function afterLogout(Session $subject, Session $result): Session
    +    {
    +        $isAreaFrontEnd = $this->state->getAreaCode() === Area::AREA_FRONTEND;
    +        $previousSessions = $this->storage->getData(self::PREVIOUS_ACTIVE_SESSIONS);
    +
    +        if ($isAreaFrontEnd && !empty($previousSessions)) {
    +            foreach ($previousSessions as $sessionId) {
    +                try {
    +                    $this->session->start();
    +                    $this->saveHandler->destroy($sessionId);
    +                    $this->session->writeClose();
    +                } catch (SessionException $e) {
    +                    $this->logger->error($e);
    +                }
    +
    +            }
    +            $this->storage->setData(self::PREVIOUS_ACTIVE_SESSIONS, []);
    +        }
    +        return $result;
    +    }
    +}
    
  • app/code/Magento/Customer/Model/Plugin/CustomerNotification.php+47 15 modified
    @@ -3,6 +3,7 @@
      * Copyright © Magento, Inc. All rights reserved.
      * See COPYING.txt for license details.
      */
    +declare(strict_types=1);
     
     namespace Magento\Customer\Model\Plugin;
     
    @@ -16,13 +17,21 @@
     use Magento\Framework\App\RequestInterface;
     use Magento\Framework\App\State;
     use Magento\Framework\Exception\NoSuchEntityException;
    +use Magento\Framework\Session\StorageInterface;
     use Psr\Log\LoggerInterface;
     
     /**
      * Refresh the Customer session if `UpdateSession` notification registered
    + *
    + * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
      */
     class CustomerNotification
     {
    +    /**
    +     * Array key for all active previous session ids.
    +     */
    +    private const PREVIOUS_ACTIVE_SESSIONS = 'previous_active_sessions';
    +
         /**
          * @var Session
          */
    @@ -53,6 +62,11 @@ class CustomerNotification
          */
         private $request;
     
    +    /**
    +     * @var StorageInterface
    +     */
    +    private StorageInterface $storage;
    +
         /**
          * Initialize dependencies.
          *
    @@ -61,22 +75,25 @@ class CustomerNotification
          * @param State $state
          * @param CustomerRepositoryInterface $customerRepository
          * @param LoggerInterface $logger
    -     * @param RequestInterface|null $request
    +     * @param RequestInterface $request
    +     * @param StorageInterface|null $storage
          */
         public function __construct(
             Session $session,
             NotificationStorage $notificationStorage,
             State $state,
             CustomerRepositoryInterface $customerRepository,
             LoggerInterface $logger,
    -        RequestInterface $request
    +        RequestInterface $request,
    +        StorageInterface $storage = null
         ) {
             $this->session = $session;
             $this->notificationStorage = $notificationStorage;
             $this->state = $state;
             $this->customerRepository = $customerRepository;
             $this->logger = $logger;
             $this->request = $request;
    +        $this->storage = $storage ?? ObjectManager::getInstance()->get(StorageInterface::class);
         }
     
         /**
    @@ -89,18 +106,33 @@ public function __construct(
          */
         public function beforeExecute(ActionInterface $subject)
         {
    -        $customerId = $this->session->getCustomerId();
    -
    -        if ($this->isFrontendRequest() && $this->isPostRequest() && $this->isSessionUpdateRegisteredFor($customerId)) {
    -            try {
    -                $this->session->regenerateId();
    -                $customer = $this->customerRepository->getById($customerId);
    -                $this->session->setCustomerData($customer);
    -                $this->session->setCustomerGroupId($customer->getGroupId());
    -                $this->notificationStorage->remove(NotificationStorage::UPDATE_CUSTOMER_SESSION, $customer->getId());
    -            } catch (NoSuchEntityException $e) {
    -                $this->logger->error($e);
    +        $customerId = (int)$this->session->getCustomerId();
    +
    +        if (!$this->isFrontendRequest()
    +            || !$this->isPostRequest()
    +            || !$this->isSessionUpdateRegisteredFor($customerId)) {
    +            return;
    +        }
    +
    +        try {
    +            $oldSessionId = $this->session->getSessionId();
    +            $previousSessions = $this->storage->getData(self::PREVIOUS_ACTIVE_SESSIONS);
    +
    +            if (empty($previousSessions)) {
    +                $previousSessions = [];
                 }
    +            $previousSessions[] = $oldSessionId;
    +            $this->storage->setData(self::PREVIOUS_ACTIVE_SESSIONS, $previousSessions);
    +            $this->session->regenerateId();
    +            $customer = $this->customerRepository->getById($customerId);
    +            $this->session->setCustomerData($customer);
    +            $this->session->setCustomerGroupId($customer->getGroupId());
    +            $this->notificationStorage->remove(
    +                NotificationStorage::UPDATE_CUSTOMER_SESSION,
    +                $customer->getId()
    +            );
    +        } catch (NoSuchEntityException $e) {
    +            $this->logger->error($e);
             }
         }
     
    @@ -131,8 +163,8 @@ private function isFrontendRequest(): bool
          * @param int $customerId
          * @return bool
          */
    -    private function isSessionUpdateRegisteredFor($customerId): bool
    +    private function isSessionUpdateRegisteredFor(int $customerId): bool
         {
    -        return $this->notificationStorage->isExists(NotificationStorage::UPDATE_CUSTOMER_SESSION, $customerId);
    +        return (bool)$this->notificationStorage->isExists(NotificationStorage::UPDATE_CUSTOMER_SESSION, $customerId);
         }
     }
    
  • app/code/Magento/Customer/Model/Plugin/UpdateCustomer.php+11 6 modified
    @@ -52,14 +52,19 @@ public function beforeSave(
             CustomerInterface $customer,
             ?string $passwordHash = null
         ): array {
    -        $customerSessionId = $this->userContext->getUserType() === $this->userContext::USER_TYPE_CUSTOMER ?
    -                             (int)$this->userContext->getUserId() : 0;
    +        $userType = $this->userContext->getUserType();
    +        $customerSessionId = (int)$this->userContext->getUserId();
             $customerId = (int)$this->request->getParam('customerId');
             $bodyParams = $this->request->getBodyParams();
    -        if (!isset($bodyParams['customer']['Id']) && $customerId) {
    -            if ($customerId === $customerSessionId || $customerSessionId === 0) {
    -                $customer = $this->getUpdatedCustomer($customerRepository->getById($customerId), $customer);
    -            }
    +
    +        if ($userType === UserContextInterface::USER_TYPE_CUSTOMER &&
    +            !isset($bodyParams['customer']['Id']) &&
    +            $customerId &&
    +            $customerId === $customerSessionId
    +        ) {
    +            $customer = $this->getUpdatedCustomer($customerRepository->getById($customerId), $customer);
    +        } elseif ($userType === UserContextInterface::USER_TYPE_ADMIN && $customerId) {
    +            $customer = $this->getUpdatedCustomer($customerRepository->getById($customerId), $customer);
             }
     
             return [$customer, $passwordHash];
    
  • app/code/Magento/Customer/Model/ResourceModel/Customer.php+92 40 modified
    @@ -7,12 +7,25 @@
     
     namespace Magento\Customer\Model\ResourceModel;
     
    +use Magento\Customer\Api\Data\CustomerInterface;
     use Magento\Customer\Model\AccountConfirmation;
     use Magento\Customer\Model\Customer\NotificationStorage;
    +use Magento\Eav\Model\Entity\Context;
    +use Magento\Eav\Model\Entity\VersionControl\AbstractEntity;
    +use Magento\Framework\App\Config\ScopeConfigInterface;
     use Magento\Framework\App\ObjectManager;
    +use Magento\Framework\DataObject;
    +use Magento\Framework\DB\Select;
     use Magento\Framework\Exception\AlreadyExistsException;
    +use Magento\Framework\Exception\LocalizedException;
    +use Magento\Framework\Exception\NoSuchEntityException;
    +use Magento\Framework\Model\ResourceModel\Db\VersionControl\RelationComposite;
    +use Magento\Framework\Model\ResourceModel\Db\VersionControl\Snapshot;
    +use Magento\Framework\Stdlib\DateTime;
     use Magento\Framework\Validator\Exception as ValidatorException;
     use Magento\Framework\Encryption\EncryptorInterface;
    +use Magento\Framework\Validator\Factory;
    +use Magento\Store\Model\StoreManagerInterface;
     
     /**
      * Customer entity resource model
    @@ -21,27 +34,27 @@
      * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
      * @since 100.0.2
      */
    -class Customer extends \Magento\Eav\Model\Entity\VersionControl\AbstractEntity
    +class Customer extends AbstractEntity
     {
         /**
    -     * @var \Magento\Framework\Validator\Factory
    +     * @var Factory
          */
         protected $_validatorFactory;
     
         /**
          * Core store config
          *
    -     * @var \Magento\Framework\App\Config\ScopeConfigInterface
    +     * @var ScopeConfigInterface
          */
         protected $_scopeConfig;
     
         /**
    -     * @var \Magento\Framework\Stdlib\DateTime
    +     * @var DateTime
          */
         protected $dateTime;
     
         /**
    -     * @var \Magento\Store\Model\StoreManagerInterface
    +     * @var StoreManagerInterface
          */
         protected $storeManager;
     
    @@ -63,26 +76,26 @@ class Customer extends \Magento\Eav\Model\Entity\VersionControl\AbstractEntity
         /**
          * Customer constructor.
          *
    -     * @param \Magento\Eav\Model\Entity\Context $context
    -     * @param \Magento\Framework\Model\ResourceModel\Db\VersionControl\Snapshot $entitySnapshot
    -     * @param \Magento\Framework\Model\ResourceModel\Db\VersionControl\RelationComposite $entityRelationComposite
    -     * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig
    -     * @param \Magento\Framework\Validator\Factory $validatorFactory
    -     * @param \Magento\Framework\Stdlib\DateTime $dateTime
    -     * @param \Magento\Store\Model\StoreManagerInterface $storeManager
    +     * @param Context $context
    +     * @param Snapshot $entitySnapshot
    +     * @param RelationComposite $entityRelationComposite
    +     * @param ScopeConfigInterface $scopeConfig
    +     * @param Factory $validatorFactory
    +     * @param DateTime $dateTime
    +     * @param StoreManagerInterface $storeManager
          * @param array $data
    -     * @param AccountConfirmation $accountConfirmation
    +     * @param AccountConfirmation|null $accountConfirmation
          * @param EncryptorInterface|null $encryptor
          * @SuppressWarnings(PHPMD.ExcessiveParameterList)
          */
         public function __construct(
    -        \Magento\Eav\Model\Entity\Context $context,
    -        \Magento\Framework\Model\ResourceModel\Db\VersionControl\Snapshot $entitySnapshot,
    -        \Magento\Framework\Model\ResourceModel\Db\VersionControl\RelationComposite $entityRelationComposite,
    -        \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig,
    -        \Magento\Framework\Validator\Factory $validatorFactory,
    -        \Magento\Framework\Stdlib\DateTime $dateTime,
    -        \Magento\Store\Model\StoreManagerInterface $storeManager,
    +        Context $context,
    +        Snapshot $entitySnapshot,
    +        RelationComposite $entityRelationComposite,
    +        ScopeConfigInterface $scopeConfig,
    +        Factory $validatorFactory,
    +        DateTime $dateTime,
    +        StoreManagerInterface $storeManager,
             $data = [],
             AccountConfirmation $accountConfirmation = null,
             EncryptorInterface $encryptor = null
    @@ -120,16 +133,16 @@ protected function _getDefaultAttributes()
         /**
          * Check customer scope, email and confirmation key before saving
          *
    -     * @param \Magento\Framework\DataObject|\Magento\Customer\Api\Data\CustomerInterface $customer
    +     * @param DataObject|CustomerInterface $customer
          *
          * @return $this
          * @throws AlreadyExistsException
          * @throws ValidatorException
    -     * @throws \Magento\Framework\Exception\NoSuchEntityException
    +     * @throws NoSuchEntityException
          * @SuppressWarnings(PHPMD.CyclomaticComplexity)
          * @SuppressWarnings(PHPMD.NPathComplexity)
          */
    -    protected function _beforeSave(\Magento\Framework\DataObject $customer)
    +    protected function _beforeSave(DataObject $customer)
         {
             /** @var \Magento\Customer\Model\Customer $customer */
             if ($customer->getStoreId() === null) {
    @@ -169,13 +182,7 @@ protected function _beforeSave(\Magento\Framework\DataObject $customer)
             }
     
             // set confirmation key logic
    -        if (!$customer->getId() &&
    -            $this->accountConfirmation->isConfirmationRequired(
    -                $customer->getWebsiteId(),
    -                $customer->getId(),
    -                $customer->getEmail()
    -            )
    -        ) {
    +        if ($this->isConfirmationRequired($customer)) {
                 $customer->setConfirmation($customer->getRandomConfirmationKey());
             }
             // remove customer confirmation key from database, if empty
    @@ -195,6 +202,51 @@ protected function _beforeSave(\Magento\Framework\DataObject $customer)
             return $this;
         }
     
    +    /**
    +     * Checks if customer email verification is required
    +     *
    +     * @param DataObject|CustomerInterface $customer
    +     * @return bool
    +     */
    +    private function isConfirmationRequired(DataObject $customer): bool
    +    {
    +        return $this->isNewCustomerConfirmationRequired($customer)
    +            || $this->isExistingCustomerConfirmationRequired($customer);
    +    }
    +
    +    /**
    +     * Checks if customer email verification is required for a new customer
    +     *
    +     * @param DataObject|CustomerInterface $customer
    +     * @return bool
    +     */
    +    private function isNewCustomerConfirmationRequired(DataObject $customer): bool
    +    {
    +        return !$customer->getId()
    +            && $this->accountConfirmation->isConfirmationRequired(
    +                $customer->getWebsiteId(),
    +                $customer->getId(),
    +                $customer->getEmail()
    +            );
    +    }
    +
    +    /**
    +     * Checks if customer email verification is required for an existing customer
    +     *
    +     * @param DataObject|CustomerInterface $customer
    +     * @return bool
    +     */
    +    private function isExistingCustomerConfirmationRequired(DataObject $customer): bool
    +    {
    +         return $customer->getId()
    +             && $customer->dataHasChangedFor('email')
    +             && $this->accountConfirmation->isEmailChangedConfirmationRequired(
    +                 (int)$customer->getWebsiteId(),
    +                 (int)$customer->getId(),
    +                 $customer->getEmail()
    +             );
    +    }
    +
         /**
          * Validate customer entity
          *
    @@ -231,10 +283,10 @@ private function getNotificationStorage()
         /**
          * Save customer addresses and set default addresses in attributes backend
          *
    -     * @param \Magento\Framework\DataObject $customer
    +     * @param DataObject $customer
          * @return $this
          */
    -    protected function _afterSave(\Magento\Framework\DataObject $customer)
    +    protected function _afterSave(DataObject $customer)
         {
             $this->getNotificationStorage()->add(
                 NotificationStorage::UPDATE_CUSTOMER_SESSION,
    @@ -250,9 +302,9 @@ protected function _afterSave(\Magento\Framework\DataObject $customer)
         /**
          * Retrieve select object for loading base entity row
          *
    -     * @param \Magento\Framework\DataObject $object
    +     * @param DataObject $object
          * @param string|int $rowId
    -     * @return \Magento\Framework\DB\Select
    +     * @return Select
          */
         protected function _getLoadRowSelect($object, $rowId)
         {
    @@ -270,7 +322,7 @@ protected function _getLoadRowSelect($object, $rowId)
          * @param \Magento\Customer\Model\Customer $customer
          * @param string $email
          * @return $this
    -     * @throws \Magento\Framework\Exception\LocalizedException
    +     * @throws LocalizedException
          */
         public function loadByEmail(\Magento\Customer\Model\Customer $customer, $email)
         {
    @@ -285,7 +337,7 @@ public function loadByEmail(\Magento\Customer\Model\Customer $customer, $email)
     
             if ($customer->getSharingConfig()->isWebsiteScope()) {
                 if (!$customer->hasData('website_id')) {
    -                throw new \Magento\Framework\Exception\LocalizedException(
    +                throw new LocalizedException(
                         __("A customer website ID wasn't specified. The ID must be specified to use the website scope.")
                     );
                 }
    @@ -390,10 +442,10 @@ public function getWebsiteId($customerId)
         /**
          * Custom setter of increment ID if its needed
          *
    -     * @param \Magento\Framework\DataObject $object
    +     * @param DataObject $object
          * @return $this
          */
    -    public function setNewIncrementId(\Magento\Framework\DataObject $object)
    +    public function setNewIncrementId(DataObject $object)
         {
             if ($this->_scopeConfig->getValue(
                 \Magento\Customer\Model\Customer::XML_PATH_GENERATE_HUMAN_FRIENDLY_ID,
    @@ -419,7 +471,7 @@ public function changeResetPasswordLinkToken(\Magento\Customer\Model\Customer $c
             if (is_string($passwordLinkToken) && !empty($passwordLinkToken)) {
                 $customer->setRpToken($passwordLinkToken);
                 $customer->setRpTokenCreatedAt(
    -                (new \DateTime())->format(\Magento\Framework\Stdlib\DateTime::DATETIME_PHP_FORMAT)
    +                (new \DateTime())->format(DateTime::DATETIME_PHP_FORMAT)
                 );
             }
             return $this;
    @@ -469,7 +521,7 @@ public function updateSessionCutOff(int $customerId, int $timestamp): void
         /**
          * @inheritDoc
          */
    -    protected function _afterLoad(\Magento\Framework\DataObject $customer)
    +    protected function _afterLoad(DataObject $customer)
         {
             if ($customer->getData('rp_token')) {
                 $rpToken = $customer->getData('rp_token');
    
  • app/code/Magento/Customer/Model/ResourceModel/CustomerRepository.php+42 1 modified
    @@ -4,6 +4,8 @@
      * See COPYING.txt for license details.
      */
     
    +declare(strict_types=1);
    +
     namespace Magento\Customer\Model\ResourceModel;
     
     use Magento\Customer\Api\CustomerMetadataInterface;
    @@ -28,6 +30,7 @@
     use Magento\Framework\Api\SearchCriteriaInterface;
     use Magento\Framework\App\ObjectManager;
     use Magento\Framework\Event\ManagerInterface;
    +use Magento\Framework\Exception\InputException;
     use Magento\Framework\Exception\LocalizedException;
     use Magento\Framework\Exception\NoSuchEntityException;
     use Magento\Store\Model\StoreManagerInterface;
    @@ -196,12 +199,19 @@ public function __construct(
          * @throws \Magento\Framework\Exception\LocalizedException
          * @SuppressWarnings(PHPMD.CyclomaticComplexity)
          * @SuppressWarnings(PHPMD.NPathComplexity)
    +     * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
          */
         public function save(CustomerInterface $customer, $passwordHash = null)
         {
             /** @var NewOperation|null $delegatedNewOperation */
             $delegatedNewOperation = !$customer->getId() ? $this->delegatedStorage->consumeNewOperation() : null;
             $prevCustomerData = $prevCustomerDataArr = null;
    +        if ($customer->getDefaultBilling()) {
    +            $this->validateDefaultAddress($customer, CustomerInterface::DEFAULT_BILLING);
    +        }
    +        if ($customer->getDefaultShipping()) {
    +            $this->validateDefaultAddress($customer, CustomerInterface::DEFAULT_SHIPPING);
    +        }
             if ($customer->getId()) {
                 $prevCustomerData = $this->getById($customer->getId());
                 $prevCustomerDataArr = $prevCustomerData->__toArray();
    @@ -229,7 +239,7 @@ public function save(CustomerInterface $customer, $passwordHash = null)
                     $prevCustomerData ? $prevCustomerData->getStoreId() : $this->storeManager->getStore()->getId()
                 );
             }
    -        $this->validateGroupId($customer->getGroupId());
    +        $this->validateGroupId((int)$customer->getGroupId());
             $this->setCustomerGroupId($customerModel, $customerArr, $prevCustomerDataArr);
             // Need to use attribute set or future updates can cause data loss
             if (!$customerModel->getAttributeSetId()) {
    @@ -485,6 +495,7 @@ public function deleteById($customerId)
          * Helper function that adds a FilterGroup to the collection.
          *
          * @deprecated 101.0.0
    +     * @see no alternative
          * @param FilterGroup $filterGroup
          * @param Collection $collection
          * @return void
    @@ -528,4 +539,34 @@ private function setCustomerGroupId($customerModel, $customerArr, $prevCustomerD
                 $customerModel->setGroupId($prevCustomerDataArr['group_id']);
             }
         }
    +
    +    /**
    +     * To validate default address
    +     *
    +     * @param CustomerInterface $customer
    +     * @param string $defaultAddressType
    +     * @return void
    +     * @throws InputException
    +     */
    +    private function validateDefaultAddress(
    +        CustomerInterface $customer,
    +        string $defaultAddressType
    +    ): void {
    +        $addressId = $defaultAddressType === CustomerInterface::DEFAULT_BILLING ? $customer->getDefaultBilling()
    +            : $customer->getDefaultShipping();
    +        if ($customer->getAddresses()) {
    +            foreach ($customer->getAddresses() as $address) {
    +                if ((int) $addressId === (int) $address->getId()) {
    +                    return;
    +                }
    +            }
    +
    +            throw new InputException(
    +                __(
    +                    'The %fieldName value is invalid. Set the correct value and try again.',
    +                    ['fieldName' => $defaultAddressType]
    +                )
    +            );
    +        }
    +    }
     }
    
  • app/code/Magento/Customer/Test/Unit/Controller/Account/ConfirmTest.php+67 19 modified
    @@ -12,6 +12,8 @@
     use Magento\Customer\Api\Data\CustomerInterface;
     use Magento\Customer\Controller\Account\Confirm;
     use Magento\Customer\Helper\Address;
    +use Magento\Customer\Model\Logger as CustomerLogger;
    +use Magento\Customer\Model\Log;
     use Magento\Customer\Model\Session;
     use Magento\Customer\Model\Url;
     use Magento\Framework\App\Action\Context;
    @@ -123,6 +125,16 @@ class ConfirmTest extends TestCase
          */
         protected $redirectResultMock;
     
    +    /**
    +     * @var CustomerLogger|MockObject
    +     */
    +    private $customerLoggerMock;
    +
    +    /**
    +     * @var Log|MockObject
    +     */
    +    private $logMock;
    +
         /**
          * @inheritdoc
          */
    @@ -143,6 +155,9 @@ protected function setUp(): void
                 ->method('create')
                 ->willReturn($this->urlMock);
     
    +        $this->customerLoggerMock = $this->createMock(CustomerLogger::class);
    +        $this->logMock = $this->createMock(Log::class);
    +
             $this->customerAccountManagementMock =
                 $this->getMockForAbstractClass(AccountManagementInterface::class);
             $this->customerDataMock = $this->getMockForAbstractClass(CustomerInterface::class);
    @@ -195,7 +210,8 @@ protected function setUp(): void
                     'customerAccountManagement' => $this->customerAccountManagementMock,
                     'customerRepository' => $this->customerRepositoryMock,
                     'addressHelper' => $this->addressHelperMock,
    -                'urlFactory' => $urlFactoryMock
    +                'urlFactory' => $urlFactoryMock,
    +                'customerLogger' => $this->customerLoggerMock
                 ]
             );
         }
    @@ -218,6 +234,8 @@ public function testIsLoggedIn(): void
         }
     
         /**
    +     * @param $customerId
    +     * @param $key
          * @return void
          * @dataProvider getParametersDataProvider
          */
    @@ -271,7 +289,8 @@ public function getParametersDataProvider(): array
          * @param $key
          * @param $vatValidationEnabled
          * @param $addressType
    -     * @param Phrase $successMessage
    +     * @param $lastLoginAt
    +     * @param $successMessage
          *
          * @return void
          * @dataProvider getSuccessMessageDataProvider
    @@ -282,7 +301,8 @@ public function testSuccessMessage(
             $key,
             $vatValidationEnabled,
             $addressType,
    -        Phrase $successMessage
    +        $lastLoginAt,
    +        $successMessage
         ): void {
             $this->customerSessionMock->expects($this->once())
                 ->method('isLoggedIn')
    @@ -292,7 +312,7 @@ public function testSuccessMessage(
                 ->method('getParam')
                 ->willReturnMap(
                     [
    -                    ['id', false, $customerId],
    +                    ['id', 0, $customerId],
                         ['key', false, $key]
                     ]
                 );
    @@ -333,6 +353,14 @@ public function testSuccessMessage(
                     ['*/*/admin', ['_secure' => true], 'http://store.web/back']
                 ]);
     
    +        $this->logMock->expects($vatValidationEnabled ? $this->never() : $this->once())
    +            ->method('getLastLoginAt')
    +            ->willReturn($lastLoginAt);
    +        $this->customerLoggerMock->expects($vatValidationEnabled ? $this->never() : $this->once())
    +            ->method('get')
    +            ->with(1)
    +            ->willReturn($this->logMock);
    +
             $this->addressHelperMock->expects($this->once())
                 ->method('isVatValidationEnabled')
                 ->willReturn($vatValidationEnabled);
    @@ -388,12 +416,14 @@ public function testSuccessMessage(
         public function getSuccessMessageDataProvider(): array
         {
             return [
    -            [1, 1, false, null, __('Thank you for registering with %1.', 'frontend')],
    +            [1, 1, false, null, 'some-datetime', null],
    +            [1, 1, false, null, null, __('Thank you for registering with %1.', 'frontend')],
                 [
                     1,
                     1,
                     true,
                     Address::TYPE_BILLING,
    +                null,
                     __(
                         'If you are a registered VAT customer, please click <a href="%1">here</a>'
                         . ' to enter your billing address for proper VAT calculation.',
    @@ -405,12 +435,13 @@ public function getSuccessMessageDataProvider(): array
                     1,
                     true,
                     Address::TYPE_SHIPPING,
    +                null,
                     __(
                         'If you are a registered VAT customer, please click <a href="%1">here</a>'
                         . ' to enter your shipping address for proper VAT calculation.',
                         'http://store.web/customer/address/edit'
                     )
    -            ]
    +            ],
             ];
         }
     
    @@ -421,7 +452,8 @@ public function getSuccessMessageDataProvider(): array
          * @param $successUrl
          * @param $resultUrl
          * @param $isSetFlag
    -     * @param Phrase $successMessage
    +     * @param $successMessage
    +     * @param $lastLoginAt
          *
          * @return void
          * @dataProvider getSuccessRedirectDataProvider
    @@ -433,7 +465,8 @@ public function testSuccessRedirect(
             $successUrl,
             $resultUrl,
             $isSetFlag,
    -        Phrase $successMessage
    +        $lastLoginAt,
    +        $successMessage
         ): void {
             $this->customerSessionMock->expects($this->once())
                 ->method('isLoggedIn')
    @@ -443,7 +476,7 @@ public function testSuccessRedirect(
                 ->method('getParam')
                 ->willReturnMap(
                     [
    -                    ['id', false, $customerId],
    +                    ['id', 0, $customerId],
                         ['key', false, $key],
                         ['back_url', false, $backUrl]
                     ]
    @@ -469,23 +502,28 @@ public function testSuccessRedirect(
                 ->with($this->customerDataMock)
                 ->willReturnSelf();
     
    -        $this->messageManagerMock
    -            ->method('addSuccess')
    +        $this->messageManagerMock->method('addSuccess')
                 ->with($successMessage)
                 ->willReturnSelf();
     
    -        $this->messageManagerMock
    -            ->expects($this->never())
    +        $this->messageManagerMock->expects($this->never())
                 ->method('addException');
     
    -        $this->urlMock
    -            ->method('getUrl')
    +        $this->urlMock->method('getUrl')
                 ->willReturnMap([
                     ['customer/address/edit', null, 'http://store.web/customer/address/edit'],
                     ['*/*/admin', ['_secure' => true], 'http://store.web/back'],
                     ['*/*/index', ['_secure' => true], $successUrl]
                 ]);
     
    +        $this->logMock->expects($this->once())
    +            ->method('getLastLoginAt')
    +            ->willReturn($lastLoginAt);
    +        $this->customerLoggerMock->expects($this->once())
    +            ->method('get')
    +            ->with(1)
    +            ->willReturn($this->logMock);
    +
             $this->storeMock->expects($this->any())
                 ->method('getFrontendName')
                 ->willReturn('frontend');
    @@ -500,10 +538,7 @@ public function testSuccessRedirect(
     
             $this->scopeConfigMock->expects($this->any())
                 ->method('isSetFlag')
    -            ->with(
    -                Url::XML_PATH_CUSTOMER_STARTUP_REDIRECT_TO_DASHBOARD,
    -                ScopeInterface::SCOPE_STORE
    -            )
    +            ->with(Url::XML_PATH_CUSTOMER_STARTUP_REDIRECT_TO_DASHBOARD, ScopeInterface::SCOPE_STORE)
                 ->willReturn($isSetFlag);
     
             $cookieMetadataManager = $this->getMockBuilder(PhpCookieManager::class)
    @@ -535,6 +570,7 @@ public function getSuccessRedirectDataProvider(): array
                     null,
                     'http://example.com/back',
                     true,
    +                null,
                     __('Thank you for registering with %1.', 'frontend'),
                 ],
                 [
    @@ -544,6 +580,7 @@ public function getSuccessRedirectDataProvider(): array
                     'http://example.com/success',
                     'http://example.com/success',
                     true,
    +                null,
                     __('Thank you for registering with %1.', 'frontend'),
                 ],
                 [
    @@ -553,7 +590,18 @@ public function getSuccessRedirectDataProvider(): array
                     'http://example.com/success',
                     'http://example.com/success',
                     false,
    +                null,
                     __('Thank you for registering with %1.', 'frontend'),
    +            ],
    +            [
    +                1,
    +                1,
    +                null,
    +                'http://example.com/success',
    +                'http://example.com/success',
    +                false,
    +                'some data',
    +                null,
                 ]
             ];
         }
    
  • app/code/Magento/Customer/Test/Unit/Model/Plugin/CustomerNotificationTest.php+24 1 modified
    @@ -20,7 +20,13 @@
     use PHPUnit\Framework\MockObject\MockObject;
     use PHPUnit\Framework\TestCase;
     use Psr\Log\LoggerInterface;
    +use Magento\Framework\Session\StorageInterface;
     
    +/**
    + * Unit test for CustomerNotification plugin
    + *
    + * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
    + */
     class CustomerNotificationTest extends TestCase
     {
         private const STUB_CUSTOMER_ID = 1;
    @@ -65,6 +71,11 @@ class CustomerNotificationTest extends TestCase
          */
         private $plugin;
     
    +    /**
    +     * @var StorageInterface|MockObject
    +     */
    +    private $storage;
    +
         protected function setUp(): void
         {
             $this->sessionMock = $this->createMock(Session::class);
    @@ -87,19 +98,27 @@ protected function setUp(): void
                 ->with(NotificationStorage::UPDATE_CUSTOMER_SESSION, self::STUB_CUSTOMER_ID)
                 ->willReturn(true);
     
    +        $this->storage = $this
    +            ->getMockBuilder(StorageInterface::class)
    +            ->addMethods(['getData', 'setData'])
    +            ->disableOriginalConstructor()
    +            ->getMockForAbstractClass();
    +
             $this->plugin = new CustomerNotification(
                 $this->sessionMock,
                 $this->notificationStorageMock,
                 $this->appStateMock,
                 $this->customerRepositoryMock,
                 $this->loggerMock,
    -            $this->requestMock
    +            $this->requestMock,
    +            $this->storage
             );
         }
     
         public function testBeforeExecute()
         {
             $customerGroupId = 1;
    +        $testSessionId = [uniqid()];
     
             $customerMock = $this->getMockForAbstractClass(CustomerInterface::class);
             $customerMock->method('getGroupId')->willReturn($customerGroupId);
    @@ -116,6 +135,10 @@ public function testBeforeExecute()
             $this->sessionMock->expects($this->once())->method('setCustomerData')->with($customerMock);
             $this->sessionMock->expects($this->once())->method('setCustomerGroupId')->with($customerGroupId);
             $this->sessionMock->expects($this->once())->method('regenerateId');
    +        $this->storage->expects($this->once())->method('getData')->willReturn($testSessionId);
    +        $this->storage
    +            ->expects($this->once())
    +            ->method('setData');
     
             $this->plugin->beforeExecute($this->actionMock);
         }
    
  • app/code/Magento/Deploy/composer.json+12 10 modified
    @@ -1,22 +1,23 @@
     {
         "name": "magento/module-deploy",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.5",
         "require": {
             "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    -        "magento/module-config": "*",
    -        "magento/module-require-js": "*",
    -        "magento/module-store": "*",
    -        "magento/module-user": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-config": "101.2.*",
    +        "magento/module-require-js": "100.4.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/module-user": "101.2.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "cli_commands.php",
    @@ -27,3 +28,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Developer/composer.json+10 8 modified
    @@ -1,20 +1,21 @@
     {
         "name": "magento/module-developer",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.5",
         "require": {
             "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    -        "magento/module-config": "*",
    -        "magento/module-store": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-config": "101.2.*",
    +        "magento/module-store": "101.1.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -24,3 +25,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Dhl/composer.json+18 16 modified
    @@ -1,31 +1,32 @@
     {
         "name": "magento/module-dhl",
         "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",
             "lib-libxml": "*",
    -        "magento/framework": "*",
    -        "magento/module-backend": "*",
    -        "magento/module-catalog": "*",
    -        "magento/module-catalog-inventory": "*",
    -        "magento/module-config": "*",
    -        "magento/module-directory": "*",
    -        "magento/module-quote": "*",
    -        "magento/module-sales": "*",
    -        "magento/module-shipping": "*",
    -        "magento/module-store": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-catalog-inventory": "100.4.*",
    +        "magento/module-config": "101.2.*",
    +        "magento/module-directory": "100.4.*",
    +        "magento/module-quote": "101.2.*",
    +        "magento/module-sales": "103.0.*",
    +        "magento/module-shipping": "100.4.*",
    +        "magento/module-store": "101.1.*"
         },
         "suggest": {
    -        "magento/module-checkout": "*"
    +        "magento/module-checkout": "100.4.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -35,3 +36,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Directory/composer.json+11 9 modified
    @@ -1,22 +1,23 @@
     {
         "name": "magento/module-directory",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.5-p3",
         "require": {
             "php": "~7.4.0||~8.1.0",
             "lib-libxml": "*",
    -        "magento/framework": "*",
    -        "magento/module-backend": "*",
    -        "magento/module-config": "*",
    -        "magento/module-store": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-config": "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 @@
             }
         }
     }
    +
    
  • app/code/Magento/DirectoryGraphQl/composer.json+9 7 modified
    @@ -2,17 +2,18 @@
         "name": "magento/module-directory-graph-ql",
         "description": "N/A",
         "type": "magento2-module",
    -    "require": {
    -        "php": "~7.4.0||~8.1.0",
    -        "magento/module-directory": "*",
    -        "magento/module-store": "*",
    -        "magento/module-graph-ql": "*",
    -        "magento/framework": "*"
    -    },
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "100.4.3",
    +    "require": {
    +        "php": "~7.4.0||~8.1.0",
    +        "magento/module-directory": "100.4.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/module-graph-ql": "100.4.*",
    +        "magento/framework": "103.0.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -22,3 +23,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Directory/Model/Currency.php+9 3 modified
    @@ -6,14 +6,14 @@
     
     namespace Magento\Directory\Model;
     
    +use Magento\Directory\Model\Currency\Filter;
     use Magento\Framework\App\ObjectManager;
     use Magento\Framework\Exception\InputException;
    -use Magento\Directory\Model\Currency\Filter;
    +use Magento\Framework\Exception\LocalizedException;
     use Magento\Framework\Locale\Currency as LocaleCurrency;
     use Magento\Framework\Locale\ResolverInterface as LocalResolverInterface;
     use Magento\Framework\NumberFormatterFactory;
     use Magento\Framework\Serialize\Serializer\Json;
    -use Magento\Framework\Exception\LocalizedException;
     
     /**
      * Currency model
    @@ -442,6 +442,12 @@ private function formatCurrency(string $price, array $options): string
                 $formattedCurrency = str_replace(' ', '', $formattedCurrency);
             }
     
    +        // Sanitize data for Arabic currency
    +        if (str_contains($this->localeResolver->getLocale(), 'ar_') &&
    +            preg_match('/[\p{Arabic}]/u', $formattedCurrency) > 0) {
    +            $formattedCurrency = ltrim($formattedCurrency, '‏');
    +        }
    +
             return preg_replace('/^\s+|\s+$/u', '', $formattedCurrency);
         }
     
    @@ -591,7 +597,7 @@ public function saveRates($rates)
         private function trimUnicodeDirectionMark($string)
         {
             if (preg_match('/^(\x{200E}|\x{200F})/u', $string, $match)) {
    -            $string = preg_replace('/^'.$match[1].'/u', '', $string);
    +            $string = preg_replace('/^' . $match[1] . '/u', '', $string);
             }
             return $string;
         }
    
  • app/code/Magento/Directory/Test/Unit/Model/CurrencyTest.php+5 5 modified
    @@ -171,7 +171,7 @@ public function testFormatTxtWithNumberFormatter(
             array $options,
             string $expected
         ): void {
    -        $this->localeResolver->expects(self::once())
    +        $this->localeResolver->expects(self::atLeastOnce())
                 ->method('getLocale')
                 ->willReturn($locale);
             $this->numberFormatterFactory
    @@ -203,23 +203,23 @@ public function getFormatTxtNumberFormatterDataProvider(): array
                 ['en_US', 'USD', '9999', [], '$9,999.00'],
                 ['en_US', 'EUR', '9999', [], '€9,999.00'],
                 ['en_US', 'LBP', '9999', [], "LBP\u{00A0}9,999"],
    -            ['ar_AE', 'USD', '9', [], "\u{0669}\u{066B}\u{0660}\u{0660}\u{00A0}US$"],
    -            ['ar_AE', 'AED', '9', [], "\u{0669}\u{066B}\u{0660}\u{0660}\u{00A0}\u{062F}.\u{0625}.\u{200F}"],
    +            ['ar_SA', 'USD', '9', [], "\u{0669}\u{066B}\u{0660}\u{0660}\u{00A0}US$"],
    +            ['ar_SA', 'AED', '9', [], "\u{0669}\u{066B}\u{0660}\u{0660}\u{00A0}\u{062F}.\u{0625}.\u{200F}"],
                 ['de_DE', 'USD', '9999', [], "9.999,00\u{00A0}$"],
                 ['de_DE', 'EUR', '9999', [], "9.999,00\u{00A0}€"],
                 ['en_US', 'USD', '9999', ['display' => Currency::NO_SYMBOL, 'precision' => 2], '9,999.00'],
                 ['en_US', 'USD', '9999', ['display' => Currency::NO_SYMBOL], '9,999.00'],
                 ['en_US', 'PLN', '9999', ['display' => Currency::NO_SYMBOL], '9,999.00'],
                 ['en_US', 'LBP', '9999', ['display' => Currency::NO_SYMBOL], '9,999'],
                 [
    -                'ar_AE',
    +                'ar_SA',
                     'USD',
                     '9999',
                     ['display' => Currency::NO_SYMBOL],
                     "\u{0669}\u{066C}\u{0669}\u{0669}\u{0669}\u{066B}\u{0660}\u{0660}"
                 ],
                 [
    -                'ar_AE',
    +                'ar_SA',
                     'AED',
                     '9999',
                     ['display' => Currency::NO_SYMBOL],
    
  • app/code/Magento/Downloadable/composer.json+25 23 modified
    @@ -1,37 +1,38 @@
     {
         "name": "magento/module-downloadable",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.5",
         "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-config": "*",
    -        "magento/module-customer": "*",
    -        "magento/module-directory": "*",
    -        "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-theme": "*",
    -        "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-config": "101.2.*",
    +        "magento/module-customer": "103.0.*",
    +        "magento/module-directory": "100.4.*",
    +        "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-theme": "101.1.*",
    +        "magento/module-ui": "101.2.*"
         },
         "suggest": {
    -        "magento/module-downloadable-sample-data": "*"
    +        "magento/module-downloadable-sample-data": "Sample Data version: 100.4.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -41,3 +42,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/DownloadableGraphQl/composer.json+15 13 modified
    @@ -2,24 +2,25 @@
         "name": "magento/module-downloadable-graph-ql",
         "description": "N/A",
         "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
    +    "version": "100.4.5",
         "require": {
             "php": "~7.4.0||~8.1.0",
    -        "magento/module-store": "*",
    -        "magento/module-catalog": "*",
    -        "magento/module-downloadable": "*",
    -        "magento/module-quote": "*",
    -        "magento/module-sales": "*",
    -        "magento/module-quote-graph-ql": "*",
    -        "magento/framework": "*"
    +        "magento/module-store": "101.1.*",
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-downloadable": "100.4.*",
    +        "magento/module-quote": "101.2.*",
    +        "magento/module-sales": "103.0.*",
    +        "magento/module-quote-graph-ql": "100.4.*",
    +        "magento/framework": "103.0.*"
         },
         "suggest": {
    -        "magento/module-catalog-graph-ql": "*",
    -        "magento/module-sales-graph-ql": "*"
    +        "magento/module-catalog-graph-ql": "100.4.*",
    +        "magento/module-sales-graph-ql": "100.4.*"
         },
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -29,3 +30,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/DownloadableImportExport/composer.json+14 12 modified
    @@ -1,24 +1,25 @@
     {
         "name": "magento/module-downloadable-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-downloadable": "*",
    -        "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-downloadable": "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 @@
             }
         }
     }
    +
    
  • app/code/Magento/Eav/composer.json+13 11 modified
    @@ -1,23 +1,24 @@
     {
         "name": "magento/module-eav",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "102.1.5",
         "require": {
             "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    -        "magento/module-backend": "*",
    -        "magento/module-catalog": "*",
    -        "magento/module-config": "*",
    -        "magento/module-media-storage": "*",
    -        "magento/module-store": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-config": "101.2.*",
    +        "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"
    @@ -27,3 +28,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/EavGraphQl/composer.json+9 7 modified
    @@ -2,18 +2,19 @@
         "name": "magento/module-eav-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-eav": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-eav": "102.1.*"
         },
         "suggest": {
    -        "magento/module-graph-ql": "*"
    +        "magento/module-graph-ql": "100.4.*"
         },
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -23,3 +24,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Elasticsearch6/composer.json+13 11 modified
    @@ -1,23 +1,24 @@
     {
         "name": "magento/module-elasticsearch-6",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
    +    "version": "100.4.5",
         "require": {
             "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    -        "magento/module-advanced-search": "*",
    -        "magento/module-catalog-search": "*",
    -        "magento/module-search": "*",
    -        "magento/module-elasticsearch": "*",
    +        "magento/framework": "103.0.*",
    +        "magento/module-advanced-search": "100.4.*",
    +        "magento/module-catalog-search": "102.0.*",
    +        "magento/module-search": "101.1.*",
    +        "magento/module-elasticsearch": "101.0.*",
             "elasticsearch/elasticsearch": "~7.17.0"
         },
         "suggest": {
    -        "magento/module-config": "*"
    +        "magento/module-config": "101.2.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -27,3 +28,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Elasticsearch7/composer.json+13 11 modified
    @@ -1,23 +1,24 @@
     {
         "name": "magento/module-elasticsearch-7",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
    +    "version": "100.4.5",
         "require": {
             "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    -        "magento/module-elasticsearch": "*",
    +        "magento/framework": "103.0.*",
    +        "magento/module-elasticsearch": "101.0.*",
             "elasticsearch/elasticsearch": "~7.17.0",
    -        "magento/module-advanced-search": "*",
    -        "magento/module-catalog-search": "*"
    +        "magento/module-advanced-search": "100.4.*",
    +        "magento/module-catalog-search": "102.0.*"
         },
         "suggest": {
    -        "magento/module-config": "*",
    -        "magento/module-search": "*"
    +        "magento/module-config": "101.2.*",
    +        "magento/module-search": "101.1.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -27,3 +28,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Elasticsearch/composer.json+17 15 modified
    @@ -1,27 +1,28 @@
     {
         "name": "magento/module-elasticsearch",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
    +    "version": "101.0.5",
         "require": {
             "php": "~7.4.0||~8.1.0",
    -        "magento/module-advanced-search": "*",
    -        "magento/module-catalog": "*",
    -        "magento/module-catalog-search": "*",
    -        "magento/module-customer": "*",
    -        "magento/module-eav": "*",
    -        "magento/module-search": "*",
    -        "magento/module-store": "*",
    -        "magento/module-catalog-inventory": "*",
    -        "magento/framework": "*",
    +        "magento/module-advanced-search": "100.4.*",
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-catalog-search": "102.0.*",
    +        "magento/module-customer": "103.0.*",
    +        "magento/module-eav": "102.1.*",
    +        "magento/module-search": "101.1.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/module-catalog-inventory": "100.4.*",
    +        "magento/framework": "103.0.*",
             "elasticsearch/elasticsearch": "~7.17.0"
         },
         "suggest": {
    -        "magento/module-config": "*"
    +        "magento/module-config": "101.2.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -31,3 +32,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Email/composer.json+18 16 modified
    @@ -1,30 +1,31 @@
     {
         "name": "magento/module-email",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "101.1.5-p6",
         "require": {
             "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    -        "magento/module-backend": "*",
    -        "magento/module-cms": "*",
    -        "magento/module-config": "*",
    -        "magento/module-store": "*",
    -        "magento/module-theme": "*",
    -        "magento/module-require-js": "*",
    -        "magento/module-media-storage": "*",
    -        "magento/module-variable": "*",
    -        "magento/module-ui": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-cms": "104.0.*",
    +        "magento/module-config": "101.2.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/module-theme": "101.1.*",
    +        "magento/module-require-js": "100.4.*",
    +        "magento/module-media-storage": "100.4.*",
    +        "magento/module-variable": "100.4.*",
    +        "magento/module-ui": "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"
    @@ -34,3 +35,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Email/Model/Template/Filter.php+6 0 modified
    @@ -69,12 +69,14 @@ class Filter extends Template
         /**
          * @var bool
          * @deprecated SID is not being used as query parameter anymore.
    +     * @see Session ID's in URL
          */
         protected $_useSessionInUrl = false;
     
         /**
          * @var array
          * @deprecated 101.0.4 Use the new Directive Processor interfaces
    +     * @see Directive Processor interfaces
          */
         protected $_modifiers = ['nl2br' => ''];
     
    @@ -281,6 +283,7 @@ public function setUseAbsoluteLinks($flag)
          * @return $this
          * @SuppressWarnings(PHPMD.UnusedFormalParameter)
          * @deprecated SID query parameter is not used in URLs anymore.
    +     * @see SessionId's in URL
          */
         public function setUseSessionInUrl($flag)
         {
    @@ -688,6 +691,7 @@ public function varDirective($construction)
          * @param string $default assumed modifier if none present
          * @return array
          * @deprecated 101.0.4 Use the new FilterApplier or Directive Processor interfaces
    +     * @see Directive Processor Interfaces
          */
         protected function explodeModifiers($value, $default = null)
         {
    @@ -707,6 +711,7 @@ protected function explodeModifiers($value, $default = null)
          * @param string $modifiers
          * @return string
          * @deprecated 101.0.4 Use the new FilterApplier or Directive Processor interfaces
    +     * @see Directive Processor Interfaces
          */
         protected function applyModifiers($value, $modifiers)
         {
    @@ -736,6 +741,7 @@ protected function applyModifiers($value, $modifiers)
          * @param string $type
          * @return string
          * @deprecated 101.0.4 Use the new FilterApplier or Directive Processor interfaces
    +     * @see Directive Processor Interfacees
          */
         public function modifierEscape($value, $type = 'html')
         {
    
  • app/code/Magento/EncryptionKey/composer.json+10 8 modified
    @@ -1,20 +1,21 @@
     {
         "name": "magento/module-encryption-key",
         "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-backend": "*",
    -        "magento/module-config": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-config": "101.2.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -24,3 +25,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Fedex/composer.json+16 14 modified
    @@ -1,27 +1,28 @@
     {
         "name": "magento/module-fedex",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.3-p3",
         "require": {
             "php": "~7.4.0||~8.1.0",
             "lib-libxml": "*",
    -        "magento/framework": "*",
    -        "magento/module-catalog": "*",
    -        "magento/module-catalog-inventory": "*",
    -        "magento/module-config": "*",
    -        "magento/module-directory": "*",
    -        "magento/module-quote": "*",
    -        "magento/module-sales": "*",
    -        "magento/module-shipping": "*",
    -        "magento/module-store": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-catalog-inventory": "100.4.*",
    +        "magento/module-config": "101.2.*",
    +        "magento/module-directory": "100.4.*",
    +        "magento/module-quote": "101.2.*",
    +        "magento/module-sales": "103.0.*",
    +        "magento/module-shipping": "100.4.*",
    +        "magento/module-store": "101.1.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -31,3 +32,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Fedex/etc/adminhtml/system.xml+2 0 modified
    @@ -40,12 +40,14 @@
                     </field>
                     <field id="production_webservices_url" translate="label" type="text" sortOrder="90" showInDefault="1" showInWebsite="1" canRestore="1">
                         <label>Web-Services URL (Production)</label>
    +                    <backend_model>Magento\Fedex\Model\Config\Backend\FedexUrl</backend_model>
                         <depends>
                             <field id="sandbox_mode">0</field>
                         </depends>
                     </field>
                     <field id="sandbox_webservices_url" translate="label" type="text" sortOrder="100" showInDefault="1" showInWebsite="1" canRestore="1">
                         <label>Web-Services URL (Sandbox)</label>
    +                    <backend_model>Magento\Fedex\Model\Config\Backend\FedexUrl</backend_model>
                         <depends>
                             <field id="sandbox_mode">1</field>
                         </depends>
    
  • app/code/Magento/Fedex/i18n/en_US.csv+1 0 modified
    @@ -78,3 +78,4 @@ Debug,Debug
     "Show Method if Not Applicable","Show Method if Not Applicable"
     "Sort Order","Sort Order"
     "Can't convert a shipping cost from ""%1-%2"" for FedEx carrier.","Can't convert a shipping cost from ""%1-%2"" for FedEx carrier."
    +"Fedex API endpoint URL\'s must use fedex.com","Fedex API endpoint URL\'s must use fedex.com"
    
  • app/code/Magento/Fedex/Model/Config/Backend/FedexUrl.php+75 0 added
    @@ -0,0 +1,75 @@
    +<?php
    +/**
    + * Copyright © Magento, Inc. All rights reserved.
    + * See COPYING.txt for license details.
    + */
    +
    +declare(strict_types=1);
    +
    +namespace Magento\Fedex\Model\Config\Backend;
    +
    +use Magento\Framework\App\Cache\TypeListInterface;
    +use Magento\Framework\App\Config\ScopeConfigInterface;
    +use Magento\Framework\App\Config\Value;
    +use Magento\Framework\Data\Collection\AbstractDb;
    +use Magento\Framework\Exception\ValidatorException;
    +use Magento\Framework\Model\Context;
    +use Magento\Framework\Model\ResourceModel\AbstractResource;
    +use Magento\Framework\Registry;
    +use Magento\Framework\Validator\Url;
    +use Magento\Framework\Model\AbstractModel;
    +
    +/**
    + * Represents a config URL that may point to a Fedex endpoint
    + */
    +class FedexUrl extends Value
    +{
    +    /**
    +     * @var Url
    +     */
    +    private Url $url;
    +
    +    /**
    +     * @param Url $url
    +     * @param Context $context
    +     * @param Registry $registry
    +     * @param ScopeConfigInterface $config
    +     * @param TypeListInterface $cacheTypeList
    +     * @param AbstractResource|null $resource
    +     * @param AbstractDb|null $resourceCollection
    +     * @param array $data
    +     */
    +    public function __construct(
    +        Url $url,
    +        Context $context,
    +        Registry $registry,
    +        ScopeConfigInterface $config,
    +        TypeListInterface $cacheTypeList,
    +        AbstractResource $resource = null,
    +        AbstractDb $resourceCollection = null,
    +        array $data = []
    +    ) {
    +        $this->url = $url;
    +        parent::__construct($context, $registry, $config, $cacheTypeList, $resource, $resourceCollection, $data);
    +    }
    +
    +    /**
    +     * @inheritdoc
    +     *
    +     * @throws ValidatorException
    +     */
    +    public function beforeSave(): AbstractModel
    +    {
    +        $isValid = $this->url->isValid($this->getValue());
    +        if ($isValid) {
    +            // phpcs:ignore Magento2.Functions.DiscouragedFunction
    +            $host = parse_url((string)$this->getValue(), \PHP_URL_HOST);
    +
    +            if (!empty($host) && !preg_match('/(?:.+\.|^)fedex\.com$/i', $host)) {
    +                throw new ValidatorException(__('Fedex API endpoint URL\'s must use fedex.com'));
    +            }
    +        }
    +
    +        return parent::beforeSave();
    +    }
    +}
    
  • app/code/Magento/Fedex/Test/Unit/Model/Config/Backend/FedexUrlTest.php+131 0 added
    @@ -0,0 +1,131 @@
    +<?php
    +/**
    + * Copyright © Magento, Inc. All rights reserved.
    + * See COPYING.txt for license details.
    + */
    +
    +declare(strict_types=1);
    +
    +namespace Magento\Fedex\Test\Unit\Model\Config\Backend;
    +
    +use Magento\Fedex\Model\Config\Backend\FedexUrl;
    +use Magento\Framework\App\Cache\TypeListInterface;
    +use Magento\Framework\App\Config\ScopeConfigInterface;
    +use Magento\Framework\Event\ManagerInterface;
    +use Magento\Framework\Exception\ValidatorException;
    +use Magento\Framework\Registry;
    +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
    +use Magento\Rule\Model\ResourceModel\AbstractResource;
    +use Magento\Framework\Data\Collection\AbstractDb;
    +use PHPUnit\Framework\MockObject\MockObject;
    +use PHPUnit\Framework\TestCase;
    +use Magento\Framework\Validator\Url;
    +use Magento\Framework\Model\Context;
    +
    +/**
    + * Verify behavior of FedexUrl backend type
    + */
    +class FedexUrlTest extends TestCase
    +{
    +
    +    /**
    +     * @var FedexUrl
    +     */
    +    private $urlConfig;
    +
    +    /**
    +     * @var Url
    +     */
    +    private $url;
    +
    +    /**
    +     * @var Context|MockObject
    +     */
    +    private $contextMock;
    +
    +    protected function setUp(): void
    +    {
    +        $objectManager = new ObjectManager($this);
    +        $this->contextMock = $this->createMock(Context::class);
    +        $registry = $this->createMock(Registry::class);
    +        $config = $this->createMock(ScopeConfigInterface::class);
    +        $cacheTypeList = $this->createMock(TypeListInterface::class);
    +        $this->url = $this->createMock(Url::class);
    +        $resource = $this->createMock(AbstractResource::class);
    +        $resourceCollection = $this->createMock(AbstractDb::class);
    +        $eventManagerMock = $this->getMockForAbstractClass(ManagerInterface::class);
    +        $eventManagerMock->expects($this->any())->method('dispatch');
    +        $this->contextMock->expects($this->any())->method('getEventDispatcher')->willReturn($eventManagerMock);
    +
    +        $this->urlConfig = $objectManager->getObject(
    +            FedexUrl::class,
    +            [
    +                'url' => $this->url,
    +                'context' => $this->contextMock,
    +                'registry' => $registry,
    +                'config' => $config,
    +                'cacheTypeList' => $cacheTypeList,
    +                'resource' => $resource,
    +                'resourceCollection' => $resourceCollection,
    +            ]
    +        );
    +    }
    +
    +    /**
    +     * @dataProvider validDataProvider
    +     * @param string|null $data The valid data
    +     * @throws ValidatorException
    +     */
    +    public function testBeforeSave(string $data = null): void
    +    {
    +        $this->url->expects($this->any())->method('isValid')->willReturn(true);
    +        $this->urlConfig->setValue($data);
    +        $this->urlConfig->beforeSave();
    +        $this->assertTrue($this->url->isValid($data));
    +    }
    +
    +    /**
    +     * @dataProvider invalidDataProvider
    +     * @param string $data The invalid data
    +     */
    +    public function testBeforeSaveErrors(string $data): void
    +    {
    +        $this->url->expects($this->any())->method('isValid')->willReturn(true);
    +        $this->expectException('Magento\Framework\Exception\ValidatorException');
    +        $this->expectExceptionMessage('Fedex API endpoint URL\'s must use fedex.com');
    +        $this->urlConfig->setValue($data);
    +        $this->urlConfig->beforeSave();
    +    }
    +
    +    /**
    +     * Validator Data Provider
    +     *
    +     * @return array
    +     */
    +    public function validDataProvider(): array
    +    {
    +        return [
    +            [],
    +            [null],
    +            [''],
    +            ['http://fedex.com'],
    +            ['https://foo.fedex.com'],
    +            ['http://foo.fedex.com/foo/bar?baz=bash&fizz=buzz'],
    +        ];
    +    }
    +
    +    /**
    +     * @return string[][]
    +     */
    +    public function invalidDataProvider(): array
    +    {
    +        return [
    +            ['http://fedexfoo.com'],
    +            ['https://foofedex.com'],
    +            ['https://fedex.com.fake.com'],
    +            ['https://fedex.info'],
    +            ['http://fedex.com.foo.com/foo/bar?baz=bash&fizz=buzz'],
    +            ['http://foofedex.com/foo/bar?baz=bash&fizz=buzz'],
    +        ];
    +    }
    +}
    
  • app/code/Magento/GiftMessage/composer.json+18 16 modified
    @@ -1,30 +1,31 @@
     {
         "name": "magento/module-gift-message",
         "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-checkout": "*",
    -        "magento/module-customer": "*",
    -        "magento/module-quote": "*",
    -        "magento/module-sales": "*",
    -        "magento/module-store": "*",
    -        "magento/module-ui": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-checkout": "100.4.*",
    +        "magento/module-customer": "103.0.*",
    +        "magento/module-quote": "101.2.*",
    +        "magento/module-sales": "103.0.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/module-ui": "101.2.*"
         },
         "suggest": {
    -        "magento/module-eav": "*",
    -        "magento/module-multishipping": "*"
    +        "magento/module-eav": "102.1.*",
    +        "magento/module-multishipping": "100.4.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -34,3 +35,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/GiftMessageGraphQl/composer.json+9 7 modified
    @@ -2,18 +2,19 @@
         "name": "magento/module-gift-message-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/framework": "*",
    -        "magento/module-gift-message": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-gift-message": "100.4.*"
         },
         "suggest": {
    -        "magento/module-graph-ql": "*"
    +        "magento/module-graph-ql": "100.4.*"
         },
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -23,3 +24,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/GoogleAdwords/composer.json+10 8 modified
    @@ -1,20 +1,21 @@
     {
         "name": "magento/module-google-adwords",
         "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-sales": "*",
    -        "magento/module-store": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-sales": "103.0.*",
    +        "magento/module-store": "101.1.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -24,3 +25,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/GoogleAnalytics/composer.json+12 10 modified
    @@ -1,24 +1,25 @@
     {
         "name": "magento/module-google-analytics",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.1",
         "require": {
             "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    -        "magento/module-cookie": "*",
    -        "magento/module-sales": "*",
    -        "magento/module-store": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-cookie": "100.4.*",
    +        "magento/module-sales": "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"
    @@ -28,3 +29,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/GoogleGtag/composer.json+12 10 modified
    @@ -1,24 +1,25 @@
     {
         "name": "magento/module-google-gtag",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.0",
         "require": {
             "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    -        "magento/module-cookie": "*",
    -        "magento/module-sales": "*",
    -        "magento/module-store": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-cookie": "100.4.*",
    +        "magento/module-sales": "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"
    @@ -28,3 +29,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/GoogleOptimizer/composer.json+15 13 modified
    @@ -1,25 +1,26 @@
     {
         "name": "magento/module-google-optimizer",
         "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-cms": "*",
    -        "magento/module-google-analytics": "*",
    -        "magento/module-google-gtag": "*",
    -        "magento/module-store": "*",
    -        "magento/module-ui": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-cms": "104.0.*",
    +        "magento/module-google-analytics": "100.4.*",
    +        "magento/module-google-gtag": "100.4.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/module-ui": "101.2.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -29,3 +30,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/GraphQlCache/composer.json+10 8 modified
    @@ -2,18 +2,19 @@
         "name": "magento/module-graph-ql-cache",
         "description": "N/A",
         "type": "magento2-module",
    -    "require": {
    -        "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    -        "magento/module-page-cache": "*",
    -        "magento/module-graph-ql": "*",
    -        "magento/module-authorization": "*",
    -        "magento/module-integration": "*"
    -    },
         "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-page-cache": "100.4.*",
    +        "magento/module-graph-ql": "100.4.*",
    +        "magento/module-authorization": "100.4.*",
    +        "magento/module-integration": "100.4.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -23,3 +24,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/GraphQl/composer.json+12 10 modified
    @@ -2,22 +2,23 @@
         "name": "magento/module-graph-ql",
         "description": "N/A",
         "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
    +    "version": "100.4.5",
         "require": {
             "php": "~7.4.0||~8.1.0",
    -        "magento/module-eav": "*",
    -        "magento/framework": "*",
    -        "magento/module-webapi": "*",
    -        "magento/module-new-relic-reporting": "*",
    -        "magento/module-authorization": "*",
    +        "magento/module-eav": "102.1.*",
    +        "magento/framework": "103.0.*",
    +        "magento/module-webapi": "100.4.*",
    +        "magento/module-new-relic-reporting": "100.4.*",
    +        "magento/module-authorization": "100.4.*",
             "webonyx/graphql-php": "~14.11.5"
         },
         "suggest": {
    -        "magento/module-graph-ql-cache": "*"
    +        "magento/module-graph-ql-cache": "100.4.*"
         },
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -27,3 +28,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/GroupedCatalogInventory/composer.json+11 9 modified
    @@ -1,21 +1,22 @@
     {
         "name": "magento/module-grouped-catalog-inventory",
         "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-inventory": "*",
    -        "magento/module-grouped-product": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-catalog-inventory": "100.4.*",
    +        "magento/module-grouped-product": "100.4.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -25,3 +26,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/GroupedImportExport/composer.json+13 11 modified
    @@ -1,23 +1,24 @@
     {
         "name": "magento/module-grouped-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-catalog": "*",
    -        "magento/module-catalog-import-export": "*",
    -        "magento/module-eav": "*",
    -        "magento/module-grouped-product": "*",
    -        "magento/module-import-export": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-catalog-import-export": "101.1.*",
    +        "magento/module-eav": "102.1.*",
    +        "magento/module-grouped-product": "100.4.*",
    +        "magento/module-import-export": "101.0.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -27,3 +28,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/GroupedProduct/composer.json+22 20 modified
    @@ -1,34 +1,35 @@
     {
         "name": "magento/module-grouped-product",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.5",
         "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-msrp": "*",
    -        "magento/module-quote": "*",
    -        "magento/module-sales": "*",
    -        "magento/module-store": "*",
    -        "magento/module-ui": "*",
    -        "magento/module-wishlist": "*"
    +        "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-msrp": "100.4.*",
    +        "magento/module-quote": "101.2.*",
    +        "magento/module-sales": "103.0.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/module-ui": "101.2.*",
    +        "magento/module-wishlist": "101.2.*"
         },
         "suggest": {
    -        "magento/module-grouped-product-sample-data": "*"
    +        "magento/module-grouped-product-sample-data": "Sample Data version: 100.4.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -38,3 +39,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/GroupedProductGraphQl/composer.json+9 7 modified
    @@ -2,17 +2,18 @@
         "name": "magento/module-grouped-product-graph-ql",
         "description": "N/A",
         "type": "magento2-module",
    -    "require": {
    -        "php": "~7.4.0||~8.1.0",
    -        "magento/module-grouped-product": "*",
    -        "magento/module-catalog": "*",
    -        "magento/module-catalog-graph-ql": "*",
    -        "magento/framework": "*"
    -    },
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "100.4.5",
    +    "require": {
    +        "php": "~7.4.0||~8.1.0",
    +        "magento/module-grouped-product": "100.4.*",
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-catalog-graph-ql": "100.4.*",
    +        "magento/framework": "103.0.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -22,3 +23,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/ImportExport/composer.json+14 12 modified
    @@ -1,25 +1,26 @@
     {
         "name": "magento/module-import-export",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "101.0.5",
         "require": {
             "php": "~7.4.0||~8.1.0",
             "ext-ctype": "*",
    -        "magento/framework": "*",
    -        "magento/module-backend": "*",
    -        "magento/module-catalog": "*",
    -        "magento/module-eav": "*",
    -        "magento/module-media-storage": "*",
    -        "magento/module-store": "*",
    -        "magento/module-ui": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-eav": "102.1.*",
    +        "magento/module-media-storage": "100.4.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/module-ui": "101.2.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -29,3 +30,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Indexer/composer.json+9 7 modified
    @@ -1,19 +1,20 @@
     {
         "name": "magento/module-indexer",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.5",
         "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 @@
             }
         }
     }
    +
    
  • app/code/Magento/InstantPurchase/composer.json+10 8 modified
    @@ -6,16 +6,17 @@
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "100.4.4",
         "require": {
             "php": "~7.4.0||~8.1.0",
    -        "magento/module-store": "*",
    -        "magento/module-catalog": "*",
    -        "magento/module-customer": "*",
    -        "magento/module-sales": "*",
    -        "magento/module-shipping": "*",
    -        "magento/module-quote": "*",
    -        "magento/module-vault": "*",
    -        "magento/framework": "*"
    +        "magento/module-store": "101.1.*",
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-customer": "103.0.*",
    +        "magento/module-sales": "103.0.*",
    +        "magento/module-shipping": "100.4.*",
    +        "magento/module-quote": "101.2.*",
    +        "magento/module-vault": "101.2.*",
    +        "magento/framework": "103.0.*"
         },
         "autoload": {
             "files": [
    @@ -26,3 +27,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Integration/composer.json+15 13 modified
    @@ -1,25 +1,26 @@
     {
         "name": "magento/module-integration",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.5",
         "require": {
             "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    -        "magento/module-authorization": "*",
    -        "magento/module-backend": "*",
    -        "magento/module-customer": "*",
    -        "magento/module-security": "*",
    -        "magento/module-store": "*",
    -        "magento/module-user": "*",
    -        "magento/module-ui": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-authorization": "100.4.*",
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-customer": "103.0.*",
    +        "magento/module-security": "100.4.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/module-user": "101.2.*",
    +        "magento/module-ui": "101.2.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -29,3 +30,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/JwtFrameworkAdapter/composer.json+8 6 modified
    @@ -1,19 +1,20 @@
     {
         "name": "magento/module-jwt-framework-adapter",
         "description": "JWT Manager implementation based on jwt-framework",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.1",
         "require": {
             "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    +        "magento/framework": "103.0.*",
             "web-token/jwt-framework": "^v2.2.7"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -23,3 +24,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/JwtUserToken/composer.json+10 8 modified
    @@ -1,20 +1,21 @@
     {
         "name": "magento/module-jwt-user-token",
         "description": "Introduces JWT token support for web API authentication",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.0",
         "require": {
             "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    -        "magento/module-integration": "*",
    -        "magento/module-authorization": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-integration": "100.4.*",
    +        "magento/module-authorization": "100.4.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -24,3 +25,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/LayeredNavigation/composer.json+10 8 modified
    @@ -1,20 +1,21 @@
     {
         "name": "magento/module-layered-navigation",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.5",
         "require": {
             "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    -        "magento/module-catalog": "*",
    -        "magento/module-config": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-config": "101.2.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -24,3 +25,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/LoginAsCustomerAdminUi/composer.json+15 14 modified
    @@ -1,24 +1,24 @@
     {
         "name": "magento/module-login-as-customer-admin-ui",
    -    "description": "",
    -    "require": {
    -        "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    -        "magento/module-login-as-customer-api": "*",
    -        "magento/module-login-as-customer-frontend-ui": "*",
    -        "magento/module-backend": "*",
    -        "magento/module-customer": "*",
    -        "magento/module-sales": "*",
    -        "magento/module-store": "*"
    -    },
    -    "suggest": {
    -        "magento/module-login-as-customer": "*"
    -    },
         "type": "magento2-module",
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "100.4.5",
    +    "require": {
    +        "php": "~7.4.0||~8.1.0",
    +        "magento/framework": "103.0.*",
    +        "magento/module-login-as-customer-api": "100.4.*",
    +        "magento/module-login-as-customer-frontend-ui": "100.4.*",
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-customer": "103.0.*",
    +        "magento/module-sales": "103.0.*",
    +        "magento/module-store": "101.1.*"
    +    },
    +    "suggest": {
    +        "magento/module-login-as-customer": "100.4.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -28,3 +28,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/LoginAsCustomerApi/composer.json+6 4 modified
    @@ -1,15 +1,16 @@
     {
         "name": "magento/module-login-as-customer-api",
         "description": "Allow for admin to enter a customer account",
    -    "require": {
    -        "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*"
    -    },
         "type": "magento2-module",
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "100.4.4",
    +    "require": {
    +        "php": "~7.4.0||~8.1.0",
    +        "magento/framework": "103.0.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -19,3 +20,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/LoginAsCustomerAssistance/composer.json+15 14 modified
    @@ -1,24 +1,24 @@
     {
         "name": "magento/module-login-as-customer-assistance",
    -    "description": "",
    -    "require": {
    -        "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    -        "magento/module-authorization": "*",
    -        "magento/module-backend": "*",
    -        "magento/module-customer": "*",
    -        "magento/module-store": "*",
    -        "magento/module-login-as-customer": "*",
    -        "magento/module-login-as-customer-api": "*"
    -    },
    -    "suggest": {
    -        "magento/module-login-as-customer-admin-ui": "*"
    -    },
         "type": "magento2-module",
         "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-authorization": "100.4.*",
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-customer": "103.0.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/module-login-as-customer": "100.4.*",
    +        "magento/module-login-as-customer-api": "100.4.*"
    +    },
    +    "suggest": {
    +        "magento/module-login-as-customer-admin-ui": "100.4.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -28,3 +28,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/LoginAsCustomer/composer.json+12 10 modified
    @@ -1,21 +1,22 @@
     {
         "name": "magento/module-login-as-customer",
         "description": "Allow for admin to enter a customer account",
    -    "require": {
    -        "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    -        "magento/module-backend": "*",
    -        "magento/module-customer": "*",
    -        "magento/module-login-as-customer-api": "*"
    -    },
    -    "suggest": {
    -        "magento/module-backend": "*"
    -    },
         "type": "magento2-module",
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "100.4.5",
    +    "require": {
    +        "php": "~7.4.0||~8.1.0",
    +        "magento/framework": "103.0.*",
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-customer": "103.0.*",
    +        "magento/module-login-as-customer-api": "100.4.*"
    +    },
    +    "suggest": {
    +        "magento/module-backend": "102.0.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -25,3 +26,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/LoginAsCustomerFrontendUi/composer.json+9 8 modified
    @@ -1,18 +1,18 @@
     {
         "name": "magento/module-login-as-customer-frontend-ui",
    -    "description": "",
    -    "require": {
    -        "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    -        "magento/module-login-as-customer-api": "*",
    -        "magento/module-customer": "*",
    -        "magento/module-store": "*"
    -    },
         "type": "magento2-module",
         "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-login-as-customer-api": "100.4.*",
    +        "magento/module-customer": "103.0.*",
    +        "magento/module-store": "101.1.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -22,3 +22,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/LoginAsCustomerGraphQl/composer.json+14 12 modified
    @@ -1,23 +1,24 @@
     {
         "name": "magento/module-login-as-customer-graph-ql",
         "description": "Flexible login as a customer so a merchant or merchant admin can log into an end customer's account to assist them with their account.",
    -    "require": {
    -        "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    -        "magento/module-login-as-customer-api": "*",
    -        "magento/module-login-as-customer-assistance": "*",
    -        "magento/module-integration": "*",
    -        "magento/module-store": "*",
    -        "magento/module-customer": "*"
    -    },
    -    "suggest": {
    -        "magento/module-login-as-customer": "*"
    -    },
         "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-login-as-customer-api": "100.4.*",
    +        "magento/module-login-as-customer-assistance": "100.4.*",
    +        "magento/module-integration": "100.4.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/module-customer": "103.0.*"
    +    },
    +    "suggest": {
    +        "magento/module-login-as-customer": "100.4.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -27,3 +28,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/LoginAsCustomerLog/composer.json+14 13 modified
    @@ -1,23 +1,23 @@
     {
         "name": "magento/module-login-as-customer-log",
    -    "description": "",
    -    "require": {
    -        "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    -        "magento/module-backend": "*",
    -        "magento/module-customer": "*",
    -        "magento/module-login-as-customer-api": "*",
    -        "magento/module-ui": "*",
    -        "magento/module-user": "*"
    -    },
    -    "suggest": {
    -        "magento/module-login-as-customer": "*"
    -    },
         "type": "magento2-module",
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "100.4.3",
    +    "require": {
    +        "php": "~7.4.0||~8.1.0",
    +        "magento/framework": "103.0.*",
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-customer": "103.0.*",
    +        "magento/module-login-as-customer-api": "100.4.*",
    +        "magento/module-ui": "101.2.*",
    +        "magento/module-user": "101.2.*"
    +    },
    +    "suggest": {
    +        "magento/module-login-as-customer": "100.4.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -27,3 +27,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/LoginAsCustomerPageCache/composer.json+11 10 modified
    @@ -1,20 +1,20 @@
     {
         "name": "magento/module-login-as-customer-page-cache",
    -    "description": "",
    -    "require": {
    -        "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    -        "magento/module-store": "*",
    -        "magento/module-login-as-customer-api": "*"
    -    },
    -    "suggest": {
    -        "magento/module-page-cache": "*"
    -    },
         "type": "magento2-module",
         "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-store": "101.1.*",
    +        "magento/module-login-as-customer-api": "100.4.*"
    +    },
    +    "suggest": {
    +        "magento/module-page-cache": "100.4.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -24,3 +24,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/LoginAsCustomerQuote/composer.json+12 11 modified
    @@ -1,21 +1,21 @@
     {
         "name": "magento/module-login-as-customer-quote",
    -    "description": "",
    -    "require": {
    -        "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    -        "magento/module-checkout": "*",
    -        "magento/module-customer": "*",
    -        "magento/module-quote": "*"
    -    },
    -    "suggest": {
    -        "magento/module-login-as-customer-api": "*"
    -    },
         "type": "magento2-module",
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "100.4.3",
    +    "require": {
    +        "php": "~7.4.0||~8.1.0",
    +        "magento/framework": "103.0.*",
    +        "magento/module-checkout": "100.4.*",
    +        "magento/module-customer": "103.0.*",
    +        "magento/module-quote": "101.2.*"
    +    },
    +    "suggest": {
    +        "magento/module-login-as-customer-api": "100.4.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -25,3 +25,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/LoginAsCustomerSales/composer.json+12 11 modified
    @@ -1,21 +1,21 @@
     {
         "name": "magento/module-login-as-customer-sales",
    -    "description": "",
    -    "require": {
    -        "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    -        "magento/module-backend": "*",
    -        "magento/module-user": "*",
    -        "magento/module-login-as-customer-api": "*"
    -    },
    -    "suggest": {
    -        "magento/module-sales": "*"
    -    },
         "type": "magento2-module",
         "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-backend": "102.0.*",
    +        "magento/module-user": "101.2.*",
    +        "magento/module-login-as-customer-api": "100.4.*"
    +    },
    +    "suggest": {
    +        "magento/module-sales": "103.0.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -25,3 +25,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Marketplace/composer.json+9 7 modified
    @@ -1,19 +1,20 @@
     {
         "name": "magento/module-marketplace",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.3-p4",
         "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 @@
             }
         }
     }
    +
    
  • app/code/Magento/Marketplace/view/adminhtml/templates/index.phtml+3 3 modified
    @@ -32,8 +32,8 @@
                     <h2 class="page-sub-title"><?= $block->escapeHtml(__('Partner search')) ?></h2>
                     <p>
                         <?= $block->escapeHtml(__(
    -                        'Magento has a thriving ecosystem of technology partners to help merchants and brands deliver ' .
    -                        'the best possible customer experiences. They are recognized as experts in eCommerce, ' .
    +                        'Magento has a thriving ecosystem of technology partners to help merchants and brands deliver '
    +                        . 'the best possible customer experiences. They are recognized as experts in eCommerce, ' .
                             'search, email marketing, payments, tax, fraud, optimization and analytics, fulfillment, ' .
                             'and more. Visit the Magento Partner Directory to see all of our trusted partners.'
                         )); ?>
    @@ -61,7 +61,7 @@
                     )); ?>
                 </p>
                 <a class="action-secondary" target="_blank"
    -               href="https://marketplace.magento.com/">
    +               href="https://commercemarketplace.adobe.com/">
                     <?= $block->escapeHtml(__('Visit Magento Marketplaces')) ?>
                 </a>
             </div>
    
  • app/code/Magento/MediaContentApi/composer.json+7 5 modified
    @@ -1,16 +1,17 @@
     {
         "name": "magento/module-media-content-api",
         "description": "Magento module provides the API interfaces for managing relations between content and media files used in that content",
    -    "require": {
    -        "php": "~7.4.0||~8.1.0",
    -        "magento/module-media-gallery-api": "*",
    -        "magento/framework": "*"
    -    },
         "type": "magento2-module",
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "100.4.4",
    +    "require": {
    +        "php": "~7.4.0||~8.1.0",
    +        "magento/module-media-gallery-api": "101.0.*",
    +        "magento/framework": "103.0.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -20,3 +21,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/MediaContentCatalog/composer.json+10 8 modified
    @@ -1,19 +1,20 @@
     {
         "name": "magento/module-media-content-catalog",
         "description": "Magento module provides the implementation of MediaContent functionality for Magento_Catalog module",
    -    "require": {
    -        "php": "~7.4.0||~8.1.0",
    -        "magento/module-media-content-api": "*",
    -        "magento/module-catalog": "*",
    -        "magento/module-eav": "*",
    -        "magento/module-store": "*",
    -        "magento/framework": "*"
    -    },
         "type": "magento2-module",
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "100.4.3",
    +    "require": {
    +        "php": "~7.4.0||~8.1.0",
    +        "magento/module-media-content-api": "100.4.*",
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-eav": "102.1.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/framework": "103.0.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -23,3 +24,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/MediaContentCms/composer.json+8 6 modified
    @@ -1,17 +1,18 @@
     {
         "name": "magento/module-media-content-cms",
         "description": "Magento module provides the implementation of MediaContent functionality for Magento_Cms module",
    -    "require": {
    -        "php": "~7.4.0||~8.1.0",
    -        "magento/module-media-content-api": "*",
    -        "magento/module-cms": "*",
    -        "magento/framework": "*"
    -    },
         "type": "magento2-module",
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "100.4.3",
    +    "require": {
    +        "php": "~7.4.0||~8.1.0",
    +        "magento/module-media-content-api": "100.4.*",
    +        "magento/module-cms": "104.0.*",
    +        "magento/framework": "103.0.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -21,3 +22,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/MediaContent/composer.json+8 6 modified
    @@ -1,17 +1,18 @@
     {
         "name": "magento/module-media-content",
         "description": "Magento module provides the implementation for managing relations between content and media files used in that content",
    -    "require": {
    -        "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    -        "magento/module-media-content-api": "*",
    -        "magento/module-media-gallery-api": "*"
    -    },
         "type": "magento2-module",
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "100.4.3",
    +    "require": {
    +        "php": "~7.4.0||~8.1.0",
    +        "magento/framework": "103.0.*",
    +        "magento/module-media-content-api": "100.4.*",
    +        "magento/module-media-gallery-api": "101.0.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -21,3 +22,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/MediaContentSynchronizationApi/composer.json+7 5 modified
    @@ -1,16 +1,17 @@
     {
         "name": "magento/module-media-content-synchronization-api",
         "description": "Magento module responsible for the media content synchronization implementation API",
    -    "require": {
    -        "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    -        "magento/module-media-content-api": "*"
    -    },
         "type": "magento2-module",
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "100.4.3",
    +    "require": {
    +        "php": "~7.4.0||~8.1.0",
    +        "magento/framework": "103.0.*",
    +        "magento/module-media-content-api": "100.4.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -20,3 +21,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/MediaContentSynchronizationCatalog/composer.json+9 7 modified
    @@ -1,18 +1,19 @@
     {
         "name": "magento/module-media-content-synchronization-catalog",
         "description": "Magento module provides the implementation of MediaContentSynchronization functionality for Magento_Catalog module",
    -    "require": {
    -        "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    -        "magento/module-media-content-synchronization-api": "*",
    -        "magento/module-media-gallery-synchronization-api": "*",
    -        "magento/module-media-content-api": "*"
    -    },
         "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-media-content-synchronization-api": "100.4.*",
    +        "magento/module-media-gallery-synchronization-api": "100.4.*",
    +        "magento/module-media-content-api": "100.4.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -22,3 +23,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/MediaContentSynchronizationCms/composer.json+9 7 modified
    @@ -1,18 +1,19 @@
     {
         "name": "magento/module-media-content-synchronization-cms",
         "description": "Magento module provides the implementation of MediaContentSynchronization functionality for Magento_Cms module",
    -    "require": {
    -        "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    -        "magento/module-media-content-synchronization-api": "*",
    -        "magento/module-media-gallery-synchronization-api": "*",
    -        "magento/module-media-content-api": "*"
    -    },
         "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-media-content-synchronization-api": "100.4.*",
    +        "magento/module-media-gallery-synchronization-api": "100.4.*",
    +        "magento/module-media-content-api": "100.4.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -22,3 +23,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/MediaContentSynchronization/composer.json+13 11 modified
    @@ -1,22 +1,23 @@
     {
         "name": "magento/module-media-content-synchronization",
         "description": "Magento module provides implementation of the media content data synchronization.",
    -    "require": {
    -        "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    -        "magento/framework-bulk": "*",
    -        "magento/module-media-content-synchronization-api": "*",
    -        "magento/module-media-content-api": "*",
    -        "magento/module-asynchronous-operations": "*"
    -    },
    -    "suggest": {
    -        "magento/module-media-gallery-synchronization": "*"
    -    },
         "type": "magento2-module",
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "100.4.4",
    +    "require": {
    +        "php": "~7.4.0||~8.1.0",
    +        "magento/framework": "103.0.*",
    +        "magento/framework-bulk": "101.0.*",
    +        "magento/module-media-content-synchronization-api": "100.4.*",
    +        "magento/module-media-content-api": "100.4.*",
    +        "magento/module-asynchronous-operations": "100.4.*"
    +    },
    +    "suggest": {
    +        "magento/module-media-gallery-synchronization": "100.4.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -26,3 +27,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/MediaGalleryApi/composer.json+6 4 modified
    @@ -1,15 +1,16 @@
     {
         "name": "magento/module-media-gallery-api",
         "description": "Magento module responsible for media gallery asset attributes storage and management",
    -    "require": {
    -        "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*"
    -    },
         "type": "magento2-module",
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "101.0.4",
    +    "require": {
    +        "php": "~7.4.0||~8.1.0",
    +        "magento/framework": "103.0.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -19,3 +20,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/MediaGalleryCatalog/composer.json+8 6 modified
    @@ -1,17 +1,18 @@
     {
         "name": "magento/module-media-gallery-catalog",
         "description": "Magento module responsible for catalog gallery processor delete operation handling",
    -    "require": {
    -        "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    -        "magento/module-media-gallery-api": "*",
    -        "magento/module-catalog": "*"
    -    },
         "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-media-gallery-api": "101.0.*",
    +        "magento/module-catalog": "104.0.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -21,3 +22,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/MediaGalleryCatalogIntegration/composer.json+13 11 modified
    @@ -1,22 +1,23 @@
     {
         "name": "magento/module-media-gallery-catalog-integration",
         "description": "Magento module responsible for extending catalog image uploader functionality",
    -    "require": {
    -        "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    -        "magento/module-cms": "*",
    -        "magento/module-media-gallery-api": "*",
    -        "magento/module-media-gallery-synchronization-api": "*",
    -        "magento/module-media-gallery-ui-api": "*"
    -    },
    -    "suggest": {
    -        "magento/module-catalog": "*"
    -    },
         "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-cms": "104.0.*",
    +        "magento/module-media-gallery-api": "101.0.*",
    +        "magento/module-media-gallery-synchronization-api": "100.4.*",
    +        "magento/module-media-gallery-ui-api": "100.4.*"
    +    },
    +    "suggest": {
    +        "magento/module-catalog": "104.0.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -26,3 +27,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/MediaGalleryCatalogUi/composer.json+11 9 modified
    @@ -1,20 +1,21 @@
     {
         "name": "magento/module-media-gallery-catalog-ui",
         "description": "Magento module that implement category grid for media gallery.",
    -    "require": {
    -        "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    -        "magento/module-cms": "*",
    -        "magento/module-backend": "*",
    -        "magento/module-catalog": "*",
    -        "magento/module-store": "*",
    -        "magento/module-ui": "*"
    -    },
         "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-cms": "104.0.*",
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/module-ui": "101.2.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -24,3 +25,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/MediaGalleryCmsUi/composer.json+8 6 modified
    @@ -1,17 +1,18 @@
     {
         "name": "magento/module-media-gallery-cms-ui",
         "description": "Cms related UI elements in the magento media gallery",
    -    "require": {
    -        "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    -        "magento/module-cms": "*",
    -        "magento/module-backend": "*"
    -    },
         "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-cms": "104.0.*",
    +        "magento/module-backend": "102.0.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -21,3 +22,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/MediaGallery/composer.json+8 6 modified
    @@ -1,17 +1,18 @@
     {
         "name": "magento/module-media-gallery",
         "description": "Magento module responsible for media handling",
    -    "require": {
    -        "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    -        "magento/module-media-gallery-api": "*",
    -        "magento/module-cms": "*"
    -    },
         "type": "magento2-module",
         "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-media-gallery-api": "101.0.*",
    +        "magento/module-cms": "104.0.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -21,3 +22,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/MediaGalleryIntegration/composer.json+17 15 modified
    @@ -1,32 +1,34 @@
     {
         "name": "magento/module-media-gallery-integration",
         "description": "Magento module responsible for integration of enhanced media gallery",
    -    "require": {
    -        "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    -        "magento/module-media-gallery-ui-api": "*",
    -        "magento/module-media-gallery-api": "*",
    -        "magento/module-media-gallery-synchronization-api": "*",
    -        "magento/module-ui": "*"
    -    },
    -    "require-dev": {
    -        "magento/module-cms": "*"
    -    },
    -    "suggest": {
    -        "magento/module-catalog": "*",
    -        "magento/module-cms": "*"
    -    },
         "type": "magento2-module",
         "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-media-gallery-ui-api": "100.4.*",
    +        "magento/module-media-gallery-api": "101.0.*",
    +        "magento/module-media-gallery-synchronization-api": "100.4.*",
    +        "magento/module-ui": "101.2.*"
    +    },
    +    "suggest": {
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-cms": "104.0.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
             ],
             "psr-4": {
                 "Magento\\MediaGalleryIntegration\\": ""
             }
    +    },
    +    "require-dev": {
    +        "magento/module-cms": "*"
         }
     }
    +
    
  • app/code/Magento/MediaGalleryMetadataApi/composer.json+6 4 modified
    @@ -1,15 +1,16 @@
     {
         "name": "magento/module-media-gallery-metadata-api",
         "description": "Magento module responsible for media gallery metadata implementation API",
    -    "require": {
    -        "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*"
    -    },
         "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.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -19,3 +20,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/MediaGalleryMetadata/composer.json+7 5 modified
    @@ -1,16 +1,17 @@
     {
         "name": "magento/module-media-gallery-metadata",
         "description": "Magento module responsible for images metadata processing",
    -    "require": {
    -        "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    -        "magento/module-media-gallery-metadata-api": "*"
    -    },
         "type": "magento2-module",
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "100.4.3",
    +    "require": {
    +        "php": "~7.4.0||~8.1.0",
    +        "magento/framework": "103.0.*",
    +        "magento/module-media-gallery-metadata-api": "100.4.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -20,3 +21,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/MediaGalleryRenditionsApi/composer.json+6 4 modified
    @@ -1,15 +1,16 @@
     {
         "name": "magento/module-media-gallery-renditions-api",
         "description": "Magento module that is responsible for the API implementation of Media Gallery Renditions.",
    -    "require": {
    -        "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*"
    -    },
         "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.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -19,3 +20,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/MediaGalleryRenditions/composer.json+13 11 modified
    @@ -1,22 +1,23 @@
     {
         "name": "magento/module-media-gallery-renditions",
         "description": "Magento module that implements height and width fields for for media gallery items.",
    -    "require": {
    -        "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    -        "magento/module-media-gallery-renditions-api": "*",
    -        "magento/module-media-gallery-api": "*",
    -        "magento/framework-message-queue": "*",
    -        "magento/module-cms": "*"
    -    },
    -    "suggest": {
    -        "magento/module-media-content-api": "*"
    -    },
         "type": "magento2-module",
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "100.4.3",
    +    "require": {
    +        "php": "~7.4.0||~8.1.0",
    +        "magento/framework": "103.0.*",
    +        "magento/module-media-gallery-renditions-api": "100.4.*",
    +        "magento/module-media-gallery-api": "101.0.*",
    +        "magento/framework-message-queue": "100.4.*",
    +        "magento/module-cms": "104.0.*"
    +    },
    +    "suggest": {
    +        "magento/module-media-content-api": "100.4.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -26,3 +27,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/MediaGallerySynchronizationApi/composer.json+7 5 modified
    @@ -1,16 +1,17 @@
     {
         "name": "magento/module-media-gallery-synchronization-api",
         "description": "Magento module responsible for the media gallery synchronization implementation API",
    -    "require": {
    -        "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    -        "magento/module-media-gallery-api": "*"
    -    },
         "type": "magento2-module",
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "100.4.3",
    +    "require": {
    +        "php": "~7.4.0||~8.1.0",
    +        "magento/framework": "103.0.*",
    +        "magento/module-media-gallery-api": "101.0.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -20,3 +21,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/MediaGallerySynchronization/composer.json+9 7 modified
    @@ -1,18 +1,19 @@
     {
         "name": "magento/module-media-gallery-synchronization",
         "description": "Magento module provides implementation of the media gallery data synchronization.",
    -    "require": {
    -        "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    -        "magento/module-media-gallery-api": "*",
    -        "magento/module-media-gallery-synchronization-api": "*",
    -        "magento/framework-message-queue": "*"
    -    },
         "type": "magento2-module",
         "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-media-gallery-api": "101.0.*",
    +        "magento/module-media-gallery-synchronization-api": "100.4.*",
    +        "magento/framework-message-queue": "100.4.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -22,3 +23,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/MediaGallerySynchronizationMetadata/composer.json+9 7 modified
    @@ -1,18 +1,19 @@
     {
         "name": "magento/module-media-gallery-synchronization-metadata",
         "description": "Magento module responsible for images metadata synchronization",
    -    "require": {
    -        "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    -        "magento/module-media-gallery-api": "*",
    -        "magento/module-media-gallery-metadata-api": "*",
    -        "magento/module-media-gallery-synchronization-api": "*"
    -    },
         "type": "magento2-module",
         "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-media-gallery-api": "101.0.*",
    +        "magento/module-media-gallery-metadata-api": "100.4.*",
    +        "magento/module-media-gallery-synchronization-api": "100.4.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -22,3 +23,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/MediaGalleryUiApi/composer.json+9 7 modified
    @@ -1,18 +1,19 @@
     {
         "name": "magento/module-media-gallery-ui-api",
         "description": "Magento module responsible for the media gallery UI implementation API",
    -    "require": {
    -        "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*"
    -    },
    -    "suggest": {
    -        "magento/module-cms": "*"
    -    },
         "type": "magento2-module",
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "100.4.3",
    +    "require": {
    +        "php": "~7.4.0||~8.1.0",
    +        "magento/framework": "103.0.*"
    +    },
    +    "suggest": {
    +        "magento/module-cms": "104.0.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -22,3 +23,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/MediaGalleryUi/composer.json+17 15 modified
    @@ -1,26 +1,27 @@
     {
         "name": "magento/module-media-gallery-ui",
         "description": "Magento module responsible for the media gallery UI implementation",
    -    "require": {
    -        "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    -        "magento/module-backend": "*",
    -        "magento/module-ui": "*",
    -        "magento/module-store": "*",
    -        "magento/module-media-gallery-ui-api": "*",
    -        "magento/module-media-gallery-api": "*",
    -        "magento/module-media-gallery-metadata-api": "*",
    -        "magento/module-media-gallery-synchronization-api": "*",
    -        "magento/module-media-content-api": "*",
    -        "magento/module-cms": "*",
    -        "magento/module-directory": "*",
    -        "magento/module-authorization": "*"
    -    },
         "type": "magento2-module",
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "100.4.4-p3",
    +    "require": {
    +        "php": "~7.4.0||~8.1.0",
    +        "magento/framework": "103.0.*",
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-ui": "101.2.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/module-media-gallery-ui-api": "100.4.*",
    +        "magento/module-media-gallery-api": "101.0.*",
    +        "magento/module-media-gallery-metadata-api": "100.4.*",
    +        "magento/module-media-gallery-synchronization-api": "100.4.*",
    +        "magento/module-media-content-api": "100.4.*",
    +        "magento/module-cms": "104.0.*",
    +        "magento/module-directory": "100.4.*",
    +        "magento/module-authorization": "100.4.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -30,3 +31,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/MediaGalleryUi/Test/Mftf/Test/AdminMediaGallerySwitchingBetweenViewsTest.xml+6 1 modified
    @@ -28,7 +28,12 @@
                 <actionGroup ref="AdminEnhancedMediaGalleryDeleteGridViewActionGroup" stepKey="deleteView">
                     <argument name="viewToDelete" value="{{NewGridView.name}}"/>
                 </actionGroup>
    -            <actionGroup ref="AdminMediaGalleryFolderSelectActionGroup" stepKey="selectFolderForDelete"/>
    +            <actionGroup ref="AdminMediaGalleryFolderSelectActionGroup" stepKey="selectWysiwygFolder1">
    +                <argument name="name" value="wysiwyg"/>
    +            </actionGroup>
    +            <actionGroup ref="AdminMediaGalleryFolderSelectActionGroup" stepKey="selectFolderForDelete">
    +                <argument name="name" value="{{AdminMediaGalleryFolderData.name}}"/>
    +            </actionGroup>
                 <actionGroup ref="AdminMediaGalleryFolderDeleteActionGroup" stepKey="deleteFolder"/>
                 <actionGroup ref="AdminMediaGalleryAssertFolderDoesNotExistActionGroup" stepKey="assertFolderWasDeleted"/>
                 <deleteData createDataKey="category" stepKey="deleteCategory"/>
    
  • app/code/Magento/MediaStorage/composer.json+16 14 modified
    @@ -1,26 +1,27 @@
     {
         "name": "magento/module-media-storage",
         "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/framework-bulk": "*",
    -        "magento/module-backend": "*",
    -        "magento/module-config": "*",
    -        "magento/module-store": "*",
    -        "magento/module-catalog": "*",
    -        "magento/module-theme": "*",
    -        "magento/module-asynchronous-operations": "*",
    -        "magento/module-authorization": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/framework-bulk": "101.0.*",
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-config": "101.2.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-theme": "101.1.*",
    +        "magento/module-asynchronous-operations": "100.4.*",
    +        "magento/module-authorization": "100.4.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -30,3 +31,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/MessageQueue/composer.json+9 7 modified
    @@ -1,20 +1,21 @@
     {
         "name": "magento/module-message-queue",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.5",
         "require": {
    -        "magento/framework": "*",
    -        "magento/framework-message-queue": "*",
    +        "magento/framework": "103.0.*",
    +        "magento/framework-message-queue": "100.4.*",
             "magento/magento-composer-installer": "*",
             "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 @@
             }
         }
     }
    +
    
  • app/code/Magento/Msrp/composer.json+15 13 modified
    @@ -1,27 +1,28 @@
     {
         "name": "magento/module-msrp",
         "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-downloadable": "*",
    -        "magento/module-eav": "*",
    -        "magento/module-store": "*",
    -        "magento/module-tax": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-downloadable": "100.4.*",
    +        "magento/module-eav": "102.1.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/module-tax": "100.4.*"
         },
         "suggest": {
    -        "magento/module-bundle": "*",
    -        "magento/module-msrp-sample-data": "*"
    +        "magento/module-bundle": "101.0.*",
    +        "magento/module-msrp-sample-data": "Sample Data version: 100.4.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -31,3 +32,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/MsrpConfigurableProduct/composer.json+11 9 modified
    @@ -1,21 +1,22 @@
     {
         "name": "magento/module-msrp-configurable-product",
         "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-msrp": "*",
    -        "magento/module-configurable-product": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-msrp": "100.4.*",
    +        "magento/module-configurable-product": "100.4.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -25,3 +26,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/MsrpGroupedProduct/composer.json+11 9 modified
    @@ -1,21 +1,22 @@
     {
         "name": "magento/module-msrp-grouped-product",
         "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-msrp": "*",
    -        "magento/module-grouped-product": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-msrp": "100.4.*",
    +        "magento/module-grouped-product": "100.4.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -25,3 +26,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Multishipping/composer.json+18 16 modified
    @@ -1,28 +1,29 @@
     {
         "name": "magento/module-multishipping",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.5",
         "require": {
             "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    -        "magento/module-checkout": "*",
    -        "magento/module-customer": "*",
    -        "magento/module-directory": "*",
    -        "magento/module-payment": "*",
    -        "magento/module-quote": "*",
    -        "magento/module-sales": "*",
    -        "magento/module-store": "*",
    -        "magento/module-tax": "*",
    -        "magento/module-theme": "*",
    -        "magento/module-captcha": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-checkout": "100.4.*",
    +        "magento/module-customer": "103.0.*",
    +        "magento/module-directory": "100.4.*",
    +        "magento/module-payment": "100.4.*",
    +        "magento/module-quote": "101.2.*",
    +        "magento/module-sales": "103.0.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/module-tax": "100.4.*",
    +        "magento/module-theme": "101.1.*",
    +        "magento/module-captcha": "100.4.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -32,3 +33,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/MysqlMq/composer.json+10 8 modified
    @@ -1,21 +1,22 @@
     {
         "name": "magento/module-mysql-mq",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.3",
         "require": {
    -        "magento/framework": "*",
    -        "magento/framework-message-queue": "*",
    +        "magento/framework": "103.0.*",
    +        "magento/framework-message-queue": "100.4.*",
             "magento/magento-composer-installer": "*",
    -        "magento/module-store": "*",
    +        "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"
    @@ -25,3 +26,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/NewRelicReporting/composer.json+14 12 modified
    @@ -1,25 +1,26 @@
     {
         "name": "magento/module-new-relic-reporting",
         "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-backend": "*",
    -        "magento/module-catalog": "*",
    -        "magento/module-config": "*",
    -        "magento/module-configurable-product": "*",
    -        "magento/module-customer": "*",
    -        "magento/module-store": "*"
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-config": "101.2.*",
    +        "magento/module-configurable-product": "100.4.*",
    +        "magento/module-customer": "103.0.*",
    +        "magento/module-store": "101.1.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -29,3 +30,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Newsletter/composer.json+17 15 modified
    @@ -1,27 +1,28 @@
     {
         "name": "magento/module-newsletter",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.5-p6",
         "require": {
             "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    -        "magento/module-backend": "*",
    -        "magento/module-cms": "*",
    -        "magento/module-customer": "*",
    -        "magento/module-eav": "*",
    -        "magento/module-email": "*",
    -        "magento/module-require-js": "*",
    -        "magento/module-store": "*",
    -        "magento/module-widget": "*",
    -        "magento/module-ui": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-cms": "104.0.*",
    +        "magento/module-customer": "103.0.*",
    +        "magento/module-eav": "102.1.*",
    +        "magento/module-email": "101.1.*",
    +        "magento/module-require-js": "100.4.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/module-widget": "101.2.*",
    +        "magento/module-ui": "101.2.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -31,3 +32,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/NewsletterGraphQl/composer.json+12 10 modified
    @@ -1,24 +1,25 @@
     {
         "name": "magento/module-newsletter-graph-ql",
         "description": "Provides GraphQl functionality for the newsletter subscriptions.",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    -    "type": "magento2-module",
    +    "version": "100.4.2",
         "require": {
             "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    -        "magento/module-customer": "*",
    -        "magento/module-newsletter": "*",
    -        "magento/module-store": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-customer": "103.0.*",
    +        "magento/module-newsletter": "100.4.*",
    +        "magento/module-store": "101.1.*"
         },
         "suggest": {
    -        "magento/module-graph-ql": "*"
    +        "magento/module-graph-ql": "100.4.*"
         },
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -28,3 +29,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Newsletter/Test/Mftf/ActionGroup/AdminMarketingDeleteNewsletterTemplateActionGroup.xml+2 0 modified
    @@ -10,7 +10,9 @@
                   xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd">
         <!--Delete Newsletter Template -->
         <actionGroup name="AdminMarketingDeleteNewsletterTemplateActionGroup">
    +        <waitForElementVisible selector="{{AdminNewsletterMainActionsSection.deleteTemplateButton}}" stepKey="waitForDeleteElementButtonToBeVisible"/>
             <click stepKey="clickDeleteButton" selector="{{AdminNewsletterMainActionsSection.deleteTemplateButton}}"/>
    +        <waitForElementVisible selector="{{AdminNewsletterMainActionsSection.confirmDelete}}" stepKey="waitForConfirmPopup"/>
             <click stepKey="confirmDelete" selector="{{AdminNewsletterMainActionsSection.confirmDelete}}"/>
             <waitForPageLoad stepKey="waitForPageLoading"/>
         </actionGroup>
    
  • app/code/Magento/Newsletter/Test/Mftf/ActionGroup/AdminMarketingOpenNewsletterTemplateFromGridActionGroup.xml+2 0 modified
    @@ -9,6 +9,8 @@
     <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                   xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd">
         <actionGroup name="AdminMarketingOpenNewsletterTemplateFromGridActionGroup">
    +        <waitForElementVisible selector="{{AdminNewsletterGridMainActionsSection.searchResultFirstRow}}" stepKey="waitForElementToBeVisible"/>
             <click stepKey="openTemplate" selector="{{AdminNewsletterGridMainActionsSection.searchResultFirstRow}}"/>
    +        <waitForPageLoad stepKey="waitForPageLoadAfterClick"/>
         </actionGroup>
     </actionGroups>
    
  • app/code/Magento/OfflinePayments/composer.json+12 10 modified
    @@ -1,24 +1,25 @@
     {
         "name": "magento/module-offline-payments",
         "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-checkout": "*",
    -        "magento/module-payment": "*",
    -        "magento/module-quote": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-checkout": "100.4.*",
    +        "magento/module-payment": "100.4.*",
    +        "magento/module-quote": "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"
    @@ -28,3 +29,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/OfflineShipping/composer.json+19 17 modified
    @@ -1,31 +1,32 @@
     {
         "name": "magento/module-offline-shipping",
         "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-config": "*",
    -        "magento/module-directory": "*",
    -        "magento/module-quote": "*",
    -        "magento/module-sales": "*",
    -        "magento/module-sales-rule": "*",
    -        "magento/module-shipping": "*",
    -        "magento/module-store": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-config": "101.2.*",
    +        "magento/module-directory": "100.4.*",
    +        "magento/module-quote": "101.2.*",
    +        "magento/module-sales": "103.0.*",
    +        "magento/module-sales-rule": "101.2.*",
    +        "magento/module-shipping": "100.4.*",
    +        "magento/module-store": "101.1.*"
         },
         "suggest": {
    -        "magento/module-checkout": "*",
    -        "magento/module-offline-shipping-sample-data": "*"
    +        "magento/module-checkout": "100.4.*",
    +        "magento/module-offline-shipping-sample-data": "Sample Data version: 100.4.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -35,3 +36,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/PageCache/composer.json+12 10 modified
    @@ -1,22 +1,23 @@
     {
         "name": "magento/module-page-cache",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.5-p6",
         "require": {
             "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    -        "magento/module-backend": "*",
    -        "magento/module-config": "*",
    -        "magento/module-store": "*",
    -        "magento/module-catalog": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-config": "101.2.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/module-catalog": "104.0.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -26,3 +27,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/PageCache/Controller/Block.php+65 8 modified
    @@ -1,15 +1,19 @@
     <?php
     /**
    - * PageCache controller
      *
      * Copyright © Magento, Inc. All rights reserved.
      * See COPYING.txt for license details.
      */
    +declare(strict_types=1);
    +
     namespace Magento\PageCache\Controller;
     
     use Magento\Framework\Serialize\Serializer\Base64Json;
     use Magento\Framework\Serialize\Serializer\Json;
    +use Magento\Framework\Validator\RegexFactory;
    +use Magento\Framework\App\ObjectManager;
     use Magento\Framework\View\Layout\LayoutCacheKeyInterface;
    +use Magento\Framework\App\Config\ScopeConfigInterface;
     
     abstract class Block extends \Magento\Framework\App\Action\Action
     {
    @@ -40,28 +44,55 @@ abstract class Block extends \Magento\Framework\App\Action\Action
          */
         private $layoutCacheKeyName = 'mage_pagecache';
     
    +    /**
    +     * @var RegexFactory
    +     */
    +    private RegexFactory $regexValidatorFactory;
    +
    +    /**
    +     * Validation pattern for handles array
    +     */
    +    private const VALIDATION_RULE_PATTERN = '/^[a-z0-9]+[a-z0-9_]*$/i';
    +
    +    /**
    +     * @var ScopeConfigInterface
    +     */
    +    private $config;
    +
    +    /**
    +     * Handle size system name
    +     */
    +    private const XML_HANDLES_SIZE = 'system/full_page_cache/handles_size';
    +
         /**
          * @param \Magento\Framework\App\Action\Context $context
          * @param \Magento\Framework\Translate\InlineInterface $translateInline
    -     * @param Json $jsonSerializer
    -     * @param Base64Json $base64jsonSerializer
    -     * @param LayoutCacheKeyInterface $layoutCacheKey
    +     * @param Json|null $jsonSerializer
    +     * @param Base64Json|null $base64jsonSerializer
    +     * @param LayoutCacheKeyInterface|null $layoutCacheKey
    +     * @param RegexFactory|null $regexValidatorFactory
    +     * @param ScopeConfigInterface|null $scopeConfig
          */
         public function __construct(
             \Magento\Framework\App\Action\Context $context,
             \Magento\Framework\Translate\InlineInterface $translateInline,
             Json $jsonSerializer = null,
             Base64Json $base64jsonSerializer = null,
    -        LayoutCacheKeyInterface $layoutCacheKey = null
    +        LayoutCacheKeyInterface $layoutCacheKey = null,
    +        ?RegexFactory $regexValidatorFactory = null,
    +        ScopeConfigInterface $scopeConfig = null
         ) {
             parent::__construct($context);
             $this->translateInline = $translateInline;
             $this->jsonSerializer = $jsonSerializer
    -            ?: \Magento\Framework\App\ObjectManager::getInstance()->get(Json::class);
    +            ?: ObjectManager::getInstance()->get(Json::class);
             $this->base64jsonSerializer = $base64jsonSerializer
    -            ?: \Magento\Framework\App\ObjectManager::getInstance()->get(Base64Json::class);
    +            ?: ObjectManager::getInstance()->get(Base64Json::class);
             $this->layoutCacheKey = $layoutCacheKey
    -            ?: \Magento\Framework\App\ObjectManager::getInstance()->get(LayoutCacheKeyInterface::class);
    +            ?: ObjectManager::getInstance()->get(LayoutCacheKeyInterface::class);
    +        $this->regexValidatorFactory = $regexValidatorFactory
    +            ?: ObjectManager::getInstance()->get(RegexFactory::class);
    +        $this->config = $scopeConfig;
         }
     
         /**
    @@ -80,6 +111,14 @@ protected function _getBlocks()
             $blocks = $this->jsonSerializer->unserialize($blocks);
             $handles = $this->base64jsonSerializer->unserialize($handles);
     
    +        $handlesSize = (int) $this->config->getValue(self::XML_HANDLES_SIZE);
    +        $handles = ($handlesSize && count($handles) > $handlesSize)
    +            ? array_splice($handles, 0, $handlesSize) : $handles;
    +
    +        if (!$this->validateHandleParam($handles)) {
    +            return [];
    +        }
    +
             $layout = $this->_view->getLayout();
             $this->layoutCacheKey->addCacheKeys($this->layoutCacheKeyName);
     
    @@ -95,4 +134,22 @@ protected function _getBlocks()
     
             return $data;
         }
    +
    +    /**
    +     * Validates handles parameter
    +     *
    +     * @param array $handles
    +     * @return bool
    +     */
    +    private function validateHandleParam($handles): bool
    +    {
    +        $validator = $this->regexValidatorFactory->create(['pattern' => self::VALIDATION_RULE_PATTERN]);
    +        foreach ($handles as $handle) {
    +            if ($handle && !$validator->isValid($handle)) {
    +                return false;
    +            }
    +        }
    +
    +        return true;
    +    }
     }
    
  • app/code/Magento/PageCache/etc/adminhtml/system.xml+4 0 modified
    @@ -78,6 +78,10 @@
                         <comment>Public content cache lifetime in seconds. If field is empty default value 86400 will be saved. </comment>
                         <backend_model>Magento\PageCache\Model\System\Config\Backend\Ttl</backend_model>
                     </field>
    +                <field id="handles_size" type="text" translate="label comment" sortOrder="5" showInDefault="1" canRestore="1">
    +                    <label>Handles params size</label>
    +                    <comment>Handles params size. For better performance use handles parameter size between 50 and 100. </comment>
    +                </field>
                 </group>
             </section>
         </system>
    
  • app/code/Magento/PageCache/etc/config.xml+1 0 modified
    @@ -24,6 +24,7 @@
                         <path>varnish4.vcl</path>
                     </varnish4>
                     <ttl>86400</ttl>
    +                <handles_size>100</handles_size>
                     <caching_application>1</caching_application>
                     <default>
                         <access_list>localhost</access_list>
    
  • app/code/Magento/PageCache/etc/di.xml+1 0 modified
    @@ -35,6 +35,7 @@
         <type name="Magento\PageCache\Controller\Block">
             <arguments>
                 <argument name="layoutCacheKey" xsi:type="object">Magento\Framework\View\Layout\LayoutCacheKeyInterface</argument>
    +            <argument name="scopeConfig" xsi:type="object">Magento\Framework\App\Config\ScopeConfigInterface\Proxy</argument>
             </arguments>
         </type>
         <type name="Magento\Framework\App\Cache\RuntimeStaleCacheStateModifier">
    
  • app/code/Magento/PageCache/Test/Unit/Controller/Block/EsiTest.php+19 1 modified
    @@ -18,6 +18,8 @@
     use Magento\Framework\View\Element\AbstractBlock;
     use Magento\Framework\View\Layout;
     use Magento\Framework\View\Layout\LayoutCacheKeyInterface;
    +use Magento\Framework\Validator\Regex;
    +use Magento\Framework\Validator\RegexFactory;
     use Magento\PageCache\Controller\Block;
     use Magento\PageCache\Controller\Block\Esi;
     use Magento\PageCache\Test\Unit\Block\Controller\StubBlock;
    @@ -64,6 +66,11 @@ class EsiTest extends TestCase
          */
         protected $translateInline;
     
    +    /**
    +     * Validation pattern for handles array
    +     */
    +    private const VALIDATION_RULE_PATTERN = '/^[a-z0-9]+[a-z0-9_]*$/i';
    +
         /**
          * Set up before test
          */
    @@ -98,6 +105,16 @@ protected function setUp(): void
     
             $this->translateInline = $this->getMockForAbstractClass(InlineInterface::class);
     
    +        $regexFactoryMock = $this->getMockBuilder(RegexFactory::class)
    +            ->disableOriginalConstructor()
    +            ->setMethods(['create'])
    +            ->getMock();
    +
    +        $regexObject = new Regex(self::VALIDATION_RULE_PATTERN);
    +
    +        $regexFactoryMock->expects($this->any())->method('create')
    +            ->willReturn($regexObject);
    +
             $helperObjectManager = new ObjectManager($this);
             $this->action = $helperObjectManager->getObject(
                 Esi::class,
    @@ -106,7 +123,8 @@ protected function setUp(): void
                     'translateInline' => $this->translateInline,
                     'jsonSerializer' => new Json(),
                     'base64jsonSerializer' => new Base64Json(),
    -                'layoutCacheKey' => $this->layoutCacheKeyMock
    +                'layoutCacheKey' => $this->layoutCacheKeyMock,
    +                'regexValidatorFactory' => $regexFactoryMock
                 ]
             );
         }
    
  • app/code/Magento/PageCache/Test/Unit/Controller/Block/RenderTest.php+19 1 modified
    @@ -18,6 +18,8 @@
     use Magento\Framework\View\Layout;
     use Magento\Framework\View\Layout\LayoutCacheKeyInterface;
     use Magento\Framework\View\Layout\ProcessorInterface;
    +use Magento\Framework\Validator\Regex;
    +use Magento\Framework\Validator\RegexFactory;
     use Magento\PageCache\Controller\Block;
     use Magento\PageCache\Controller\Block\Render;
     use Magento\PageCache\Test\Unit\Block\Controller\StubBlock;
    @@ -69,6 +71,11 @@ class RenderTest extends TestCase
          */
         protected $layoutCacheKeyMock;
     
    +    /**
    +     * Validation pattern for handles array
    +     */
    +    private const VALIDATION_RULE_PATTERN = '/^[a-z0-9]+[a-z0-9_]*$/i';
    +
         /**
          * @inheritDoc
          */
    @@ -111,6 +118,16 @@ protected function setUp(): void
     
             $this->translateInline = $this->getMockForAbstractClass(InlineInterface::class);
     
    +        $regexFactoryMock = $this->getMockBuilder(RegexFactory::class)
    +            ->disableOriginalConstructor()
    +            ->setMethods(['create'])
    +            ->getMock();
    +
    +        $regexObject = new Regex(self::VALIDATION_RULE_PATTERN);
    +
    +        $regexFactoryMock->expects($this->any())->method('create')
    +            ->willReturn($regexObject);
    +
             $helperObjectManager = new ObjectManager($this);
             $this->action = $helperObjectManager->getObject(
                 Render::class,
    @@ -119,7 +136,8 @@ protected function setUp(): void
                     'translateInline' => $this->translateInline,
                     'jsonSerializer' => new Json(),
                     'base64jsonSerializer' => new Base64Json(),
    -                'layoutCacheKey' => $this->layoutCacheKeyMock
    +                'layoutCacheKey' => $this->layoutCacheKeyMock,
    +                'regexValidatorFactory' => $regexFactoryMock
                 ]
             );
         }
    
  • app/code/Magento/Payment/composer.json+15 13 modified
    @@ -1,25 +1,26 @@
     {
         "name": "magento/module-payment",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.5",
         "require": {
             "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    -        "magento/module-checkout": "*",
    -        "magento/module-config": "*",
    -        "magento/module-directory": "*",
    -        "magento/module-quote": "*",
    -        "magento/module-sales": "*",
    -        "magento/module-store": "*",
    -        "magento/module-ui": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-checkout": "100.4.*",
    +        "magento/module-config": "101.2.*",
    +        "magento/module-directory": "100.4.*",
    +        "magento/module-quote": "101.2.*",
    +        "magento/module-sales": "103.0.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/module-ui": "101.2.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -29,3 +30,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/PaymentGraphQl/composer.json+10 8 modified
    @@ -2,19 +2,20 @@
         "name": "magento/module-payment-graph-ql",
         "description": "N/A",
         "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
    +    "version": "100.4.0",
         "require": {
             "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    -        "magento/module-payment": "*",
    -        "magento/module-graph-ql": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-payment": "100.4.*",
    +        "magento/module-graph-ql": "100.4.*"
         },
         "suggest": {
    -        "magento/module-store-graph-ql": "*"
    +        "magento/module-store-graph-ql": "100.4.*"
         },
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -24,3 +25,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Paypal/Block/PayLater/Banner.php+2 1 modified
    @@ -97,7 +97,8 @@ public function getJsLayout()
             $config['displayAmount'] = !$displayAmount || $this->payLaterConfig->isPPBillingAgreementEnabled()
                 ? false : true;
             $config['dataAttributes'] = [
    -            'data-partner-attribution-id' => $this->paypalConfig->getBuildNotationCode()
    +            'data-partner-attribution-id' => $this->paypalConfig->getBuildNotationCode(),
    +            'data-csp-nonce' => $this->paypalConfig->getCspNonce(),
             ];
     
             //Extend block component attributes with defaults
    
  • app/code/Magento/Paypal/Block/PayLater/LayoutProcessor.php+2 1 modified
    @@ -88,7 +88,8 @@ public function process($jsLayout)
                 $config['displayAmount'] = !$displayAmount || $this->payLaterConfig->isPPBillingAgreementEnabled()
                     ? false : true;
                 $config['dataAttributes'] = [
    -                'data-partner-attribution-id' => $this->paypalConfig->getBuildNotationCode()
    +                'data-partner-attribution-id' => $this->paypalConfig->getBuildNotationCode(),
    +                'data-csp-nonce' => $this->paypalConfig->getCspNonce(),
                 ];
     
                 $attributes = $this->payLaterConfig->getSectionConfig(
    
  • app/code/Magento/PaypalCaptcha/composer.json+12 10 modified
    @@ -1,24 +1,25 @@
     {
         "name": "magento/module-paypal-captcha",
         "description": "Provides CAPTCHA validation for PayPal Payflow Pro",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.2-p6",
         "require": {
             "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    -        "magento/module-captcha": "*",
    -        "magento/module-checkout": "*",
    -        "magento/module-store": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-captcha": "100.4.*",
    +        "magento/module-checkout": "100.4.*",
    +        "magento/module-store": "101.1.*"
         },
         "suggest": {
    -        "magento/module-paypal": "*"
    +        "magento/module-paypal": "101.0.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -28,3 +29,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/PaypalCaptcha/Test/Mftf/Test/StorefrontPaymentsCaptchaWithPayflowProTest.xml+2 0 modified
    @@ -18,6 +18,8 @@
                 <useCaseId value="MC-41572"/>
                 <severity value="AVERAGE"/>
                 <group value="captcha"/>
    +            <group value="3rd_party_integration"/>
    +            <group value="pr_exclude"/>
             </annotations>
             <before>
                 <!-- Configure Paypal Payflow Pro payment method -->
    
  • app/code/Magento/Paypal/composer.json+27 24 modified
    @@ -1,39 +1,41 @@
     {
         "name": "magento/module-paypal",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "101.0.5-p8",
         "require": {
             "php": "~7.4.0||~8.1.0",
             "lib-libxml": "*",
    -        "magento/framework": "*",
    -        "magento/module-authorization": "*",
    -        "magento/module-backend": "*",
    -        "magento/module-catalog": "*",
    -        "magento/module-checkout": "*",
    -        "magento/module-config": "*",
    -        "magento/module-customer": "*",
    -        "magento/module-directory": "*",
    -        "magento/module-eav": "*",
    -        "magento/module-instant-purchase": "*",
    -        "magento/module-payment": "*",
    -        "magento/module-quote": "*",
    -        "magento/module-sales": "*",
    -        "magento/module-store": "*",
    -        "magento/module-tax": "*",
    -        "magento/module-theme": "*",
    -        "magento/module-ui": "*",
    -        "magento/module-vault": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-authorization": "100.4.*",
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-checkout": "100.4.*",
    +        "magento/module-config": "101.2.*",
    +        "magento/module-customer": "103.0.*",
    +        "magento/module-directory": "100.4.*",
    +        "magento/module-eav": "102.1.*",
    +        "magento/module-instant-purchase": "100.4.*",
    +        "magento/module-payment": "100.4.*",
    +        "magento/module-quote": "101.2.*",
    +        "magento/module-sales": "103.0.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/module-tax": "100.4.*",
    +        "magento/module-theme": "101.1.*",
    +        "magento/module-ui": "101.2.*",
    +        "magento/module-vault": "101.2.*",
    +        "magento/module-csp": "100.4.*"
         },
         "suggest": {
    -        "magento/module-checkout-agreements": "*"
    +        "magento/module-checkout-agreements": "100.4.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -43,3 +45,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/PaypalGraphQl/composer.json+18 16 modified
    @@ -1,30 +1,31 @@
     {
         "name": "magento/module-paypal-graph-ql",
         "description": "GraphQl support for Paypal",
    +    "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-quote": "*",
    -        "magento/module-checkout": "*",
    -        "magento/module-paypal": "*",
    -        "magento/module-quote-graph-ql": "*",
    -        "magento/module-sales": "*",
    -        "magento/module-payment": "*",
    -        "magento/module-store": "*",
    -        "magento/module-vault": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-quote": "101.2.*",
    +        "magento/module-checkout": "100.4.*",
    +        "magento/module-paypal": "101.0.*",
    +        "magento/module-quote-graph-ql": "100.4.*",
    +        "magento/module-sales": "103.0.*",
    +        "magento/module-payment": "100.4.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/module-vault": "101.2.*"
         },
         "suggest": {
    -        "magento/module-graph-ql": "*",
    -        "magento/module-store-graph-ql": "*"
    +        "magento/module-graph-ql": "100.4.*",
    +        "magento/module-store-graph-ql": "100.4.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -34,3 +35,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Paypal/Model/Config.php+24 1 modified
    @@ -6,6 +6,8 @@
     
     namespace Magento\Paypal\Model;
     
    +use Magento\Csp\Helper\CspNonceProvider;
    +use Magento\Framework\App\ObjectManager;
     use Magento\Payment\Helper\Formatter;
     
     /**
    @@ -14,6 +16,7 @@
      * Works with PayPal-specific system configuration
      * @SuppressWarnings(PHPMD.ExcessivePublicCount)
      * @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
    + * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
      */
     class Config extends AbstractConfig
     {
    @@ -599,21 +602,28 @@ class Config extends AbstractConfig
          */
         protected $_certFactory;
     
    +    /**
    +     * @var CspNonceProvider
    +     */
    +    protected $cspNonceProvider;
    +
         /**
          * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig
          * @param \Magento\Directory\Helper\Data $directoryHelper
          * @param \Magento\Store\Model\StoreManagerInterface $storeManager
          * @param \Magento\Payment\Model\Source\CctypeFactory $cctypeFactory
          * @param CertFactory $certFactory
          * @param array $params
    +     * @param CspNonceProvider|null $cspNonceProvider
          */
         public function __construct(
             \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig,
             \Magento\Directory\Helper\Data $directoryHelper,
             \Magento\Store\Model\StoreManagerInterface $storeManager,
             \Magento\Payment\Model\Source\CctypeFactory $cctypeFactory,
             \Magento\Paypal\Model\CertFactory $certFactory,
    -        $params = []
    +        $params = [],
    +        CspNonceProvider $cspNonceProvider = null
         ) {
             parent::__construct($scopeConfig);
             $this->directoryHelper = $directoryHelper;
    @@ -628,6 +638,8 @@ public function __construct(
                     $this->setStoreId($storeId);
                 }
             }
    +
    +        $this->cspNonceProvider = $cspNonceProvider ?: ObjectManager::getInstance()->get(CspNonceProvider::class);
         }
     
         /**
    @@ -1845,4 +1857,15 @@ public function getPayLaterConfigValue($fieldName)
                 $this->_storeId
             );
         }
    +
    +    /**
    +     * Get a cps nonce for the current request
    +     *
    +     * @return string
    +     * @throws \Magento\Framework\Exception\LocalizedException
    +     */
    +    public function getCspNonce(): string
    +    {
    +        return $this->cspNonceProvider->generateNonce();
    +    }
     }
    
  • app/code/Magento/Paypal/Model/SmartButtonConfig.php+2 2 modified
    @@ -11,7 +11,6 @@
     use Magento\Framework\App\Config\ScopeConfigInterface;
     use Magento\Framework\Locale\ResolverInterface;
     use Magento\Store\Model\ScopeInterface;
    -use Magento\Store\Model\StoreManagerInterface;
     use Magento\Paypal\Model\Config as PaypalConfig;
     
     /**
    @@ -92,7 +91,8 @@ public function getConfig(string $page): array
                 'isGuestCheckoutAllowed'  => $isGuestCheckoutAllowed,
                 'sdkUrl' => $this->sdkUrl->getUrl(),
                 'dataAttributes' => [
    -                'data-partner-attribution-id' => $this->paypalConfig->getBuildNotationCode()
    +                'data-partner-attribution-id' => $this->paypalConfig->getBuildNotationCode(),
    +                'data-csp-nonce' => $this->paypalConfig->getCspNonce(),
                 ]
             ];
         }
    
  • app/code/Magento/Paypal/Test/Mftf/Test/AdminConfigPaymentsConflictResolutionForPayPalTest/AdminConfigPaymentsConflictResolutionForPayPalInFranceTest.xml+1 0 modified
    @@ -16,6 +16,7 @@
                 <severity value="MAJOR"/>
                 <testCaseId value="MC-16675"/>
                 <group value="paypal"/>
    +            <group value="pr_exclude"/>
             </annotations>
             <selectOption selector="{{PaymentsConfigSection.merchantCountry}}" userInput="France" stepKey="setMerchantCountry"/>
             <actionGroup ref="EnablePayPalConfigurationActionGroup" stepKey="EnableWPSExpress">
    
  • app/code/Magento/Paypal/Test/Unit/Model/_files/expected_style_config.php+8 4 modified
    @@ -29,7 +29,8 @@
                 'isGuestCheckoutAllowed' => true,
                 'sdkUrl' => 'http://mock.url',
                 'dataAttributes' => [
    -                'data-partner-attribution-id' => ''
    +                'data-partner-attribution-id' => '',
    +                'data-csp-nonce' => ''
                 ]
             ]
         ],
    @@ -56,7 +57,8 @@
                 'isGuestCheckoutAllowed' => true,
                 'sdkUrl' => 'http://mock.url',
                 'dataAttributes' => [
    -                'data-partner-attribution-id' => ''
    +                'data-partner-attribution-id' => '',
    +                'data-csp-nonce' => ''
                 ]
             ]
         ],
    @@ -82,7 +84,8 @@
                 'isGuestCheckoutAllowed' => true,
                 'sdkUrl' => 'http://mock.url',
                 'dataAttributes' => [
    -                'data-partner-attribution-id' => ''
    +                'data-partner-attribution-id' => '',
    +                'data-csp-nonce' => ''
                 ]
             ]
         ],
    @@ -108,7 +111,8 @@
                 'isGuestCheckoutAllowed' => true,
                 'sdkUrl' => 'http://mock.url',
                 'dataAttributes' => [
    -                'data-partner-attribution-id' => ''
    +                'data-partner-attribution-id' => '',
    +                'data-csp-nonce' => ''
                 ]
             ]
         ],
    
  • app/code/Magento/Persistent/composer.json+14 12 modified
    @@ -1,24 +1,25 @@
     {
         "name": "magento/module-persistent",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.5-p5",
         "require": {
             "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    -        "magento/module-checkout": "*",
    -        "magento/module-cron": "*",
    -        "magento/module-customer": "*",
    -        "magento/module-page-cache": "*",
    -        "magento/module-quote": "*",
    -        "magento/module-store": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-checkout": "100.4.*",
    +        "magento/module-cron": "100.4.*",
    +        "magento/module-customer": "103.0.*",
    +        "magento/module-page-cache": "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"
    @@ -28,3 +29,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Persistent/Test/Mftf/Test/StorefrontVerifyShoppingCartPersistenceUnderLongTermCookieTest.xml+1 0 modified
    @@ -152,6 +152,7 @@
             <actionGroup ref="AssertStorefrontCustomerLogoutSuccessPageActionGroup" stepKey="seeLogoutSuccessPageUrlAfterLogOutJohnSmithCustomerSecondTime"/>
             <waitForPageLoad stepKey="waitForHomePageLoadAfter5Seconds"/>
             <waitForText selector="{{StorefrontCMSPageSection.mainContent}}" userInput="CMS homepage content goes here." stepKey="waitForLoadMainContentMessageOnHomePage"/>
    +        <waitForElementVisible selector="{{StorefrontPanelHeaderSection.notYouLink}}" stepKey="waitForNotYouLink"/>
             <click selector="{{StorefrontPanelHeaderSection.notYouLink}}" stepKey="clickOnNotYouLink" />
             <waitForPageLoad stepKey="waitForCustomerLoginPageLoad"/>
             <actionGroup ref="AssertMiniCartEmptyActionGroup" stepKey="assertMiniCartEmptyAfterJohnDoeSignOut" />
    
  • app/code/Magento/ProductAlert/composer.json+17 15 modified
    @@ -1,29 +1,30 @@
     {
         "name": "magento/module-product-alert",
         "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/framework-bulk": "*",
    -        "magento/module-asynchronous-operations": "*",
    -        "magento/module-authorization": "*",
    -        "magento/module-backend": "*",
    -        "magento/module-catalog": "*",
    -        "magento/module-customer": "*",
    -        "magento/module-store": "*",
    -        "magento/module-theme": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/framework-bulk": "101.0.*",
    +        "magento/module-asynchronous-operations": "100.4.*",
    +        "magento/module-authorization": "100.4.*",
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-customer": "103.0.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/module-theme": "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"
    @@ -33,3 +34,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/ProductVideo/composer.json+16 14 modified
    @@ -1,29 +1,30 @@
     {
         "name": "magento/module-product-video",
         "description": "Add Video to Products",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.5",
         "require": {
             "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    +        "magento/framework": "103.0.*",
             "magento/magento-composer-installer": "*",
    -        "magento/module-backend": "*",
    -        "magento/module-catalog": "*",
    -        "magento/module-eav": "*",
    -        "magento/module-media-storage": "*",
    -        "magento/module-store": "*"
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-eav": "102.1.*",
    +        "magento/module-media-storage": "100.4.*",
    +        "magento/module-store": "101.1.*"
         },
         "suggest": {
    -        "magento/module-customer": "*",
    -        "magento/module-config": "*",
    -        "magento/module-theme": "*"
    +        "magento/module-customer": "103.0.*",
    +        "magento/module-config": "101.2.*",
    +        "magento/module-theme": "101.1.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -33,3 +34,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/QuoteAnalytics/composer.json+8 6 modified
    @@ -1,17 +1,18 @@
     {
         "name": "magento/module-quote-analytics",
         "description": "N/A",
    -    "require": {
    -        "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    -        "magento/module-quote": "*",
    -        "magento/module-analytics": "*"
    -    },
         "type": "magento2-module",
         "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-quote": "101.2.*",
    +        "magento/module-analytics": "100.4.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -21,3 +22,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/QuoteBundleOptions/composer.json+7 5 modified
    @@ -1,16 +1,17 @@
     {
         "name": "magento/module-quote-bundle-options",
         "description": "Magento module provides data provider for creating buy request for bundle products",
    -    "require": {
    -        "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    -        "magento/module-quote": "*"
    -    },
         "type": "magento2-module",
         "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-quote": "101.2.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -20,3 +21,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Quote/composer.json+23 21 modified
    @@ -1,35 +1,36 @@
     {
         "name": "magento/module-quote",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "101.2.5-p8",
         "require": {
             "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    -        "magento/module-authorization": "*",
    -        "magento/module-backend": "*",
    -        "magento/module-catalog": "*",
    -        "magento/module-catalog-inventory": "*",
    -        "magento/module-checkout": "*",
    -        "magento/module-customer": "*",
    -        "magento/module-directory": "*",
    -        "magento/module-eav": "*",
    -        "magento/module-payment": "*",
    -        "magento/module-sales": "*",
    -        "magento/module-sales-sequence": "*",
    -        "magento/module-shipping": "*",
    -        "magento/module-store": "*",
    -        "magento/module-tax": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-authorization": "100.4.*",
    +        "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-directory": "100.4.*",
    +        "magento/module-eav": "102.1.*",
    +        "magento/module-payment": "100.4.*",
    +        "magento/module-sales": "103.0.*",
    +        "magento/module-sales-sequence": "100.4.*",
    +        "magento/module-shipping": "100.4.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/module-tax": "100.4.*"
         },
         "suggest": {
    -        "magento/module-webapi": "*"
    +        "magento/module-webapi": "100.4.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -39,3 +40,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/QuoteConfigurableOptions/composer.json+7 5 modified
    @@ -1,16 +1,17 @@
     {
         "name": "magento/module-quote-configurable-options",
         "description": "Magento module provides data provider for creating buy request for configurable products",
    -    "require": {
    -        "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    -        "magento/module-quote": "*"
    -    },
         "type": "magento2-module",
         "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-quote": "101.2.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -20,3 +21,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/QuoteDownloadableLinks/composer.json+7 5 modified
    @@ -1,16 +1,17 @@
     {
         "name": "magento/module-quote-downloadable-links",
         "description": "Magento module provides data provider for creating buy request for links of downloadable products",
    -    "require": {
    -        "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    -        "magento/module-quote": "*"
    -    },
         "type": "magento2-module",
         "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-quote": "101.2.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -20,3 +21,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Quote/etc/webapi_rest/di.xml+3 2 modified
    @@ -18,8 +18,9 @@
         </type>
         <type name="Magento\Quote\Model\Quote">
             <plugin name="updateQuoteStoreId" type="Magento\Quote\Model\Quote\Plugin\UpdateQuoteStoreId" />
    +        <plugin name="validateQuoteAddress" type="Magento\Quote\Plugin\QuoteAddress" />
         </type>
    -    <type name="Magento\Webapi\Controller\Rest\ParamsOverrider">
    -        <plugin name="validateQuoteData" type="Magento\Quote\Plugin\Webapi\Controller\Rest\ValidateQuoteData" sortOrder="1" disabled="false" />
    +    <type name="Magento\Quote\Api\CartRepositoryInterface">
    +        <plugin name="quoteValidateOrderId" type="Magento\Quote\Plugin\ValidateQuoteOrigOrder"/>
         </type>
     </config>
    
  • app/code/Magento/QuoteGraphQl/composer.json+21 19 modified
    @@ -2,30 +2,31 @@
         "name": "magento/module-quote-graph-ql",
         "description": "N/A",
         "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
    +    "version": "100.4.5-p3",
         "require": {
             "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    -        "magento/module-quote": "*",
    -        "magento/module-checkout": "*",
    -        "magento/module-catalog": "*",
    -        "magento/module-store": "*",
    -        "magento/module-customer": "*",
    -        "magento/module-customer-graph-ql": "*",
    -        "magento/module-sales": "*",
    -        "magento/module-directory": "*",
    -        "magento/module-graph-ql": "*",
    -        "magento/module-gift-message": "*",
    -        "magento/module-catalog-inventory": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-quote": "101.2.*",
    +        "magento/module-checkout": "100.4.*",
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/module-customer": "103.0.*",
    +        "magento/module-customer-graph-ql": "100.4.*",
    +        "magento/module-sales": "103.0.*",
    +        "magento/module-directory": "100.4.*",
    +        "magento/module-graph-ql": "100.4.*",
    +        "magento/module-gift-message": "100.4.*",
    +        "magento/module-catalog-inventory": "100.4.*"
         },
         "suggest": {
    -        "magento/module-graph-ql-cache": "*",
    -        "magento/module-catalog-inventory-graph-ql": "*",
    -        "magento/module-payment-graph-ql": "*"
    +        "magento/module-graph-ql-cache": "100.4.*",
    +        "magento/module-catalog-inventory-graph-ql": "100.4.*",
    +        "magento/module-payment-graph-ql": "100.4.*"
         },
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -35,3 +36,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/QuoteGraphQl/Model/Resolver/RemoveItemFromCart.php+2 1 modified
    @@ -86,6 +86,8 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value
             $itemId = $processedArgs['input']['cart_item_id'];
     
             $storeId = (int)$context->getExtensionAttributes()->getStore()->getId();
    +        /** Check if the current user is allowed to perform actions with the cart */
    +        $cart = $this->getCartForUser->execute($maskedCartId, $context->getUserId(), $storeId);
     
             try {
                 $this->cartItemRepository->deleteById($cartId, $itemId);
    @@ -95,7 +97,6 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value
                 throw new GraphQlInputException(__($e->getMessage()), $e);
             }
     
    -        $cart = $this->getCartForUser->execute($maskedCartId, $context->getUserId(), $storeId);
             return [
                 'cart' => [
                     'model' => $cart,
    
  • app/code/Magento/Quote/i18n/en_US.csv+3 0 modified
    @@ -69,3 +69,6 @@ Carts,Carts
     "Validated Country Code","Validated Country Code"
     "Validated Vat Number","Validated Vat Number"
     "Invalid Quote Item id %1","Invalid Quote Item id %1"
    +"Invalid quote address id %1","Invalid quote address id %1"
    +"Please check input parameters.","Please check input parameters."
    +
    
  • app/code/Magento/Quote/Model/BillingAddressManagement.php+8 6 modified
    @@ -3,14 +3,15 @@
      * Copyright © Magento, Inc. All rights reserved.
      * See COPYING.txt for license details.
      */
    +declare(strict_types=1);
     
     namespace Magento\Quote\Model;
     
    +use Magento\Framework\App\ObjectManager;
     use Magento\Framework\Exception\InputException;
    -use Magento\Quote\Model\Quote\Address\BillingAddressPersister;
    -use Psr\Log\LoggerInterface as Logger;
     use Magento\Quote\Api\BillingAddressManagementInterface;
    -use Magento\Framework\App\ObjectManager;
    +use Magento\Quote\Api\Data\AddressInterface;
    +use Psr\Log\LoggerInterface as Logger;
     
     /**
      * Quote billing address write service object.
    @@ -25,14 +26,14 @@ class BillingAddressManagement implements BillingAddressManagementInterface
         protected $addressValidator;
     
         /**
    -     * Logger.
    +     * Logger object.
          *
          * @var Logger
          */
         protected $logger;
     
         /**
    -     * Quote repository.
    +     * Quote repository object.
          *
          * @var \Magento\Quote\Api\CartRepositoryInterface
          */
    @@ -72,7 +73,7 @@ public function __construct(
          * @inheritdoc
          * @SuppressWarnings(PHPMD.NPathComplexity)
          */
    -    public function assign($cartId, \Magento\Quote\Api\Data\AddressInterface $address, $useForShipping = false)
    +    public function assign($cartId, AddressInterface $address, $useForShipping = false)
         {
             /** @var \Magento\Quote\Model\Quote $quote */
             $quote = $this->quoteRepository->getActive($cartId);
    @@ -104,6 +105,7 @@ public function get($cartId)
          *
          * @return \Magento\Quote\Model\ShippingAddressAssignment
          * @deprecated 101.0.0
    +     * @see \Magento\Quote\Model\Quote\Address
          */
         private function getShippingAddressAssignment()
         {
    
  • app/code/Magento/Quote/Model/QuoteAddressValidator.php+55 29 modified
    @@ -3,8 +3,15 @@
      * Copyright © Magento, Inc. All rights reserved.
      * See COPYING.txt for license details.
      */
    +declare(strict_types=1);
    +
     namespace Magento\Quote\Model;
     
    +use Magento\Customer\Api\AddressRepositoryInterface;
    +use Magento\Customer\Api\CustomerRepositoryInterface;
    +use Magento\Customer\Model\Session;
    +use Magento\Framework\Exception\InputException;
    +use Magento\Framework\Exception\LocalizedException;
     use Magento\Framework\Exception\NoSuchEntityException;
     use Magento\Quote\Api\Data\AddressInterface;
     use Magento\Quote\Api\Data\CartInterface;
    @@ -17,35 +24,33 @@
     class QuoteAddressValidator
     {
         /**
    -     * Address factory.
    -     *
    -     * @var \Magento\Customer\Api\AddressRepositoryInterface
    +     * @var AddressRepositoryInterface
          */
    -    protected $addressRepository;
    +    protected AddressRepositoryInterface $addressRepository;
     
         /**
    -     * Customer repository.
    -     *
    -     * @var \Magento\Customer\Api\CustomerRepositoryInterface
    +     * @var CustomerRepositoryInterface
          */
    -    protected $customerRepository;
    +    protected CustomerRepositoryInterface $customerRepository;
     
         /**
    +     * @var Session
          * @deprecated 101.1.1 This class is not a part of HTML presentation layer and should not use sessions.
    +     * @see Session
          */
    -    protected $customerSession;
    +    protected Session $customerSession;
     
         /**
          * Constructs a quote shipping address validator service object.
          *
    -     * @param \Magento\Customer\Api\AddressRepositoryInterface $addressRepository
    -     * @param \Magento\Customer\Api\CustomerRepositoryInterface $customerRepository Customer repository.
    -     * @param \Magento\Customer\Model\Session $customerSession
    +     * @param AddressRepositoryInterface $addressRepository
    +     * @param CustomerRepositoryInterface $customerRepository Customer repository.
    +     * @param Session $customerSession
          */
         public function __construct(
    -        \Magento\Customer\Api\AddressRepositoryInterface $addressRepository,
    -        \Magento\Customer\Api\CustomerRepositoryInterface $customerRepository,
    -        \Magento\Customer\Model\Session $customerSession
    +        AddressRepositoryInterface $addressRepository,
    +        CustomerRepositoryInterface $customerRepository,
    +        Session $customerSession
         ) {
             $this->addressRepository = $addressRepository;
             $this->customerRepository = $customerRepository;
    @@ -56,18 +61,18 @@ public function __construct(
          * Validate address.
          *
          * @param AddressInterface $address
    -     * @param int|null $customerId Cart belongs to
    +     * @param int|null $customerId
          * @return void
    -     * @throws \Magento\Framework\Exception\InputException The specified address belongs to another customer.
    -     * @throws \Magento\Framework\Exception\NoSuchEntityException The specified customer ID or address ID is not valid.
    +     * @throws LocalizedException The specified customer ID or address ID is not valid.
    +     * @throws NoSuchEntityException The specified customer ID or address ID is not valid.
          */
         private function doValidate(AddressInterface $address, ?int $customerId): void
         {
             //validate customer id
             if ($customerId) {
                 $customer = $this->customerRepository->getById($customerId);
                 if (!$customer->getId()) {
    -                throw new \Magento\Framework\Exception\NoSuchEntityException(
    +                throw new NoSuchEntityException(
                         __('Invalid customer id %1', $customerId)
                     );
                 }
    @@ -76,15 +81,15 @@ private function doValidate(AddressInterface $address, ?int $customerId): void
             if ($address->getCustomerAddressId()) {
                 //Existing address cannot belong to a guest
                 if (!$customerId) {
    -                throw new \Magento\Framework\Exception\NoSuchEntityException(
    +                throw new NoSuchEntityException(
                         __('Invalid customer address id %1', $address->getCustomerAddressId())
                     );
                 }
                 //Validating address ID
                 try {
                     $this->addressRepository->getById($address->getCustomerAddressId());
                 } catch (NoSuchEntityException $e) {
    -                throw new \Magento\Framework\Exception\NoSuchEntityException(
    +                throw new NoSuchEntityException(
                         __('Invalid address id %1', $address->getId())
                     );
                 }
    @@ -94,7 +99,7 @@ private function doValidate(AddressInterface $address, ?int $customerId): void
                     return $address->getId();
                 }, $this->customerRepository->getById($customerId)->getAddresses());
                 if (!in_array($address->getCustomerAddressId(), $applicableAddressIds)) {
    -                throw new \Magento\Framework\Exception\NoSuchEntityException(
    +                throw new NoSuchEntityException(
                         __('Invalid customer address id %1', $address->getCustomerAddressId())
                     );
                 }
    @@ -104,12 +109,12 @@ private function doValidate(AddressInterface $address, ?int $customerId): void
         /**
          * Validates the fields in a specified address data object.
          *
    -     * @param \Magento\Quote\Api\Data\AddressInterface $addressData The address data object.
    +     * @param AddressInterface $addressData The address data object.
          * @return bool
    -     * @throws \Magento\Framework\Exception\InputException The specified address belongs to another customer.
    -     * @throws \Magento\Framework\Exception\NoSuchEntityException The specified customer ID or address ID is not valid.
    +     * @throws InputException The specified address belongs to another customer.
    +     * @throws NoSuchEntityException|LocalizedException The specified customer ID or address ID is not valid.
          */
    -    public function validate(AddressInterface $addressData)
    +    public function validate(AddressInterface $addressData): bool
         {
             $this->doValidate($addressData, $addressData->getCustomerId());
     
    @@ -122,11 +127,32 @@ public function validate(AddressInterface $addressData)
          * @param CartInterface $cart
          * @param AddressInterface $address
          * @return void
    -     * @throws \Magento\Framework\Exception\InputException The specified address belongs to another customer.
    -     * @throws \Magento\Framework\Exception\NoSuchEntityException The specified customer ID or address ID is not valid.
    +     * @throws InputException The specified address belongs to another customer.
    +     * @throws NoSuchEntityException|LocalizedException The specified customer ID or address ID is not valid.
          */
         public function validateForCart(CartInterface $cart, AddressInterface $address): void
         {
    -        $this->doValidate($address, $cart->getCustomerIsGuest() ? null : $cart->getCustomer()->getId());
    +        $this->doValidate($address, $cart->getCustomerIsGuest() ? null : (int) $cart->getCustomer()->getId());
    +    }
    +
    +    /**
    +     * Validate address id to be used for cart.
    +     *
    +     * @param CartInterface $cart
    +     * @param AddressInterface $address
    +     * @return void
    +     * @throws NoSuchEntityException The specified customer ID or address ID is not valid.
    +     */
    +    public function validateWithExistingAddress(CartInterface $cart, AddressInterface $address): void
    +    {
    +        // check if address belongs to quote.
    +        if ($address->getId() !== null) {
    +            $old = $cart->getAddressById($address->getId());
    +            if (empty($old)) {
    +                throw new NoSuchEntityException(
    +                    __('Invalid quote address id %1', $address->getId())
    +                );
    +            }
    +        }
         }
     }
    
  • app/code/Magento/Quote/Plugin/QuoteAddress.php+67 0 added
    @@ -0,0 +1,67 @@
    +<?php
    +/**
    + * Copyright © Magento, Inc. All rights reserved.
    + * See COPYING.txt for license details.
    + */
    +declare(strict_types=1);
    +
    +namespace Magento\Quote\Plugin;
    +
    +use Magento\Framework\Exception\NoSuchEntityException;
    +use Magento\Quote\Model\Quote;
    +use Magento\Quote\Api\Data\AddressInterface;
    +use Magento\Quote\Model\QuoteAddressValidator;
    +
    +/**
    + * Quote address plugin
    + */
    +class QuoteAddress
    +{
    +    /**
    +     * @var QuoteAddressValidator
    +     */
    +    protected QuoteAddressValidator $addressValidator;
    +
    +    /**
    +     * @param QuoteAddressValidator $addressValidator
    +     */
    +    public function __construct(
    +        QuoteAddressValidator $addressValidator
    +    ) {
    +        $this->addressValidator = $addressValidator;
    +    }
    +
    +    /**
    +     * Validate address before setting billing address
    +     *
    +     * @param Quote $subject
    +     * @param AddressInterface|null $address
    +     * @return array
    +     * @throws NoSuchEntityException
    +     */
    +    public function beforeSetBillingAddress(Quote $subject, AddressInterface $address = null): array
    +    {
    +        if ($address !== null) {
    +            $this->addressValidator->validateWithExistingAddress($subject, $address);
    +        }
    +
    +        return [$address];
    +    }
    +
    +    /**
    +     * Validate address before setting shipping address
    +     *
    +     * @param Quote $subject
    +     * @param AddressInterface|null $address
    +     * @return array
    +     * @throws NoSuchEntityException
    +     */
    +    public function beforeSetShippingAddress(Quote $subject, AddressInterface $address = null): array
    +    {
    +        if ($address !== null) {
    +            $this->addressValidator->validateWithExistingAddress($subject, $address);
    +        }
    +
    +        return [$address];
    +    }
    +}
    
  • app/code/Magento/Quote/Plugin/ValidateQuoteOrigOrder.php+65 0 added
    @@ -0,0 +1,65 @@
    +<?php
    +/**
    + * Copyright © Magento, Inc. All rights reserved.
    + * See COPYING.txt for license details.
    + */
    +
    +declare(strict_types=1);
    +
    +namespace Magento\Quote\Plugin;
    +
    +use Magento\Framework\Exception\NoSuchEntityException;
    +use Magento\Framework\Webapi\Rest\Request as RestRequest;
    +use Magento\Quote\Api\CartRepositoryInterface;
    +use Magento\Quote\Api\Data\CartInterface;
    +use Magento\Sales\Api\OrderRepositoryInterface;
    +
    +/**
    + * Validate order id from request param
    + */
    +class ValidateQuoteOrigOrder
    +{
    +    /**
    +     * @var OrderRepositoryInterface
    +     */
    +    private $orderRepository;
    +
    +    /**
    +     * @var RestRequest $request
    +     */
    +    private $request;
    +
    +    /**
    +     * @param RestRequest $request
    +     * @param OrderRepositoryInterface $orderRepository
    +     */
    +    public function __construct(RestRequest $request, OrderRepositoryInterface $orderRepository)
    +    {
    +        $this->request = $request;
    +        $this->orderRepository = $orderRepository;
    +    }
    +
    +    /**
    +     * Validate the user authorization to order
    +     *
    +     * @param CartRepositoryInterface $cartRepository
    +     * @param CartInterface $quote
    +     * @return void
    +     * @throws NoSuchEntityException
    +     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
    +     */
    +    public function beforeSave(
    +        CartRepositoryInterface $cartRepository,
    +        CartInterface $quote
    +    ): void {
    +        $params = $this->request->getBodyParams();
    +        if (!empty($params) && isset($params['quote']['orig_order_id'])) {
    +            $orderId = $params['quote']['orig_order_id'];
    +            $order = $this->orderRepository->get($orderId);
    +            $orderCustomer = (int)$order->getCustomerId();
    +            if ($quote->getCustomerId() !== $orderCustomer) {
    +                throw new NoSuchEntityException(__('Please check input parameters.'));
    +            }
    +        }
    +    }
    +}
    
  • app/code/Magento/Quote/Plugin/Webapi/Controller/Rest/ValidateQuoteData.php+0 56 removed
    @@ -1,56 +0,0 @@
    -<?php
    -/**
    - * Copyright © Magento, Inc. All rights reserved.
    - * See COPYING.txt for license details.
    - */
    -declare(strict_types=1);
    -namespace Magento\Quote\Plugin\Webapi\Controller\Rest;
    -
    -use Magento\Webapi\Controller\Rest\ParamsOverrider;
    -
    -/**
    - * Validates Quote Data
    - */
    -class ValidateQuoteData
    -{
    -    private const QUOTE_KEY = 'quote';
    -
    -    /**
    -     * Before Overriding to validate data
    -     *
    -     * @param ParamsOverrider $subject
    -     * @param array $inputData
    -     * @param array $parameters
    -     * @return array[]
    -     *
    -     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
    -     */
    -
    -    public function beforeOverride(ParamsOverrider $subject, array $inputData, array $parameters): array
    -    {
    -        if (isset($inputData[self:: QUOTE_KEY])) {
    -            $inputData[self:: QUOTE_KEY] = $this->validateInputData($inputData[self:: QUOTE_KEY]);
    -        };
    -        return [$inputData, $parameters];
    -    }
    -
    -    /**
    -     * Validates InputData
    -     *
    -     * @param array $inputData
    -     * @return array
    -     */
    -    private function validateInputData(array $inputData): array
    -    {
    -        $result = [];
    -
    -        $data = array_filter($inputData, function ($k) use (&$result) {
    -            $key = is_string($k) ? strtolower($k) : $k;
    -            return !isset($result[$key]) && ($result[$key] = true);
    -        }, ARRAY_FILTER_USE_KEY);
    -
    -        return array_map(function ($value) {
    -            return is_array($value) ? $this->validateInputData($value) : $value;
    -        }, $data);
    -    }
    -}
    
  • app/code/Magento/Quote/Test/Unit/Plugin/Webapi/Controller/Rest/ValidateQuoteDataTest.php+0 113 removed
    @@ -1,113 +0,0 @@
    -<?php
    -/**
    - * Copyright © Magento, Inc. All rights reserved.
    - * See COPYING.txt for license details.
    - */
    -
    -namespace Magento\Quote\Test\Unit\Plugin\Webapi\Controller\Rest;
    -
    -use Exception;
    -use Magento\Framework\App\ObjectManager;
    -use Magento\Quote\Plugin\Webapi\Controller\Rest\ValidateQuoteData;
    -use PHPUnit\Framework\TestCase;
    -use ReflectionClass;
    -
    -/**
    - * Unit test for ValidateQuoteData plugin
    - */
    -class ValidateQuoteDataTest extends TestCase
    -{
    -
    -    /**
    -     * @var ValidateQuoteData
    -     */
    -    private $validateQuoteDataObject;
    -
    -    /**
    -     * @var ReflectionClass
    -     *
    -     */
    -    private $reflectionObject;
    -
    -    /**
    -     * @inheritdoc
    -     */
    -    protected function setUp(): void
    -    {
    -        $this->validateQuoteDataObject = ObjectManager::getInstance()->get(ValidateQuoteData::class);
    -        $this->reflectionObject = new ReflectionClass(get_class($this->validateQuoteDataObject));
    -    }
    -    /**
    -     * Test if the quote array is valid
    -     *
    -     * @param array $array
    -     * @param array $result
    -     * @dataProvider dataProviderInputData
    -     * @throws Exception
    -     */
    -    public function testValidateInputData(array $array, array $result)
    -    {
    -        $this->assertEquals(
    -            $result,
    -            $this->invokeValidateInputData('validateInputData', [$array])
    -        );
    -    }
    -
    -    /**
    -     * @param string $methodName
    -     * @param array $arguments
    -     * @return mixed
    -     * @throws Exception
    -     */
    -    private function invokeValidateInputData(string $methodName, array $arguments = [])
    -    {
    -        $validateInputDataMethod = $this->reflectionObject->getMethod($methodName);
    -        $validateInputDataMethod->setAccessible(true);
    -        return $validateInputDataMethod->invokeArgs($this->validateQuoteDataObject, $arguments);
    -    }
    -
    -    /**
    -     * @return array
    -     */
    -    public function dataProviderInputData(): array
    -    {
    -        return [
    -            [
    -                ['person' =>
    -                    [
    -                        'id' => -1,
    -                        'Id' => 1,
    -                        'name' =>
    -                            [
    -                                'firstName' => 'John',
    -                                'LastName' => 'S'
    -                            ],
    -                        'isHavingVehicle' => 1,
    -                        'address' =>
    -                            [
    -                                'street' => '4th Street',
    -                                'Street' => '2nd Street',
    -                                'city' => 'Atlanta'
    -                            ],
    -                    ]
    -                ],
    -                ['person' =>
    -                    [
    -                        'id' => -1,
    -                        'name' =>
    -                            [
    -                                'firstName' => 'John',
    -                                'LastName' => 'S'
    -                            ],
    -                        'isHavingVehicle' => 1,
    -                        'address' =>
    -                            [
    -                                'street' => '4th Street',
    -                                'city' => 'Atlanta'
    -                            ],
    -                    ]
    -                ],
    -            ]
    -        ];
    -    }
    -}
    
30877fce83b7

Magento Release 2.4.4-p9

https://github.com/magento/magento2magento packaging serviceJun 6, 2024via ghsa
300 files changed · +6195 2388
  • app/code/Magento/AdminAnalytics/composer.json+14 11 modified
    @@ -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 @@
             }
         }
     }
    +
    
  • app/code/Magento/AdminAnalytics/view/adminhtml/templates/tracking.phtml+17 9 modified
    @@ -6,6 +6,7 @@
     
     /**
      * @var \Magento\Framework\View\Helper\SecureHtmlRenderer $secureRenderer
    + * @var \Magento\Framework\Escaper $escaper
      */
     ?>
     
    @@ -22,18 +23,25 @@
     <?php
     /** @var \Magento\AdminAnalytics\ViewModel\Metadata $metadata */
     $metadata = $block->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 . '";
     ';
     ?>
     <?= /* @noEscape */ $secureRenderer->renderTag('script', [], $scriptString, false); ?>
    
  • app/code/Magento/AdminAnalytics/ViewModel/Metadata.php+29 1 modified
    @@ -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;
    +    }
     }
    
  • app/code/Magento/AdminNotification/composer.json+13 11 modified
    @@ -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",
         "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 @@
             }
         }
     }
    +
    
  • app/code/Magento/AdvancedPricingImportExport/composer.json+16 14 modified
    @@ -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 @@
             }
         }
     }
    +
    
  • app/code/Magento/AdvancedSearch/composer.json+15 13 modified
    @@ -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",
         "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 @@
             }
         }
     }
    +
    
  • app/code/Magento/Amqp/composer.json+10 8 modified
    @@ -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 @@
             }
         }
     }
    +
    
  • app/code/Magento/AmqpStore/composer.json+12 10 modified
    @@ -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 @@
             }
         }
     }
    +
    
  • app/code/Magento/Analytics/composer.json+10 8 modified
    @@ -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 @@
             }
         }
     }
    +
    
  • app/code/Magento/Analytics/Test/Mftf/ActionGroup/AssertAdminAdvancedReportingPageUrlActionGroup.xml+1 1 modified
    @@ -15,6 +15,6 @@
     
             <switchToNextTab stepKey="switchToNewTab"/>
             <waitForPageLoad stepKey="waitForAdvancedReportingPageLoad"/>
    -        <seeInCurrentUrl url="reports/advanced-reporting" stepKey="seeAssertAdvancedReportingPageUrl"/>
    +        <seeInCurrentUrl url="report" stepKey="seeAssertAdvancedReportingPageUrl"/>
         </actionGroup>
     </actionGroups>
    
  • app/code/Magento/AsynchronousOperations/composer.json+14 12 modified
    @@ -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 @@
             }
         }
     }
    +
    
  • app/code/Magento/Authorization/composer.json+9 7 modified
    @@ -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 @@
             }
         }
     }
    +
    
  • app/code/Magento/AwsS3/composer.json+8 6 modified
    @@ -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 @@
             }
         }
     }
    +
    
  • app/code/Magento/Backend/Block/System/Store/Grid/Render/Group.php+7 3 modified
    @@ -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())) .
             '</a><br />'
    -        . '(' . __('Code') . ': ' . $row->getGroupCode() . ')';
    +            . '(' . __('Code') . ': ' . $this->escapeHtml($row->getGroupCode()) . ')';
         }
     }
    
  • app/code/Magento/Backend/composer.json+26 24 modified
    @@ -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-p9",
         "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 @@
             }
         }
     }
    +
    
  • app/code/Magento/Backend/etc/adminhtml/system.xml+3 0 modified
    @@ -10,6 +10,9 @@
             <tab id="general" translate="label" sortOrder="100">
                 <label>General</label>
             </tab>
    +        <tab id="security" translate="label" sortOrder="200">
    +            <label>Security</label>
    +        </tab>
             <tab id="service" translate="label" sortOrder="99999">
                 <label>Services</label>
             </tab>
    
  • app/code/Magento/Backup/composer.json+11 9 modified
    @@ -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 @@
             }
         }
     }
    +
    
  • app/code/Magento/Bundle/composer.json+27 25 modified
    @@ -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 @@
             }
         }
     }
    +
    
  • app/code/Magento/BundleGraphQl/composer.json+15 13 modified
    @@ -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 @@
             }
         }
     }
    +
    
  • app/code/Magento/BundleImportExport/composer.json+14 12 modified
    @@ -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 @@
             }
         }
     }
    +
    
  • app/code/Magento/Bundle/Test/Mftf/ActionGroup/StoreFrontAddProductToCartFromBundleActionGroup.xml+4 0 modified
    @@ -17,11 +17,15 @@
                 <argument name="currency" type="string" defaultValue="US Dollar"/>
             </arguments>
     
    +        <waitForElementVisible selector="{{StorefrontBundledSection.currencyTrigger}}" stepKey="waitForCurrencyTriggerVisible"/>
             <click selector="{{StorefrontBundledSection.currencyTrigger}}" stepKey="openCurrencyTrigger"/>
    +        <waitForElementVisible selector="{{StorefrontBundledSection.currency(currency)}}" stepKey="waitForChooseCurrencyVisible"/>
             <click selector="{{StorefrontBundledSection.currency(currency)}}" stepKey="chooseCurrency"/>
    +        <waitForElementVisible selector="{{StorefrontBundledSection.addToCart}}" stepKey="waitForCustomizeButtonVisible"/>
             <click selector="{{StorefrontBundledSection.addToCart}}" stepKey="clickCustomize"/>
             <waitForPageLoad stepKey="waitForBundleOpen"/>
             <checkOption selector="{{StorefrontBundledSection.productInBundle(product.name)}}" stepKey="chooseProduct"/>
    +        <waitForElementVisible selector="{{StorefrontBundledSection.addToCartConfigured}}" stepKey="waitForAddToCartButtonVisible"/>
             <click selector="{{StorefrontBundledSection.addToCartConfigured}}" stepKey="addToCartProduct"/>
             <scrollToTopOfPage stepKey="scrollToTop"/>
         </actionGroup>
    
  • app/code/Magento/Bundle/view/frontend/templates/catalog/product/view/type/bundle/option/radio.phtml+28 22 modified
    @@ -4,6 +4,8 @@
      * See COPYING.txt for license details.
      */
     use Magento\Bundle\ViewModel\ValidateQuantity;
    +
    +// phpcs:disable Generic.Files.LineLength.TooLong
     ?>
     <?php /* @var $block \Magento\Bundle\Block\Catalog\Product\View\Type\Bundle\Option\Radio */ ?>
     <?php $_option = $block->getOption(); ?>
    @@ -20,42 +22,45 @@ $viewModel = $block->getData('validateQuantityViewModel');
         </label>
         <div class="control">
             <div class="nested options-list">
    -            <?php if ($block->showSingle()) : ?>
    +            <?php if ($block->showSingle()): ?>
                     <?= /* @noEscape */ $block->getSelectionTitlePrice($_selections[0]) ?>
                     <?= /* @noEscape */ $block->getTierPriceRenderer()->renderTierPrice($_selections[0]) ?>
                     <input type="hidden"
    -                    class="bundle-option-<?= (int)$_option->getId() ?>  product bundle option"
    -                    name="bundle_option[<?= (int)$_option->getId() ?>]"
    -                    value="<?= (int)$_selections[0]->getSelectionId() ?>"
    -                    id="bundle-option-<?= (int)$_option->getId() ?>-<?= (int)$_selections[0]->getSelectionId() ?>"
    -                    checked="checked"
    +                       class="bundle-option-<?= (int)$_option->getId() ?>  product bundle option"
    +                       name="bundle_option[<?= (int)$_option->getId() ?>]"
    +                       value="<?= (int)$_selections[0]->getSelectionId() ?>"
    +                       id="bundle-option-<?= (int)$_option->getId() ?>-<?= (int)$_selections[0]->getSelectionId() ?>"
    +                       checked="checked"
                     />
    -            <?php else :?>
    -                <?php if (!$_option->getRequired()) : ?>
    +            <?php else: ?>
    +                <?php if (!$_option->getRequired()): ?>
                         <div class="field choice">
                             <input type="radio"
                                    class="radio product bundle option"
                                    id="bundle-option-<?= $block->escapeHtmlAttr($_option->getId()) ?>"
                                    name="bundle_option[<?= $block->escapeHtmlAttr($_option->getId()) ?>]"
                                    data-selector="bundle_option[<?= $block->escapeHtmlAttr($_option->getId()) ?>]"
    -                               <?= ($_default && $_default->isSalable())?'':' checked="checked" ' ?>
    +                            <?= ($_default && $_default->isSalable())?'':' checked="checked" ' ?>
                                    value=""/>
                             <label class="label" for="bundle-option-<?= $block->escapeHtmlAttr($_option->getId()) ?>">
                                 <span><?= $block->escapeHtml(__('None')) ?></span>
                             </label>
                         </div>
                     <?php endif; ?>
    -                <?php foreach ($_selections as $_selection) : ?>
    +                <?php foreach ($_selections as $_selection): ?>
                         <div class="field choice">
                             <input type="radio"
                                    class="radio product bundle option change-container-classname"
                                    id="bundle-option-<?= $block->escapeHtmlAttr($_option->getId()) ?>-<?= $block->escapeHtmlAttr($_selection->getSelectionId()) ?>"
    -                               <?php if ($_option->getRequired()) { echo 'data-validate="{\'validate-one-required-by-name\':true}"'; }?>
    +                            <?php if ($_option->getRequired()) {
    +                                echo 'data-validate="{\'validate-one-required-by-name\':true}"';
    +                            } ?>
                                    name="bundle_option[<?= $block->escapeHtmlAttr($_option->getId()) ?>]"
                                    data-selector="bundle_option[<?= $block->escapeHtmlAttr($_option->getId()) ?>]"
    -                               <?php if ($block->isSelected($_selection)) { echo ' checked="checked"'; } ?>
    -                               <?php if (!$_selection->isSaleable()) { echo ' disabled="disabled"'; } ?>
    -                               value="<?= $block->escapeHtmlAttr($_selection->getSelectionId()) ?>"/>
    +                            <?php if ($block->isSelected($_selection)) { echo ' checked="checked"'; } ?>
    +                            <?php if (!$_selection->isSaleable()) { echo ' disabled="disabled"'; } ?>
    +                               value="<?= $block->escapeHtmlAttr($_selection->getSelectionId()) ?>"
    +                               data-errors-message-box="#validation-message-box-radio"/>
                             <label class="label"
                                    for="bundle-option-<?= $block->escapeHtmlAttr($_option->getId()) ?>-<?= $block->escapeHtmlAttr($_selection->getSelectionId()) ?>">
                                 <span><?= /* @noEscape */ $block->getSelectionTitlePrice($_selection) ?></span>
    @@ -65,21 +70,22 @@ $viewModel = $block->getData('validateQuantityViewModel');
                         </div>
                     <?php endforeach; ?>
                     <div id="bundle-option-<?= $block->escapeHtmlAttr($_option->getId()) ?>-container"></div>
    +                <div id="validation-message-box-radio"></div>
                 <?php endif; ?>
                 <div class="field qty qty-holder">
                     <label class="label" for="bundle-option-<?= $block->escapeHtmlAttr($_option->getId()) ?>-qty-input">
                         <span><?= $block->escapeHtml(__('Quantity')) ?></span>
                     </label>
                     <div class="control">
                         <input <?php if (!$_canChangeQty) { echo ' disabled="disabled"'; } ?>
    -                           id="bundle-option-<?= $block->escapeHtmlAttr($_option->getId()) ?>-qty-input"
    -                           class="input-text qty<?php if (!$_canChangeQty) { echo ' qty-disabled'; } ?>"
    -                           type="number"
    -                           min="0"
    -                           data-validate="<?= $block->escapeHtmlAttr($viewModel->getQuantityValidators()) ?>"
    -                           name="bundle_option_qty[<?= $block->escapeHtmlAttr($_option->getId()) ?>]"
    -                           data-selector="bundle_option_qty[<?= $block->escapeHtmlAttr($_option->getId()) ?>]"
    -                           value="<?= $block->escapeHtmlAttr($_defaultQty) ?>"/>
    +                        id="bundle-option-<?= $block->escapeHtmlAttr($_option->getId()) ?>-qty-input"
    +                        class="input-text qty<?php if (!$_canChangeQty) { echo ' qty-disabled'; } ?>"
    +                        type="number"
    +                        min="0"
    +                        data-validate="<?= $block->escapeHtmlAttr($viewModel->getQuantityValidators()) ?>"
    +                        name="bundle_option_qty[<?= $block->escapeHtmlAttr($_option->getId()) ?>]"
    +                        data-selector="bundle_option_qty[<?= $block->escapeHtmlAttr($_option->getId()) ?>]"
    +                        value="<?= $block->escapeHtmlAttr($_defaultQty) ?>"/>
                     </div>
                 </div>
             </div>
    
  • app/code/Magento/CacheInvalidate/composer.json+9 7 modified
    @@ -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 @@
             }
         }
     }
    +
    
  • app/code/Magento/Captcha/composer.json+14 12 modified
    @@ -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 @@
             }
         }
     }
    +
    
  • app/code/Magento/Captcha/Test/Mftf/Test/StorefrontCaptchaOnOnepageCheckoutPyamentTest.xml+2 0 modified
    @@ -21,6 +21,7 @@
                 <group value="storefront_captcha_enabled"/>
             </annotations>
             <before>
    +            <magentoCLI command="config:set checkout/options/enable_guest_checkout_login 1" stepKey="EnablingGuestCheckoutLogin"/>
                 <!-- Create Simple Product -->
                 <createData entity="SimpleProduct2" stepKey="createSimpleProduct">
                     <field key="price">20</field>
    @@ -62,6 +63,7 @@
     
                 <!-- Delete customer -->
                 <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/>
    +            <magentoCLI command="config:set checkout/options/enable_guest_checkout_login 0" stepKey="DisablingGuestCheckoutLogin"/>
             </after>
     
             <!-- Reindex and flush cache -->
    
  • app/code/Magento/CardinalCommerce/composer.json+11 9 modified
    @@ -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 @@
             }
         }
     }
    +
    
  • app/code/Magento/CatalogAnalytics/composer.json+8 6 modified
    @@ -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 @@
             }
         }
     }
    +
    
  • app/code/Magento/CatalogCmsGraphQl/composer.json+12 10 modified
    @@ -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 @@
             }
         }
     }
    +
    
  • app/code/Magento/Catalog/composer.json+36 34 modified
    @@ -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-p7",
         "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 @@
             }
         }
     }
    +
    
  • app/code/Magento/Catalog/Controller/Adminhtml/Product/NewAction.php+21 4 modified
    @@ -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');
             }
    
  • app/code/Magento/CatalogCustomerGraphQl/composer.json+9 7 modified
    @@ -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 @@
             }
         }
     }
    +
    
  • app/code/Magento/CatalogGraphQl/composer.json+19 17 modified
    @@ -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 @@
             }
         }
     }
    +
    
  • app/code/Magento/Catalog/i18n/en_US.csv+2 0 modified
    @@ -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."
    +
     
    
  • app/code/Magento/CatalogImportExport/composer.json+18 16 modified
    @@ -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 @@
             }
         }
     }
    +
    
  • app/code/Magento/CatalogImportExport/Model/Import/Product.php+6 0 modified
    @@ -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);
     
    
  • app/code/Magento/CatalogInventory/composer.json+15 13 modified
    @@ -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"
     }
    +
    
  • app/code/Magento/CatalogInventoryGraphQl/composer.json+10 8 modified
    @@ -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 @@
             }
         }
     }
    +
    
  • app/code/Magento/CatalogRule/composer.json+17 15 modified
    @@ -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 @@
             }
         }
     }
    +
    
  • app/code/Magento/CatalogRuleConfigurable/composer.json+12 10 modified
    @@ -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 @@
             }
         }
     }
    +
    
  • app/code/Magento/CatalogRuleGraphQl/composer.json+8 6 modified
    @@ -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 @@
             }
         }
     }
    +
    
  • app/code/Magento/CatalogSearch/composer.json+20 18 modified
    @@ -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",
         "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 @@
             }
         }
     }
    +
    
  • app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryMainSection.xml+1 0 modified
    @@ -40,5 +40,6 @@
             <element name="productOptionList" type="text" selector="#narrow-by-list"/>
             <element name="productNameByPosition" type="text" selector=".products-grid li:nth-of-type({{position}}) .product-item-name a" parameterized="true"/>
             <element name="sidebarAdditional" type="block" selector="#maincontent .sidebar.sidebar-additional"/>
    +        <element name="searchStore" type="input" selector="//div/input[@id='search']" />
         </section>
     </sections>
    
  • app/code/Magento/Catalog/Test/Mftf/Test/AdminApplyTierPriceToProductTest/AdminApplyTierPriceToProductTest.xml+1 0 modified
    @@ -230,6 +230,7 @@
             <waitForElement selector="{{AdminProductFormAdvancedPricingSection.customerGroupPriceDeleteButton}}" stepKey="waitForcustomerGroupPriceDeleteButton"/>
             <scrollTo selector="(//*[contains(@class, 'product_form_product_form_advanced_pricing_modal')]//tr//button[@data-action='remove_row'])[1]" x="30" y="0" stepKey="scrollToDeleteFirstRowOfCustomerGroupPrice"/>
             <click selector="(//tr//button[@data-action='remove_row'])[1]" userInput=".product_form_product_form_advanced_pricing_modal" stepKey="deleteFirstRowOfCustomerGroupPrice"/>
    +        <waitForElement selector="//tr//button[@data-action='remove_row']" stepKey="waitForGroupPriceSecondRow"/>
             <click selector="//tr//button[@data-action='remove_row']" userInput=".product_form_product_form_advanced_pricing_modal" stepKey="deleteSecondRowOfCustomerGroupPrice"/>
             <click selector="{{AdminProductFormAdvancedPricingSection.doneButton}}" userInput=".product_form_product_form_advanced_pricing_modal" stepKey="clickDoneButton5"/>
             <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct5"/>
    
  • app/code/Magento/Catalog/Test/Mftf/Test/AdminMassProductAttributeUpdateAddedToQueueTest.xml+2 0 modified
    @@ -46,8 +46,10 @@
             <checkOption selector="{{AdminEditProductAttributesSection.changeAttributeShortDescriptionToggle}}" stepKey="toggleToChangeShortDescription"/>
             <fillField selector="{{AdminEditProductAttributesSection.attributeShortDescription}}" userInput="Test Update" stepKey="fillShortDescriptionField"/>
             <actionGroup ref="AdminSaveProductsMassAttributesUpdateActionGroup" stepKey="saveMassAttributeUpdate"/>
    +        <waitForElementVisible selector="{{AdminSystemMessagesSection.info}}" stepKey="waitForInfoMessage"/>
             <see selector="{{AdminSystemMessagesSection.info}}" userInput="Task &quot;Update attributes for 3 selected products&quot;: 1 item(s) have been scheduled for update." stepKey="seeInfoMessage"/>
             <click selector="{{AdminSystemMessagesSection.viewDetailsLink}}" stepKey="seeDetails"/>
    +        <waitForElementVisible selector="{{AdminBulkDetailsModalSection.descriptionValue}}" stepKey="waitForDescription"/>
             <see selector="{{AdminBulkDetailsModalSection.descriptionValue}}" userInput="Update attributes for 3 selected products" stepKey="seeDescription"/>
             <see selector="{{AdminBulkDetailsModalSection.summaryValue}}" userInput="Pending, in queue..." stepKey="seeSummary"/>
             <grabTextFrom selector="{{AdminBulkDetailsModalSection.startTimeValue}}" stepKey="grabStartTimeValue"/>
    
  • app/code/Magento/Catalog/Test/Mftf/Test/AdminRestrictedUserAddCategoryFromProductPageTest.xml+1 0 modified
    @@ -97,6 +97,7 @@
             </assertStringContainsString>
             <!--Remove the category from the product and assert that it removed-->
             <comment userInput="Remove the category from the product and assert that it removed" stepKey="assertCategoryRemoved"/>
    +        <scrollTo selector='//label/span[contains(text(),"Quantity")]' stepKey="scrollToCategorySection"/>
             <actionGroup ref="RemoveCategoryFromProductActionGroup" stepKey="removeCategoryFromProduct">
                 <argument name="categoryName" value="$$createCategory.name$$"/>
             </actionGroup>
    
  • app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockEnabledFlatCatalogTest.xml+5 1 modified
    @@ -6,7 +6,7 @@
       */
     -->
     
    -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd">
         <test name="AdminUpdateSimpleProductWithRegularPriceInStockEnabledFlatCatalogTest">
             <annotations>
    @@ -26,13 +26,17 @@
                     <requiredEntity createDataKey="initialCategoryEntity"/>
                 </createData>
                 <createData entity="SimpleSubCategory" stepKey="categoryEntity"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </before>
             <after>
                 <deleteData stepKey="deleteSimpleSubCategory" createDataKey="initialCategoryEntity"/>
                 <deleteData stepKey="deleteSimpleSubCategory2" createDataKey="categoryEntity"/>
                 <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="deleteCreatedProduct">
                     <argument name="sku" value="{{simpleProductEnabledFlat.sku}}"/>
                 </actionGroup>
    +            <actionGroup ref="ResetAdminDataGridToDefaultViewActionGroup" stepKey="clearFilters"/>
                 <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/>
                 <magentoCLI stepKey="unsetFlatCatalogProduct" command="config:set catalog/frontend/flat_catalog_product 0"/>
             </after>
    
  • app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CAdminTest.xml+4 0 modified
    @@ -21,8 +21,12 @@
                 </skip>
                 <group value="pr_exclude"/>
             </annotations>
    +        <before>
    +            <magentoCLI command="config:set checkout/options/enable_guest_checkout_login 1" stepKey="EnablingGuestCheckoutLogin"/>
    +        </before>
             <after>
                 <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutOfAdmin"/>
    +            <magentoCLI command="config:set checkout/options/enable_guest_checkout_login 0" stepKey="DisablingGuestCheckoutLogin"/>
             </after>
     
             <!--Login to Admin Area-->
    
  • app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/NewActionTest.php+68 13 modified
    @@ -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());
         }
     }
    
  • app/code/Magento/Catalog/Test/Unit/Pricing/Render/FinalPriceBoxTest.php+33 0 modified
    @@ -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);
         }
    
  • app/code/Magento/CatalogUrlRewrite/composer.json+17 15 modified
    @@ -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 @@
             }
         }
     }
    +
    
  • app/code/Magento/CatalogUrlRewriteGraphQl/composer.json+14 12 modified
    @@ -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 @@
             }
         }
     }
    +
    
  • app/code/Magento/Catalog/view/adminhtml/web/js/category-tree.js+2 3 modified
    @@ -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
                     },
    
  • app/code/Magento/CatalogWidget/composer.json+17 15 modified
    @@ -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 @@
             }
         }
     }
    +
    
  • app/code/Magento/CatalogWidget/Test/Mftf/Test/StorefrontProductGridUIUpdatesOnDesktopTest.xml+1 0 modified
    @@ -61,6 +61,7 @@
             <actionGroup ref="StorefrontGoToCMSPageActionGroup" stepKey="navigateAndOpenCreatedCmsPage">
                 <argument name="identifier" value="$createCmsPage.identifier$"/>
             </actionGroup>
    +        <moveMouseOver selector="{{StorefrontCategoryMainSection.searchStore}}" stepKey="hoverSearchTextField"/>
             <actionGroup ref="AssertStorefrontProductControlsAreNotVisibleWithoutHoverActionGroup" stepKey="assertProductControlsAreNotVisibleWithoutHoverOnCmsPage"/>
             <seeElement selector="{{StorefrontCategoryMainSection.productLinkByHref($createFirstSimpleProduct.custom_attributes[url_key]$)}}" stepKey="assertProductName"/>
             <seeElement selector="{{StorefrontCategoryMainSection.productPriceByName($createFirstSimpleProduct.name$)}}" stepKey="assertProductPrice"/>
    
  • app/code/Magento/CheckoutAgreements/composer.json+12 10 modified
    @@ -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 @@
             }
         }
     }
    +
    
  • app/code/Magento/CheckoutAgreementsGraphQl/composer.json+10 8 modified
    @@ -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 @@
             }
         }
     }
    +
    
  • app/code/Magento/CheckoutAgreements/Test/Mftf/ActionGroup/AdminDeleteAllTermConditionsActionGroup.xml+27 0 added
    @@ -0,0 +1,27 @@
    +<?xml version="1.0" encoding="UTF-8"?>
    +<!--
    + /**
    +  * Copyright © Magento, Inc. All rights reserved.
    +  * See COPYING.txt for license details.
    +  */
    +-->
    +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    +              xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd">
    +    <actionGroup name="AdminDeleteAllTermConditionsActionGroup">
    +        <annotations>
    +            <description>Deletes all rows one by one on the 'Terms and Conditions' page.</description>
    +        </annotations>
    +        <waitForElementVisible selector="{{AdminLegacyDataGridFilterSection.clear}}" stepKey="waitForResetFilter"/>
    +        <click selector="{{AdminLegacyDataGridFilterSection.clear}}" stepKey="clickResetFilter"/>
    +        <waitForPageLoad stepKey="waitForGridReset"/>
    +        <helper class="Magento\CheckoutAgreements\Test\Mftf\Helper\CheckoutAgreementsHelpers" method="deleteAllTermConditionRows" stepKey="deleteAllTermConditionRows">
    +            <argument name="rowsToDelete">{{AdminTermGridSection.allTermRows}}</argument>
    +            <argument name="deleteButton">{{AdminMainActionsSection.delete}}</argument>
    +            <argument name="modalAcceptButton">{{AdminConfirmationModalSection.ok}}</argument>
    +            <argument name="successMessage">You deleted the condition.</argument>
    +            <argument name="successMessageContainer">{{AdminMessagesSection.success}}</argument>
    +        </helper>
    +        <waitForPageLoad stepKey="waitForGridLoad"/>
    +        <waitForText userInput="We couldn't find any records." selector="{{AdminTermGridSection.emptyGrid}}" stepKey="waitForEmptyGrid"/>
    +    </actionGroup>
    +</actionGroups>
    
  • app/code/Magento/CheckoutAgreements/Test/Mftf/Helper/CheckoutAgreementsHelpers.php+61 0 added
    @@ -0,0 +1,61 @@
    +<?php
    +/**
    + * Copyright © Magento, Inc. All rights reserved.
    + * See COPYING.txt for license details.
    + */
    +declare(strict_types=1);
    +
    +namespace Magento\CheckoutAgreements\Test\Mftf\Helper;
    +
    +use Facebook\WebDriver\WebDriverBy;
    +use Magento\FunctionalTestingFramework\Helper\Helper;
    +use Magento\FunctionalTestingFramework\Module\MagentoWebDriver;
    +use Exception;
    +
    +/**
    + * Class for MFTF helpers for CheckoutAgreements module.
    + */
    +class CheckoutAgreementsHelpers extends Helper
    +{
    +    /**
    +     * Delete all term conditions one by one from the Terms & Conditions grid page.
    +     *
    +     * @param string $rowsToDelete
    +     * @param string $deleteButton
    +     * @param string $modalAcceptButton
    +     * @param string $successMessage
    +     * @param string $successMessageContainer
    +     *
    +     * @return void
    +     */
    +    public function deleteAllTermConditionRows(
    +        string $rowsToDelete,
    +        string $deleteButton,
    +        string $modalAcceptButton,
    +        string $successMessage,
    +        string $successMessageContainer
    +    ): void {
    +        try {
    +            /** @var MagentoWebDriver $magentoWebDriver */
    +            $magentoWebDriver = $this->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());
    +        }
    +    }
    +}
    
  • app/code/Magento/CheckoutAgreements/Test/Mftf/Section/AdminTermGridSection.xml+2 0 modified
    @@ -14,5 +14,7 @@
             <element name="firstRowConditionName" type="text" selector=".data-grid>tbody>tr>td.col-name"/>
             <element name="firstRowConditionId" type="text" selector=".data-grid>tbody>tr>td.col-id.col-agreement_id"/>
             <element name="successMessage" type="text" selector=".message-success"/>
    +        <element name="allTermRows" type="block" selector="//table[@id='agreementGrid_table']//tbody//tr[not(contains(@class,'data-grid-tr-no-data'))]"/>
    +        <element name="emptyGrid" type="block" selector="//table[@id='agreementGrid_table']//tbody//tr[contains(@class,'data-grid-tr-no-data')]"/>
         </section>
     </sections>
    
  • app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminCreateActiveHtmlTermEntityTest.xml+3 4 modified
    @@ -32,10 +32,9 @@
                 <deleteData createDataKey="createProduct" stepKey="deletedProduct"/>
     
                 <actionGroup ref="AdminTermsConditionsOpenGridActionGroup" stepKey="openTermsGridToDelete"/>
    -            <actionGroup ref="AdminTermsConditionsEditTermByNameActionGroup" stepKey="openTermToDelete">
    -                <argument name="termName" value="{{activeHtmlTerm.name}}"/>
    -            </actionGroup>
    -            <actionGroup ref="AdminTermsConditionsDeleteTermByNameActionGroup" stepKey="deleteOpenedTerm"/>
    +            <actionGroup ref="AdminDeleteAllTermConditionsActionGroup" stepKey="deleteAllTerms"/>
    +            <comment userInput="BIC workaround" stepKey="openTermToDelete"/>
    +            <comment userInput="BIC workaround" stepKey="deleteOpenedTerm"/>
     
                 <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/>
             </after>
    
  • app/code/Magento/CheckoutAgreements/Test/Mftf/Test/AdminCreateActiveTextTermEntityTest.xml+1 3 modified
    @@ -20,9 +20,7 @@
                 <group value="mtf_migrated"/>
             </annotations>
             <after>
    -            <actionGroup ref="AdminTermsConditionsEditTermByNameActionGroup" stepKey="openTermToDelete">
    -                <argument name="termName" value="{{activeTextTerm.name}}"/>
    -            </actionGroup>
    +            <comment userInput="BIC workaround" stepKey="openTermToDelete"/>
             </after>
     
             <actionGroup ref="AdminTermsConditionsFillTermEditFormActionGroup" stepKey="fillNewTerm">
    
  • app/code/Magento/Checkout/composer.json+30 27 modified
    @@ -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-p9",
         "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 @@
             }
         }
     }
    +
    
  • app/code/Magento/Checkout/Controller/Sidebar/UpdateItemQty.php+14 2 modified
    @@ -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 {
    
  • app/code/Magento/Checkout/etc/adminhtml/system.xml+18 1 modified
    @@ -13,6 +13,11 @@
                 <resource>Magento_Checkout::checkout</resource>
                 <group id="options" translate="label" type="text" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="1">
                     <label>Checkout Options</label>
    +                <field id="enable_guest_checkout_login" translate="label" type="select" sortOrder="4" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1">
    +                    <label>Enable Guest Checkout Login</label>
    +                    <source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
    +                    <comment>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.</comment>
    +                </field>
                     <field id="onepage_checkout_enabled" translate="label" type="select" sortOrder="5" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1">
                         <label>Enable Onepage Checkout</label>
                         <source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
    @@ -23,7 +28,7 @@
                     </field>
                     <field id="display_billing_address_on" translate="label" type="select" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1">
                         <label>Display Billing Address On</label>
    -                    <source_model>\Magento\Checkout\Model\Adminhtml\BillingAddressDisplayOptions</source_model>
    +                    <source_model>Magento\Checkout\Model\Adminhtml\BillingAddressDisplayOptions</source_model>
                     </field>
                     <field id="max_items_display_count" translate="label" type="text" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1">
                         <label>Maximum Number of Items to Display in Order Summary</label>
    @@ -101,5 +106,17 @@
                     </field>
                 </group>
             </section>
    +        <section id="csp">
    +            <group id="mode">
    +                <group id="storefront_checkout_index_index" translate="label" sortOrder="4" showInDefault="1" showInWebsite="1" showInStore="1">
    +                    <label>Storefront > One Page Checkout</label>
    +                    <field id="report_uri" translate="label" type="text" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="1">
    +                        <label>Report URI</label>
    +                        <comment>If empty, Default Report URI for storefront will be used.</comment>
    +                        <validate>validate-url</validate>
    +                    </field>
    +                </group>
    +            </group>
    +        </section>
         </system>
     </config>
    
  • app/code/Magento/Checkout/etc/config.xml+16 0 modified
    @@ -9,6 +9,7 @@
         <default>
             <checkout>
                 <options>
    +                <enable_guest_checkout_login>0</enable_guest_checkout_login>
                     <onepage_checkout_enabled>1</onepage_checkout_enabled>
                     <guest_checkout>1</guest_checkout>
                     <display_billing_address_on>0</display_billing_address_on>
    @@ -55,5 +56,20 @@
                     </shown_to_logged_in_user>
                 </captcha>
             </customer>
    +        <csp>
    +            <mode>
    +                <storefront_checkout_index_index>
    +                    <report_only>0</report_only>
    +                </storefront_checkout_index_index>
    +            </mode>
    +            <policies>
    +                <storefront_checkout_index_index>
    +                    <scripts>
    +                        <inline>0</inline>
    +                        <event_handlers>1</event_handlers>
    +                    </scripts>
    +                </storefront_checkout_index_index>
    +            </policies>
    +        </csp>
         </default>
     </config>
    
  • app/code/Magento/Checkout/etc/frontend/events.xml+4 0 modified
    @@ -12,4 +12,8 @@
         <event name="customer_logout">
             <observer name="unsetAll" instance="Magento\Checkout\Observer\UnsetAllObserver" />
         </event>
    +    <event name="controller_action_predispatch_checkout_index_index">
    +        <observer name="cps_storefront_checkout_index_index_predispatch"
    +                  instance="Magento\Checkout\Observer\CspPolicyObserver"/>
    +    </event>
     </config>
    
  • app/code/Magento/Checkout/i18n/en_US.csv+1 0 modified
    @@ -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 <a href=""%2"">shopping cart</a>.","You added %1 to your <a href=""%2"">shopping cart</a>."
     "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."
    
  • app/code/Magento/Checkout/Model/DefaultConfigProvider.php+11 1 modified
    @@ -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',
    
  • app/code/Magento/Checkout/Model/ShippingInformationManagement.php+20 14 modified
    @@ -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();
    
  • app/code/Magento/Checkout/Observer/CspPolicyObserver.php+69 0 added
    @@ -0,0 +1,69 @@
    +<?php
    +/**
    + * Copyright © Magento, Inc. All rights reserved.
    + * See COPYING.txt for license details.
    + */
    +declare(strict_types=1);
    +
    +namespace Magento\Checkout\Observer;
    +
    +use Magento\Csp\Model\Collector\DynamicCollector;
    +use Magento\Csp\Model\Policy\FetchPolicy;
    +use Magento\Framework\Event\Observer;
    +use Magento\Framework\Event\ObserverInterface;
    +use Magento\Framework\Translate\InlineInterface;
    +
    +/**
    + * Observer for adding CSP policy for inline translation
    + */
    +class CspPolicyObserver implements ObserverInterface
    +{
    +    /**
    +     * @var InlineInterface
    +     */
    +    private InlineInterface $inlineTranslate;
    +
    +    /**
    +     * @var DynamicCollector
    +     */
    +    private DynamicCollector $dynamicCollector;
    +
    +    /**
    +     * @param InlineInterface $inlineTranslate
    +     * @param DynamicCollector $dynamicCollector
    +     */
    +    public function __construct(InlineInterface $inlineTranslate, DynamicCollector $dynamicCollector)
    +    {
    +        $this->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);
    +        }
    +    }
    +}
    
  • app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertMiniCartEmptyActionGroup.xml+2 1 modified
    @@ -13,8 +13,9 @@
                 <description>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.</description>
             </annotations>
     
    +        <wait stepKey="waitForMinicartAjaxCallToComplete" time="15"/>
             <dontSeeElement selector="{{StorefrontMinicartSection.productCount}}" stepKey="dontSeeMinicartProductCount"/>
             <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="expandMinicart"/>
    -        <see selector="{{StorefrontMinicartSection.minicartContent}}" userInput="You have no items in your shopping cart." stepKey="seeEmptyCartMessage"/>
    +        <see selector="{{StorefrontMinicartSection.messageEmptyCart}}" userInput="You have no items in your shopping cart." stepKey="seeEmptyCartMessage"/>
         </actionGroup>
     </actionGroups>
    
  • app/code/Magento/Checkout/Test/Mftf/ActionGroup/CheckoutPlaceOrderActionGroup.xml+4 0 modified
    @@ -17,8 +17,12 @@
                 <argument name="emailYouMessage"/>
             </arguments>
     
    +        <waitForPageLoad stepKey="waitForPageLoad"/>
             <waitForElementVisible selector="{{CheckoutPaymentSection.placeOrder}}" time="30" stepKey="waitForPlaceOrderButton"/>
    +        <waitForElementVisible selector="{{CheckoutPaymentSection.placeOrder}}" stepKey="waitForPlaceOrderButtonVisible"/>
             <click selector="{{CheckoutPaymentSection.placeOrder}}" stepKey="clickPlaceOrder"/>
    +        <waitForPageLoad stepKey="waitForOrderPlaced"/>
    +        <waitForElementVisible selector="{{CheckoutSuccessMainSection.success}}" stepKey="waitForSuccess"/>
             <see selector="{{CheckoutSuccessMainSection.success}}" userInput="{{orderNumberMessage}}" stepKey="seeOrderNumber"/>
             <see selector="{{CheckoutSuccessMainSection.success}}" userInput="{{emailYouMessage}}" stepKey="seeEmailYou"/>
         </actionGroup>
    
  • app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutAsCustomerUsingDefaultAddressTest.xml+2 1 modified
    @@ -20,11 +20,11 @@
                 <group value="mtf_migrated"/>
             </annotations>
             <before>
    +            <magentoCLI command="config:set checkout/options/enable_guest_checkout_login 1" stepKey="EnablingGuestCheckoutLogin"/>
                 <!-- Create Simple Product -->
                 <createData entity="SimpleProduct2" stepKey="createSimpleProduct">
                     <field key="price">560</field>
                 </createData>
    -
                 <!-- Create customer -->
                 <createData entity="Simple_US_Customer_Multiple_Addresses" stepKey="createCustomer"/>
             </before>
    @@ -40,6 +40,7 @@
     
                 <!-- Delete customer -->
                 <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/>
    +            <magentoCLI command="config:set checkout/options/enable_guest_checkout_login 0" stepKey="DisablingGuestCheckoutLogin"/>
             </after>
     
             <!-- Add Simple Product to cart -->
    
  • app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutAsCustomerUsingNonDefaultAddressTest.xml+2 0 modified
    @@ -20,6 +20,7 @@
                 <group value="mtf_migrated"/>
             </annotations>
             <before>
    +            <magentoCLI command="config:set checkout/options/enable_guest_checkout_login 1" stepKey="EnablingGuestCheckoutLogin"/>
                 <!-- Create Simple Product -->
                 <createData entity="SimpleProduct2" stepKey="createSimpleProduct">
                     <field key="price">560</field>
    @@ -40,6 +41,7 @@
     
                 <!-- Delete customer -->
                 <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/>
    +            <magentoCLI command="config:set checkout/options/enable_guest_checkout_login 0" stepKey="DisablingGuestCheckoutLogin"/>
             </after>
     
             <!-- Add Simple Product to cart -->
    
  • app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckoutWithSpecialPriceProductsTest.xml+2 0 modified
    @@ -18,6 +18,7 @@
             </annotations>
     
             <before>
    +            <magentoCLI command="config:set checkout/options/enable_guest_checkout_login 1" stepKey="EnablingGuestCheckoutLogin"/>
                 <actionGroup ref="AdminLoginActionGroup" stepKey="loginToAdminPanel"/>
                 <createData entity="Simple_US_Customer" stepKey="createCustomer"/>
                 <createData entity="defaultSimpleProduct" stepKey="simpleProduct">
    @@ -105,6 +106,7 @@
     
                 <!-- Reindex invalidated indices after product attribute has been created/deleted -->
                 <magentoCron groups="index" stepKey="reindexInvalidatedIndices"/>
    +            <magentoCLI command="config:set checkout/options/enable_guest_checkout_login 0" stepKey="DisablingGuestCheckoutLogin"/>
             </after>
     
             <!--Open Product page in StoreFront and assert product and price range -->
    
  • app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerLoginDuringCheckoutTest.xml+2 1 modified
    @@ -19,6 +19,7 @@
                 <group value="OnePageCheckout"/>
             </annotations>
             <before>
    +            <magentoCLI command="config:set checkout/options/enable_guest_checkout_login 1" stepKey="EnablingGuestCheckoutLogin"/>
                 <!-- Create simple product -->
                 <createData entity="SimpleProduct2" stepKey="createProduct"/>
     
    @@ -39,9 +40,9 @@
                 <actionGroup ref="AdminDeleteCustomerActionGroup" stepKey="deleteCustomer">
                     <argument name="customerEmail" value="CustomerEntityOne.email"/>
                 </actionGroup>
    -
                 <!-- Logout admin -->
                 <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/>
    +            <magentoCLI command="config:set checkout/options/enable_guest_checkout_login 0" stepKey="DisablingGuestCheckoutLogin"/>
             </after>
             <!-- Go to Storefront as Guest and create new account -->
             <actionGroup ref="StorefrontOpenCustomerAccountCreatePageActionGroup" stepKey="openCreateAccountPage"/>
    
  • app/code/Magento/Checkout/Test/Mftf/Test/StorefrontVerifySecureURLRedirectCheckoutTest.xml+3 1 modified
    @@ -45,8 +45,10 @@
             </after>
             <executeJS function="return window.location.host" stepKey="hostname"/>
             <amOnUrl url="http://{$hostname}/checkout" stepKey="goToUnsecureCheckoutURL"/>
    -        <seeCurrentUrlEquals url="https://{$hostname}/checkout" stepKey="seeSecureCheckoutURL"/>
    +        <waitForPageLoad stepKey="waitForCheckoutShippingPageToLoad"/>
    +        <seeCurrentUrlMatches regex="~https://$hostname/checkout(?:#shipping)?~" stepKey="seeSecureCheckoutURL"/>
             <amOnUrl url="http://{$hostname}/checkout/sidebar" stepKey="goToUnsecureCheckoutSidebarURL"/>
    +        <waitForPageLoad stepKey="waitForUnsecureCheckoutSidebarPageToLoad"/>
             <seeCurrentUrlEquals url="http://{$hostname}/checkout/sidebar" stepKey="seeUnsecureCheckoutSidebarURL"/>
         </test>
     </tests>
    
  • app/code/Magento/Checkout/Test/Unit/Controller/Sidebar/UpdateItemQtyTest.php+35 0 modified
    @@ -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());
    +    }
     }
    
  • app/code/Magento/Cms/composer.json+18 16 modified
    @@ -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-p2",
         "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 @@
             }
         }
     }
    +
    
  • app/code/Magento/Cms/Controller/Noroute/Index.php+5 2 modified
    @@ -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 */
    
  • app/code/Magento/CmsGraphQl/composer.json+13 11 modified
    @@ -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 @@
             }
         }
     }
    +
    
  • app/code/Magento/Cms/Test/Unit/Controller/Noroute/IndexTest.php+7 5 modified
    @@ -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(
    
  • app/code/Magento/CmsUrlRewrite/composer.json+11 9 modified
    @@ -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 @@
             }
         }
     }
    +
    
  • app/code/Magento/CmsUrlRewriteGraphQl/composer.json+13 11 modified
    @@ -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 @@
             }
         }
     }
    +
    
  • app/code/Magento/CompareListGraphQl/composer.json+8 6 modified
    @@ -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 @@
             }
         }
     }
    +
    
  • app/code/Magento/Config/Block/System/Config/Form/Field/File.php+3 1 modified
    @@ -9,6 +9,8 @@
      *
      * @author     Magento Core Team <core@magentocommerce.com>
      */
    +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 .= '<div>' . $this->getValue() . ' ';
    +            $html .= '<div>' . $this->_escaper->escapeHtml($this->getValue()) . ' ';
                 $html .= '<input type="checkbox" name="' .
                     parent::getName() .
                     '[delete]" value="1" class="checkbox" id="' .
    
  • app/code/Magento/Config/composer.json+15 13 modified
    @@ -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-p8",
         "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 @@
             }
         }
     }
    +
    
  • app/code/Magento/Config/i18n/en_US.csv+1 0 modified
    @@ -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"
    
  • app/code/Magento/Config/Model/Config/Backend/File.php+20 1 modified
    @@ -3,6 +3,8 @@
      * Copyright © Magento, Inc. All rights reserved.
      * See COPYING.txt for license details.
      */
    +declare(strict_types=1);
    +
     namespace Magento\Config\Model\Config\Backend;
     
     use Exception;
    @@ -114,7 +116,7 @@ public function beforeSave()
                 if (is_array($value) && !empty($value['delete'])) {
                     $this->setValue('');
                 } elseif (is_array($value) && !empty($value['value'])) {
    -                $this->setValue($value['value']);
    +                $this->setValueAfterValidation($value['value']);
                 } else {
                     $this->unsValue();
                 }
    @@ -266,4 +268,21 @@ protected function _getAllowedExtensions()
         {
             return [];
         }
    +
    +    /**
    +     * Validate if the value is intercepted
    +     *
    +     * @param string $value
    +     * @return void
    +     * @throws LocalizedException
    +     */
    +    private function setValueAfterValidation(string $value): void
    +    {
    +        // avoid intercepting value
    +        if (preg_match('/[^a-z0-9_\/\\-\\.]+/i', $value)) {
    +            throw new LocalizedException(__('Invalid file name'));
    +        }
    +
    +        $this->setValue($value);
    +    }
     }
    
  • app/code/Magento/Config/Test/Unit/Block/System/Config/Form/Field/FileTest.php+62 19 modified
    @@ -8,49 +8,85 @@
     namespace Magento\Config\Test\Unit\Block\System\Config\Form\Field;
     
     use Magento\Config\Block\System\Config\Form\Field\File;
    +use Magento\Framework\Data\Form\Element\Factory;
    +use Magento\Framework\Data\Form\Element\CollectionFactory;
     use Magento\Framework\DataObject;
     use Magento\Framework\Escaper;
     use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
    +use PHPUnit\Framework\MockObject\MockObject;
     use PHPUnit\Framework\TestCase;
     
     /**
      * Tests for \Magento\Framework\Data\Form\Field\File
      */
     class FileTest extends TestCase
     {
    +    /**
    +     * XSS value
    +     */
    +    private const XSS_FILE_NAME_TEST = '<img src=x onerror=alert(1)>.crt';
    +
    +    /**
    +     * Input name
    +     */
    +    private const INPUT_NAME_TEST = 'test_name';
    +
         /**
          * @var File
          */
         protected $file;
     
    +    /**
    +     * @var Factory|MockObject
    +     */
    +    private $factoryMock;
    +
    +    /**
    +     * @var CollectionFactory|MockObject
    +     */
    +    private $factoryCollectionMock;
    +
    +    /**
    +     * @var Escaper|MockObject
    +     */
    +    private $escaperMock;
    +
         /**
          * @var array
          */
    -    protected $testData;
    +    protected array $testData = [
    +        'before_element_html' => 'test_before_element_html',
    +        'html_id' => 'test_id',
    +        'name' => 'test_name',
    +        'value' => 'test_value',
    +        'title' => 'test_title',
    +        'disabled' => true,
    +        'after_element_js' => 'test_after_element_js',
    +        'after_element_html' => 'test_after_element_html',
    +        'html_id_prefix' => 'test_id_prefix_',
    +        'html_id_suffix' => '_test_id_suffix',
    +    ];
     
         protected function setUp(): void
         {
             $objectManager = new ObjectManager($this);
     
    -        $this->testData = [
    -            'before_element_html' => 'test_before_element_html',
    -            'html_id' => 'test_id',
    -            'name' => 'test_name',
    -            'value' => 'test_value',
    -            'title' => 'test_title',
    -            'disabled' => true,
    -            'after_element_js'    => 'test_after_element_js',
    -            'after_element_html'  => 'test_after_element_html',
    -            'html_id_prefix'      => 'test_id_prefix_',
    -            'html_id_suffix'      => '_test_id_suffix',
    -        ];
    -
    +        $this->factoryMock = $this->getMockBuilder(Factory::class)
    +            ->disableOriginalConstructor()
    +            ->getMock();
    +        $this->factoryCollectionMock = $this->getMockBuilder(CollectionFactory::class)
    +            ->disableOriginalConstructor()
    +            ->getMock();
    +        $this->escaperMock = $this->getMockBuilder(Escaper::class)
    +            ->disableOriginalConstructor()
    +            ->getMock();
             $this->file = $objectManager->getObject(
                 File::class,
                 [
    -                '_escaper' => $objectManager->getObject(Escaper::class),
    +                'factoryElement' => $this->factoryMock,
    +                'factoryCollection' => $this->factoryCollectionMock,
    +                '_escaper' => $this->escaperMock,
                     'data' => $this->testData,
    -
                 ]
             );
     
    @@ -60,13 +96,20 @@ protected function setUp(): void
             $this->file->setForm($formMock);
         }
     
    -    public function testGetElementHtml()
    +    public function testGetElementHtml(): void
         {
    -        $html = $this->file->getElementHtml();
    -
             $expectedHtmlId = $this->testData['html_id_prefix']
                 . $this->testData['html_id']
                 . $this->testData['html_id_suffix'];
    +        $this->escaperMock->expects($this->any())->method('escapeHtml')->willReturnMap(
    +            [
    +                [$expectedHtmlId, null, $expectedHtmlId],
    +                [self::XSS_FILE_NAME_TEST, null, self::XSS_FILE_NAME_TEST],
    +                [self::INPUT_NAME_TEST, null, self::INPUT_NAME_TEST],
    +            ]
    +        );
    +
    +        $html = $this->file->getElementHtml();
     
             $this->assertStringContainsString('<label class="addbefore" for="' . $expectedHtmlId . '"', $html);
             $this->assertStringContainsString($this->testData['before_element_html'], $html);
    
  • app/code/Magento/ConfigurableImportExport/composer.json+14 12 modified
    @@ -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 @@
             }
         }
     }
    +
    
  • app/code/Magento/ConfigurableProduct/composer.json+26 24 modified
    @@ -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 @@
             }
         }
     }
    +
    
  • app/code/Magento/ConfigurableProductGraphQl/composer.json+13 11 modified
    @@ -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 @@
             }
         }
     }
    +
    
  • app/code/Magento/ConfigurableProductSales/composer.json+12 10 modified
    @@ -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 @@
             }
         }
     }
    +
    
  • app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/CreateNewAttributeActionGroup.xml+1 0 modified
    @@ -51,6 +51,7 @@
             <waitForPageLoad stepKey="waitForSavingSettings"/>
     
             <!--Select created Attribute -->
    +        <switchToIFrame stepKey="switchOutOfIFrame"/>
             <click selector="{{ConfigurableProductSection.selectCreatedAttribute}}" stepKey="selectCreatedAttribute"/>
     
             <!--Click Next button-->
    
  • app/code/Magento/ConfigurableProduct/Test/Mftf/Data/ConfigurableProductAttributeNameDesignData.xml+1 1 modified
    @@ -12,7 +12,7 @@
             <data key="productName" unique="prefix">Shoes</data>
             <data key="price">60</data>
             <data key="weight">100</data>
    -        <data key="defaultLabel">design</data>
    +        <data key="defaultLabel">design123</data>
             <data key="adminFieldRed">red</data>
             <data key="defaultStoreViewFieldRed">red123</data>
             <data key="adminFieldBlue">blue</data>
    
  • app/code/Magento/ConfigurableProduct/Test/Mftf/Section/ConfigurableProductAttributeNameDesignSection/CatalogProductsSection.xml+1 1 modified
    @@ -14,7 +14,7 @@
             <element name="searchDefaultLabelField" type="input" selector="//*[@id='attributeGrid_filter_frontend_label']"/>
             <element name="searchButton" type="button" selector="//div[@class='admin__filter-actions']//*[contains(text(), 'Search')]"/>
             <element name="storesProductItem" type="button" selector="//*[@data-ui-id='menu-magento-catalog-catalog-attributes-attributes']/a"/>
    -        <element name="createdAttributeItem" type="button" selector="//td[contains(@class, 'col-label') and normalize-space()='design']"/>
    +        <element name="createdAttributeItem" type="button" selector="//td[contains(@class, 'col-label') and normalize-space()='design123']"/>
             <element name="deleteAttributeItem" type="button" selector="//*[@id='delete']"/>
             <element name="okButton" type="button" selector="//footer[@class='modal-footer']//*[contains(text(),'OK')]"/>
             <element name="messageSuccessSavedProduct" type="button" selector="//div[@data-ui-id='messages-message-success']"/>
    
  • app/code/Magento/ConfigurableProduct/Test/Mftf/Section/ConfigurableProductAttributeNameDesignSection/ConfigurableProductSection.xml+1 1 modified
    @@ -12,7 +12,7 @@
             <element name="configProductItem" type="button" selector="//*[@id='add_new_product']//*[contains(text(),'Configurable Product')]"/>
             <element name="nextButton" type="button" selector="//div[@class='nav-bar-outer-actions']//*[contains(text(),'Next')]"/>
             <element name="generateConfigure" type="button" selector="//div[@class='nav-bar-outer-actions']//*[contains(text(),'Generate Products')]"/>
    -        <element name="selectCreatedAttribute" type="button" selector="//*[@class='admin__data-grid-wrap']//td[normalize-space()='design']/preceding-sibling::td"/>
    +        <element name="selectCreatedAttribute" type="button" selector="//*[@class='admin__data-grid-wrap']//td[normalize-space()='design123']/preceding-sibling::td"/>
             <element name="closeFrame" type="button" selector="//*[@class='modal-header']//*[contains(text(),'Create Product Configurations')]/following-sibling::button"/>
         </section>
     </sections>
    
  • app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontGalleryConfigurableProductWithVisualSwatchAttributePrependMediaTest.xml+33 8 modified
    @@ -106,7 +106,9 @@
                 <actionGroup ref="SaveProductFormActionGroup" stepKey="saveConfigurableProductVariationOption3"/>
     
                 <!-- Reindex invalidated indices after product attribute has been created -->
    -           <magentoCron groups="index" stepKey="reindexInvalidatedIndicesAfterCreateAttributes"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindexInvalidatedIndicesAfterCreateAttributes">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </before>
     
             <after>
    @@ -115,22 +117,27 @@
                 </actionGroup>
                 <waitForElementVisible selector="{{AdminMessagesSection.success}}" stepKey="waitForDeleteSuccessMessage"/>
                 <see selector="{{AdminMessagesSection.success}}" userInput="A total of 4 record(s) have been deleted." stepKey="seeDeleteSuccessMessage"/>
    -            <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearProductGridFilters"/>
    +            <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="clearProductGridFilters"/>
                 <actionGroup ref="DeleteProductAttributeActionGroup" stepKey="deleteProductVisualSwatchAttribute">
                     <argument name="ProductAttribute" value="$createVisualSwatchAttribute$"/>
                 </actionGroup>
                 <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearProductAttributeGridFilters"/>
                 <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/>
                 <!-- Reindex invalidated indices after product attribute has been created -->
    -           <magentoCron groups="index" stepKey="reindexInvalidatedIndicesAfterDeleteAttributes"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindexInvalidatedIndicesAfterDeleteAttributes">
    +                <argument name="indices" value=""/>
    +            </actionGroup>
             </after>
     
             <actionGroup ref="StorefrontOpenProductPageActionGroup" stepKey="openConfigurableProductPage">
                 <argument name="productUrl" value="$createConfigurableProduct.custom_attributes[url_key]$"/>
             </actionGroup>
     
             <!--CASE 0: Selected options = none; Expected media : C1, C2, C3-->
    -        <waitForElementVisible selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="waitForThumbnailsAppearCase0"/>
    +        <comment userInput="BIC workaround" stepKey="waitForThumbnailsAppearCase0"/>
    +        <waitForElementVisible selector="{{StorefrontProductMediaSection.fotoramaImageThumbnailImgByNumber('1')}}" stepKey="waitForFirstThumbnailCase0"/>
    +        <waitForElementVisible selector="{{StorefrontProductMediaSection.fotoramaImageThumbnailImgByNumber('2')}}" stepKey="waitForSecondThumbnailCase0"/>
    +        <waitForElementVisible selector="{{StorefrontProductMediaSection.fotoramaImageThumbnailImgByNumber('3')}}" stepKey="waitForThirdThumbnailCase0"/>
             <grabMultiple userInput="src" selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="getListOfThumbnailsCase0"/>
             <assertRegExp stepKey="checkPositionInThumbnailForImage1Case0">
                 <expectedResult type="string">|{{Magento2.filename}}.*.jpg|</expectedResult>
    @@ -157,7 +164,13 @@
     
             <!--CASE 1: Selected options = B1; Expected media : D1, D2, D3, C1, C2, C3-->
             <click selector="{{StorefrontProductInfoMainSection.swatchOptionByLabel($swatchAttributeFirstOption.option[store_labels][0][label]$)}}" stepKey="chooseFirstOptionCase1"/>
    -        <waitForElementVisible selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="waitForThumbnailsAppearCase1"/>
    +        <waitForPageLoad stepKey="waitForThumbnailsAppearCase1"/>
    +        <waitForElementVisible selector="{{StorefrontProductMediaSection.fotoramaImageThumbnailImgByNumber('1')}}" stepKey="waitForFirstThumbnailCase1"/>
    +        <waitForElementVisible selector="{{StorefrontProductMediaSection.fotoramaImageThumbnailImgByNumber('2')}}" stepKey="waitForSecondThumbnailCase1"/>
    +        <waitForElementVisible selector="{{StorefrontProductMediaSection.fotoramaImageThumbnailImgByNumber('3')}}" stepKey="waitForThirdThumbnailCase1"/>
    +        <waitForElementVisible selector="{{StorefrontProductMediaSection.fotoramaImageThumbnailImgByNumber('4')}}" stepKey="waitForFourthThumbnailCase1"/>
    +        <waitForElementVisible selector="{{StorefrontProductMediaSection.fotoramaImageThumbnailImgByNumber('5')}}" stepKey="waitForFifthThumbnailCase1"/>
    +        <waitForElementVisible selector="{{StorefrontProductMediaSection.fotoramaImageThumbnailImgByNumber('6')}}" stepKey="waitForSixthThumbnailCase1"/>
             <grabMultiple userInput="src" selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="getListOfThumbnailsCase1"/>
             <assertRegExp stepKey="checkPositionInThumbnailForImage1Case1">
                 <expectedResult type="string">|{{MagentoLogo.filename}}.*.png|</expectedResult>
    @@ -196,7 +209,10 @@
     
             <!--CASE 2: Selected options = B2; Expected media : C1, C2, C3-->
             <click selector="{{StorefrontProductInfoMainSection.swatchOptionByLabel($swatchAttributeSecondOption.option[store_labels][0][label]$)}}" stepKey="chooseFirstOptionCase2"/>
    -        <waitForElementVisible selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="waitForThumbnailsAppearCase2"/>
    +        <waitForPageLoad stepKey="waitForThumbnailsAppearCase2"/>
    +        <waitForElementVisible selector="{{StorefrontProductMediaSection.fotoramaImageThumbnailImgByNumber('1')}}" stepKey="waitForFirstThumbnailCase2"/>
    +        <waitForElementVisible selector="{{StorefrontProductMediaSection.fotoramaImageThumbnailImgByNumber('2')}}" stepKey="waitForSecondThumbnailCase2"/>
    +        <waitForElementVisible selector="{{StorefrontProductMediaSection.fotoramaImageThumbnailImgByNumber('3')}}" stepKey="waitForThirdThumbnailCase2"/>
             <grabMultiple userInput="src" selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="getListOfThumbnailsCase2"/>
             <assertRegExp stepKey="checkPositionInThumbnailForImage1Case2">
                 <expectedResult type="string">|{{Magento2.filename}}.*.jpg|</expectedResult>
    @@ -223,7 +239,13 @@
     
             <!--CASE 3: Selected options = B3; Expected media : E1, E2, E3, C1, C2, C3-->
             <click selector="{{StorefrontProductInfoMainSection.swatchOptionByLabel($swatchAttributeThirdOption.option[store_labels][0][label]$)}}" stepKey="chooseFirstOptionCase3"/>
    -        <waitForElementVisible selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="waitForThumbnailsAppearCase3"/>
    +        <waitForPageLoad stepKey="waitForThumbnailsAppearCase3"/>
    +        <waitForElementVisible selector="{{StorefrontProductMediaSection.fotoramaImageThumbnailImgByNumber('1')}}" stepKey="waitForFirstThumbnailCase3"/>
    +        <waitForElementVisible selector="{{StorefrontProductMediaSection.fotoramaImageThumbnailImgByNumber('2')}}" stepKey="waitForSecondThumbnailCase3"/>
    +        <waitForElementVisible selector="{{StorefrontProductMediaSection.fotoramaImageThumbnailImgByNumber('3')}}" stepKey="waitForThirdThumbnailCase3"/>
    +        <waitForElementVisible selector="{{StorefrontProductMediaSection.fotoramaImageThumbnailImgByNumber('4')}}" stepKey="waitForFourthThumbnailCase3"/>
    +        <waitForElementVisible selector="{{StorefrontProductMediaSection.fotoramaImageThumbnailImgByNumber('5')}}" stepKey="waitForFifthThumbnailCase3"/>
    +        <waitForElementVisible selector="{{StorefrontProductMediaSection.fotoramaImageThumbnailImgByNumber('6')}}" stepKey="waitForSixthThumbnailCase3"/>
             <grabMultiple userInput="src" selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="getListOfThumbnailsCase3"/>
             <assertRegExp stepKey="checkPositionInThumbnailForImage1Case3">
                 <expectedResult type="string">|{{Magento3.filename}}.*.jpg|</expectedResult>
    @@ -262,7 +284,10 @@
     
             <!--CASE 4: Selected options = none; Expected media : C1, C2, C3-->
             <click selector="{{StorefrontProductInfoMainSection.swatchOptionByLabel($swatchAttributeThirdOption.option[store_labels][0][label]$)}}" stepKey="unselectThirdOptionCase4"/>
    -        <waitForElementVisible selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="waitForThumbnailsAppearCase4"/>
    +        <waitForPageLoad stepKey="waitForThumbnailsAppearCase4"/>
    +        <waitForElementVisible selector="{{StorefrontProductMediaSection.fotoramaImageThumbnailImgByNumber('1')}}" stepKey="waitForFirstThumbnailCase4"/>
    +        <waitForElementVisible selector="{{StorefrontProductMediaSection.fotoramaImageThumbnailImgByNumber('2')}}" stepKey="waitForSecondThumbnailCase4"/>
    +        <waitForElementVisible selector="{{StorefrontProductMediaSection.fotoramaImageThumbnailImgByNumber('3')}}" stepKey="waitForThirdThumbnailCase4"/>
             <grabMultiple userInput="src" selector="{{StorefrontProductMediaSection.fotoramaAnyMedia}}" stepKey="getListOfThumbnailsCase4"/>
             <assertRegExp stepKey="checkPositionInThumbnailForImage1Case4">
                 <expectedResult type="string">|{{Magento2.filename}}.*.jpg|</expectedResult>
    
  • app/code/Magento/Contact/composer.json+12 10 modified
    @@ -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 @@
             }
         }
     }
    +
    
  • app/code/Magento/Cookie/composer.json+10 8 modified
    @@ -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 @@
             }
         }
     }
    +
    
  • app/code/Magento/Cron/composer.json+10 8 modified
    @@ -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 @@
             }
         }
     }
    +
    
  • app/code/Magento/Csp/Block/Sri/Hashes.php+94 0 added
    @@ -0,0 +1,94 @@
    +<?php
    +/**
    + * Copyright © Magento, Inc. All rights reserved.
    + * See COPYING.txt for license details.
    + */
    +declare(strict_types=1);
    +
    +namespace Magento\Csp\Block\Sri;
    +
    +use Magento\Framework\UrlInterface;
    +use Magento\Deploy\Package\Package;
    +use Magento\Framework\App\ObjectManager;
    +use Magento\Framework\View\Element\Template;
    +use Magento\Framework\Exception\LocalizedException;
    +use Magento\Framework\Serialize\SerializerInterface;
    +use Magento\Framework\View\Element\Template\Context;
    +use Magento\Csp\Model\SubresourceIntegrityRepositoryPool;
    +
    +/**
    + * Block for Subresource Integrity hashes rendering.
    + *
    + * @api
    + */
    +class Hashes extends Template
    +{
    +    /**
    +     * @var SerializerInterface
    +     */
    +    private SerializerInterface $serializer;
    +
    +    /**
    +     * @var SubresourceIntegrityRepositoryPool
    +     */
    +    private SubresourceIntegrityRepositoryPool $integrityRepositoryPool;
    +
    +    /**
    +     * @param Context $context
    +     * @param array $data
    +     * @param SubresourceIntegrityRepositoryPool|null $integrityRepositoryPool
    +     * @param SerializerInterface|null $serializer
    +     */
    +    public function __construct(
    +        Context $context,
    +        array $data = [],
    +        ?SubresourceIntegrityRepositoryPool $integrityRepositoryPool = null,
    +        ?SerializerInterface $serializer = null
    +    ) {
    +        parent::__construct($context, $data);
    +
    +        $this->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);
    +    }
    +}
    
  • app/code/Magento/Csp/composer.json+11 7 modified
    @@ -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 @@
             }
         }
     }
    +
    
  • app/code/Magento/Csp/etc/acl.xml+22 0 added
    @@ -0,0 +1,22 @@
    +<?xml version="1.0"?>
    +<!--
    +/**
    + * Copyright © Magento, Inc. All rights reserved.
    + * See COPYING.txt for license details.
    + */
    +-->
    +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Acl/etc/acl.xsd">
    +    <acl>
    +        <resources>
    +            <resource id="Magento_Backend::admin">
    +                <resource id="Magento_Backend::stores">
    +                    <resource id="Magento_Backend::stores_settings">
    +                        <resource id="Magento_Config::config">
    +                            <resource id="Magento_Csp::config" title="CSP Configuration" translate="title" sortOrder="150" />
    +                        </resource>
    +                    </resource>
    +                </resource>
    +            </resource>
    +        </resources>
    +    </acl>
    +</config>
    
  • app/code/Magento/Csp/etc/adminhtml/system.xml+35 0 added
    @@ -0,0 +1,35 @@
    +<?xml version="1.0"?>
    +<!--
    +/**
    + * Copyright © Magento, Inc. All rights reserved.
    + * See COPYING.txt for license details.
    + */
    +-->
    +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd">
    +    <system>
    +        <section id="csp" translate="label" type="text" sortOrder="305" showInDefault="1" showInWebsite="1" showInStore="1">
    +            <label>Content Security Policy (CSP)</label>
    +            <tab>security</tab>
    +            <resource>Magento_Csp::config</resource>
    +            <group id="mode" translate="label" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="1">
    +                <label>Mode</label>
    +                <group id="storefront" translate="label" sortOrder="2" showInDefault="1" showInWebsite="1" showInStore="1">
    +                    <label>Storefront Default</label>
    +                    <field id="report_uri" translate="label" type="text" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="1">
    +                        <label>Report URI</label>
    +                        <comment>URI to report CSP violations on storefront. Used for all storefront pages that don't have own URI configured above.</comment>
    +                        <validate>validate-url</validate>
    +                    </field>
    +                </group>
    +                <group id="admin" translate="label" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="1">
    +                    <label>Admin Default</label>
    +                    <field id="report_uri" translate="label" type="text" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="1">
    +                        <label>Report URI</label>
    +                        <comment>URI to report CSP violations in admin area. Used for all admin pages that don't have own URI configured above.</comment>
    +                        <validate>validate-url</validate>
    +                    </field>
    +                </group>
    +            </group>
    +        </section>
    +    </system>
    +</config>
    
  • app/code/Magento/Csp/etc/di.xml+23 0 modified
    @@ -110,4 +110,27 @@
                 <argument name="cache" xsi:type="object">Magento\Csp\Model\BlockCache</argument>
             </arguments>
         </type>
    +    <type name="Magento\Deploy\Package\Package">
    +        <arguments>
    +            <argument name="postProcessors" xsi:type="array">
    +                <item name="integrity" xsi:type="object">Magento\Csp\Model\Deploy\Package\Processor\PostProcessor\Integrity</item>
    +            </argument>
    +        </arguments>
    +    </type>
    +    <type name="Magento\Framework\View\Asset\GroupedCollection">
    +        <plugin name="addDefaultPropertiesToGroup" type="Magento\Csp\Plugin\AddDefaultPropertiesToGroupPlugin" />
    +    </type>
    +    <type name="Magento\Deploy\Service\DeployStaticContent">
    +        <plugin name="removeAllAssetIntegrityHashes" type="Magento\Csp\Plugin\RemoveAllAssetIntegrityHashes" />
    +        <plugin name="storeAssetIntegrityHashes" type="Magento\Csp\Plugin\StoreAssetIntegrityHashes" />
    +    </type>
    +    <type name="Magento\RequireJs\Model\FileManager">
    +        <plugin name="addResourceIntegrityAfterAssetCreate" type="Magento\Csp\Plugin\GenerateAssetIntegrity"/>
    +    </type>
    +    <preference for="Magento\Deploy\Package\Processor\PostProcessor\Map" type="Magento\Csp\Model\Deploy\Package\Processor\PostProcessor\Map" />
    +    <type name="Magento\Csp\Model\Deploy\Package\Processor\PostProcessor\Map">
    +        <arguments>
    +            <argument name="driver" xsi:type="object">Magento\Framework\Filesystem\Driver\File</argument>
    +        </arguments>
    +    </type>
     </config>
    
  • app/code/Magento/Csp/Helper/CspNonceProvider.php+86 0 added
    @@ -0,0 +1,86 @@
    +<?php
    +/**
    + * Copyright © Magento, Inc. All rights reserved.
    + * See COPYING.txt for license details.
    + */
    +
    +declare(strict_types=1);
    +
    +namespace Magento\Csp\Helper;
    +
    +use Magento\Csp\Model\Collector\DynamicCollector;
    +use Magento\Csp\Model\Policy\FetchPolicy;
    +use Magento\Framework\Exception\LocalizedException;
    +use Magento\Framework\Math\Random;
    +
    +/**
    + * This helper class is used to provide nonce for CSP
    + *
    + * It also adds a nonce to the CSP header.
    + */
    +class CspNonceProvider
    +{
    +    /**
    +     * @var string
    +     */
    +    private const NONCE_LENGTH = 32;
    +
    +    /**
    +     * @var string
    +     */
    +    private string $nonce;
    +
    +    /**
    +     * @var Random
    +     */
    +    private Random $random;
    +
    +    /**
    +     * @var DynamicCollector
    +     */
    +    private DynamicCollector $dynamicCollector;
    +
    +    /**
    +     * @param Random $random
    +     * @param DynamicCollector $dynamicCollector
    +     */
    +    public function __construct(
    +        Random $random,
    +        DynamicCollector $dynamicCollector
    +    ) {
    +        $this->random = $random;
    +        $this->dynamicCollector = $dynamicCollector;
    +    }
    +
    +    /**
    +     * 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);
    +    }
    +}
    
  • app/code/Magento/Csp/Helper/InlineUtil.php+40 14 modified
    @@ -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())
    +                        )
    +                    );
    +                }
                 }
             }
     
    
  • app/code/Magento/Csp/Model/Collector/ConfigCollector.php+29 2 modified
    @@ -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'])
    
  • app/code/Magento/Csp/Model/Deploy/Package/Processor/PostProcessor/Integrity.php+89 0 added
    @@ -0,0 +1,89 @@
    +<?php
    +/**
    + * Copyright © Magento, Inc. All rights reserved.
    + * See COPYING.txt for license details.
    + */
    +declare(strict_types=1);
    +
    +namespace Magento\Csp\Model\Deploy\Package\Processor\PostProcessor;
    +
    +use Magento\Framework\Filesystem;
    +use Magento\Deploy\Package\Package;
    +use Magento\Csp\Model\SubresourceIntegrityFactory;
    +use Magento\Framework\App\Filesystem\DirectoryList;
    +use Magento\Csp\Model\SubresourceIntegrityCollector;
    +use Magento\Deploy\Package\Processor\ProcessorInterface;
    +use Magento\Csp\Model\SubresourceIntegrity\HashGenerator;
    +
    +/**
    + * Post-processor that generates integrity hashes after static content package deployed.
    + */
    +class Integrity implements ProcessorInterface
    +{
    +    /**
    +     * @var Filesystem
    +     */
    +    private Filesystem $filesystem;
    +
    +    /**
    +     * @var HashGenerator
    +     */
    +    private HashGenerator $hashGenerator;
    +
    +    /**
    +     * @var SubresourceIntegrityFactory
    +     */
    +    private SubresourceIntegrityFactory $integrityFactory;
    +
    +    /**
    +     * @var SubresourceIntegrityCollector
    +     */
    +    private SubresourceIntegrityCollector $integrityCollector;
    +
    +    /**
    +     * @param Filesystem $filesystem
    +     * @param HashGenerator $hashGenerator
    +     * @param SubresourceIntegrityFactory $integrityFactory
    +     * @param SubresourceIntegrityCollector $integrityCollector
    +     */
    +    public function __construct(
    +        Filesystem $filesystem,
    +        HashGenerator $hashGenerator,
    +        SubresourceIntegrityFactory $integrityFactory,
    +        SubresourceIntegrityCollector $integrityCollector
    +    ) {
    +        $this->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;
    +    }
    +}
    
  • app/code/Magento/Csp/Model/Deploy/Package/Processor/PostProcessor/Map.php+139 0 added
    @@ -0,0 +1,139 @@
    +<?php
    +/**
    + * Copyright © Magento, Inc. All rights reserved.
    + * See COPYING.txt for license details.
    + */
    +declare(strict_types=1);
    +
    +namespace Magento\Csp\Model\Deploy\Package\Processor\PostProcessor;
    +
    +use Magento\Deploy\Package\Package;
    +use Magento\Deploy\Package\PackageFileFactory;
    +use Magento\Deploy\Service\DeployStaticFile;
    +use Magento\Framework\App\DeploymentConfig\Writer\PhpFormatter;
    +use Magento\Framework\App\Filesystem\DirectoryList;
    +use Magento\Framework\Exception\FileSystemException;
    +use Magento\Framework\Filesystem;
    +use Magento\Framework\View\Asset\Minification;
    +use Magento\Framework\View\Asset\RepositoryMap;
    +use Magento\Csp\Model\SubresourceIntegrityFactory;
    +use Magento\Csp\Model\SubresourceIntegrity\HashGenerator;
    +use Magento\Framework\Filesystem\DriverInterface;
    +use Magento\Csp\Model\SubresourceIntegrityCollector;
    +
    +/**
    + * Class Adds Integrity attribute to requirejs-map.js asset
    + *
    + * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
    + */
    +class Map extends \Magento\Deploy\Package\Processor\PostProcessor\Map
    +{
    +
    +    /**
    +     * @var HashGenerator
    +     */
    +    private HashGenerator $hashGenerator;
    +
    +    /**
    +     * @var SubresourceIntegrityCollector
    +     */
    +    private SubresourceIntegrityCollector $integrityCollector;
    +
    +    /**
    +     * @var SubresourceIntegrityFactory
    +     */
    +    private SubresourceIntegrityFactory $integrityFactory;
    +
    +    /**
    +     * @var Minification
    +     */
    +    private Minification $minification;
    +
    +    /**
    +     * @var DriverInterface
    +     */
    +    private DriverInterface $driver;
    +
    +    /**
    +     * @var FileSystem
    +     */
    +    private FileSystem $filesystem;
    +
    +    /**
    +     * Constructor
    +     *
    +     * @param DeployStaticFile $deployStaticFile
    +     * @param PhpFormatter $formatter
    +     * @param PackageFileFactory $packageFileFactory
    +     * @param Minification $minification
    +     * @param SubresourceIntegrityFactory $integrityFactory
    +     * @param HashGenerator $hashGenerator
    +     * @param DriverInterface $driver
    +     * @param SubresourceIntegrityCollector $integrityCollector
    +     * @param FileSystem $filesystem
    +     */
    +    public function __construct(
    +        DeployStaticFile $deployStaticFile,
    +        PhpFormatter $formatter,
    +        PackageFileFactory $packageFileFactory,
    +        Minification $minification,
    +        SubresourceIntegrityFactory $integrityFactory,
    +        HashGenerator $hashGenerator,
    +        DriverInterface $driver,
    +        SubresourceIntegrityCollector $integrityCollector,
    +        Filesystem $filesystem
    +    ) {
    +        $this->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);
    +    }
    +}
    
  • app/code/Magento/Csp/Model/Mode/ConfigManager.php+55 7 modified
    @@ -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
    +        );
         }
     }
    
  • app/code/Magento/Csp/Model/SubresourceIntegrityCollector.php+49 0 added
    @@ -0,0 +1,49 @@
    +<?php
    +/**
    + * Copyright © Magento, Inc. All rights reserved.
    + * See COPYING.txt for license details.
    + */
    +declare(strict_types=1);
    +
    +namespace Magento\Csp\Model;
    +
    +/**
    + * Collector of Integrity objects.
    + */
    +class SubresourceIntegrityCollector
    +{
    +    /**
    +     * @var array
    +     */
    +    private array $data = [];
    +
    +    /**
    +     * @param array $data
    +     */
    +    public function __construct(array $data = [])
    +    {
    +        $this->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;
    +    }
    +}
    
  • app/code/Magento/Csp/Model/SubresourceIntegrity/HashGenerator.php+37 0 added
    @@ -0,0 +1,37 @@
    +<?php
    +/**
    + * Copyright © Magento, Inc. All rights reserved.
    + * See COPYING.txt for license details.
    + */
    +declare(strict_types=1);
    +
    +namespace Magento\Csp\Model\SubresourceIntegrity;
    +
    +/**
    + * Subresource Integrity hashes generator.
    + */
    +class HashGenerator
    +{
    +    /**
    +     * CHashing algorithm.
    +     *
    +     * @var string
    +     */
    +    private const ALGORITHM = 'sha256';
    +
    +    /**
    +     * Computes integrity hash for a given content.
    +     *
    +     * @param string $content
    +     *
    +     * @return string
    +     */
    +    public function generate(string $content): string
    +    {
    +        $base64Hash = base64_encode(
    +            hash(self::ALGORITHM, $content, true)
    +        );
    +
    +        return self::ALGORITHM . "-{$base64Hash}";
    +    }
    +}
    
  • app/code/Magento/Csp/Model/SubresourceIntegrity.php+34 0 added
    @@ -0,0 +1,34 @@
    +<?php
    +/**
    + * Copyright © Magento, Inc. All rights reserved.
    + * See COPYING.txt for license details.
    + */
    +declare(strict_types=1);
    +
    +namespace Magento\Csp\Model;
    +
    +/**
    + * Subresource Integrity data model.
    + */
    +class SubresourceIntegrity extends \Magento\Framework\DataObject
    +{
    +    /**
    +     * Gets an integrity Path.
    +     *
    +     * @return string|null
    +     */
    +    public function getPath(): ?string
    +    {
    +        return $this->getData("path");
    +    }
    +
    +    /**
    +     * Gets an integrity hash.
    +     *
    +     * @return string|null
    +     */
    +    public function getHash(): ?string
    +    {
    +        return $this->getData("hash");
    +    }
    +}
    
  • app/code/Magento/Csp/Model/SubresourceIntegrityRepository.php+211 0 added
    @@ -0,0 +1,211 @@
    +<?php
    +/**
    + * Copyright © Magento, Inc. All rights reserved.
    + * See COPYING.txt for license details.
    + */
    +declare(strict_types=1);
    +
    +namespace Magento\Csp\Model;
    +
    +use Magento\Framework\App\CacheInterface;
    +use Magento\Framework\Serialize\SerializerInterface;
    +
    +/**
    + * Class contains methods equivalent to repository design to manage SRI hashes in cache.
    + */
    +class SubresourceIntegrityRepository
    +{
    +    /**
    +     * Cache prefix.
    +     *
    +     * @var string
    +     */
    +    private const CACHE_PREFIX = 'INTEGRITY';
    +
    +    /**
    +     * Integrity data.
    +     *
    +     * @var array|null
    +     */
    +    private ?array $data = null;
    +
    +    /**
    +     * Context of integrity data.
    +     *
    +     * @var string|null
    +     */
    +    private ?string $context;
    +
    +    /**
    +     * @var CacheInterface
    +     */
    +    private CacheInterface $cache;
    +
    +    /**
    +     * @var SerializerInterface
    +     */
    +    private SerializerInterface $serializer;
    +
    +    /**
    +     * @var SubresourceIntegrityFactory
    +     */
    +    private SubresourceIntegrityFactory $integrityFactory;
    +
    +    /**
    +     * @param CacheInterface $cache
    +     * @param SerializerInterface $serializer
    +     * @param SubresourceIntegrityFactory $integrityFactory
    +     * @param string|null $context
    +     */
    +    public function __construct(
    +        CacheInterface $cache,
    +        SerializerInterface $serializer,
    +        SubresourceIntegrityFactory $integrityFactory,
    +        ?string $context = null
    +    ) {
    +        $this->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;
    +    }
    +}
    
  • app/code/Magento/Csp/Model/SubresourceIntegrityRepositoryPool.php+53 0 added
    @@ -0,0 +1,53 @@
    +<?php
    +/**
    + * Copyright © Magento, Inc. All rights reserved.
    + * See COPYING.txt for license details.
    + */
    +declare(strict_types=1);
    +
    +namespace Magento\Csp\Model;
    +
    +/**
    + * Pool of subresource integrity repositories.
    + */
    +class SubresourceIntegrityRepositoryPool
    +{
    +    /**
    +     * @var array
    +     */
    +    private array $repositories = [];
    +
    +    /**
    +     * @var SubresourceIntegrityRepositoryFactory
    +     */
    +    private SubresourceIntegrityRepositoryFactory $integrityRepositoryFactory;
    +
    +    /**
    +     * @param SubresourceIntegrityRepositoryFactory $integrityRepositoryFactory
    +     */
    +    public function __construct(
    +        SubresourceIntegrityRepositoryFactory $integrityRepositoryFactory
    +    ) {
    +        $this->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];
    +    }
    +}
    
  • app/code/Magento/Csp/Plugin/AddDefaultPropertiesToGroupPlugin.php+81 0 added
    @@ -0,0 +1,81 @@
    +<?php
    +/**
    + * Copyright © Magento, Inc. All rights reserved.
    + * See COPYING.txt for license details.
    + */
    +declare(strict_types=1);
    +
    +namespace Magento\Csp\Plugin;
    +
    +use Magento\Framework\App\State;
    +use Magento\Deploy\Package\Package;
    +use Magento\Framework\View\Asset\AssetInterface;
    +use Magento\Framework\View\Asset\LocalInterface;
    +use Magento\Framework\View\Asset\GroupedCollection;
    +use Magento\Csp\Model\SubresourceIntegrityRepositoryPool;
    +
    +/**
    + * Plugin to add integrity to assets on page load.
    + */
    +class AddDefaultPropertiesToGroupPlugin
    +{
    +    /**
    +     * @var State
    +     */
    +    private State $state;
    +
    +    /**
    +     * @var SubresourceIntegrityRepositoryPool
    +     */
    +    private SubresourceIntegrityRepositoryPool $integrityRepositoryPool;
    +
    +    /**
    +     * @param State $state
    +     * @param SubresourceIntegrityRepositoryPool $integrityRepositoryPool
    +     */
    +    public function __construct(
    +        State $state,
    +        SubresourceIntegrityRepositoryPool $integrityRepositoryPool
    +    ) {
    +        $this->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];
    +    }
    +}
    
  • app/code/Magento/Csp/Plugin/GenerateAssetIntegrity.php+91 0 added
    @@ -0,0 +1,91 @@
    +<?php
    +/**
    + * Copyright © Magento, Inc. All rights reserved.
    + * See COPYING.txt for license details.
    + */
    +declare(strict_types=1);
    +
    +namespace Magento\Csp\Plugin;
    +
    +use Magento\Framework\View\Asset\File;
    +use Magento\RequireJs\Model\FileManager;
    +use Magento\Csp\Model\SubresourceIntegrityFactory;
    +use Magento\Csp\Model\SubresourceIntegrityCollector;
    +use Magento\Csp\Model\SubresourceIntegrity\HashGenerator;
    +
    +/**
    + * Plugin to add asset integrity value after static content deploy.
    + */
    +class GenerateAssetIntegrity
    +{
    +    /**
    +     * Supported content types.
    +     *
    +     * @var array
    +     */
    +    private const CONTENT_TYPES = ["js"];
    +
    +    /**
    +     * @var HashGenerator
    +     */
    +    private HashGenerator $hashGenerator;
    +
    +    /**
    +     * @var SubresourceIntegrityFactory
    +     */
    +    private SubresourceIntegrityFactory $integrityFactory;
    +
    +    /**
    +     * @var SubresourceIntegrityCollector
    +     */
    +    private SubresourceIntegrityCollector $integrityCollector;
    +
    +    /**
    +     * @param HashGenerator $hashGenerator
    +     * @param SubresourceIntegrityFactory $integrityFactory
    +     * @param SubresourceIntegrityCollector $integrityCollector
    +     */
    +    public function __construct(
    +        HashGenerator $hashGenerator,
    +        SubresourceIntegrityFactory $integrityFactory,
    +        SubresourceIntegrityCollector $integrityCollector
    +    ) {
    +        $this->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;
    +    }
    +}
    
  • app/code/Magento/Csp/Plugin/RemoveAllAssetIntegrityHashes.php+69 0 added
    @@ -0,0 +1,69 @@
    +<?php
    +/**
    + * Copyright © Magento, Inc. All rights reserved.
    + * See COPYING.txt for license details.
    + */
    +declare(strict_types=1);
    +
    +namespace Magento\Csp\Plugin;
    +
    +use Magento\Framework\App\Area;
    +use Magento\Deploy\Package\Package;
    +use Magento\Deploy\Console\DeployStaticOptions;
    +use Magento\Deploy\Service\DeployStaticContent;
    +use Magento\Csp\Model\SubresourceIntegrityRepositoryPool;
    +
    +/**
    + * Plugin that removes existing integrity hashes for all assets.
    + */
    +class RemoveAllAssetIntegrityHashes
    +{
    +    /**
    +     * @var SubresourceIntegrityRepositoryPool
    +     */
    +    private SubresourceIntegrityRepositoryPool $integrityRepositoryPool;
    +
    +    /**
    +     * @param SubresourceIntegrityRepositoryPool $integrityRepositoryPool
    +     */
    +    public function __construct(
    +        SubresourceIntegrityRepositoryPool $integrityRepositoryPool
    +    ) {
    +        $this->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];
    +    }
    +}
    
  • app/code/Magento/Csp/Plugin/StoreAssetIntegrityHashes.php+70 0 added
    @@ -0,0 +1,70 @@
    +<?php
    +/**
    + * Copyright © Magento, Inc. All rights reserved.
    + * See COPYING.txt for license details.
    + */
    +declare(strict_types=1);
    +
    +namespace Magento\Csp\Plugin;
    +
    +use Magento\Deploy\Service\DeployStaticContent;
    +use Magento\Csp\Model\SubresourceIntegrityCollector;
    +use Magento\Csp\Model\SubresourceIntegrityRepositoryPool;
    +
    +/**
    + * Plugin that stores generated integrity hashes for all assets.
    + */
    +class StoreAssetIntegrityHashes
    +{
    +    /**
    +     * @var SubresourceIntegrityCollector
    +     */
    +    private SubresourceIntegrityCollector $integrityCollector;
    +
    +    /**
    +     * @var SubresourceIntegrityRepositoryPool
    +     */
    +    private SubresourceIntegrityRepositoryPool $integrityRepositoryPool;
    +
    +    /**
    +     * @param SubresourceIntegrityCollector $integrityCollector
    +     * @param SubresourceIntegrityRepositoryPool $integrityRepositoryPool
    +     */
    +    public function __construct(
    +        SubresourceIntegrityCollector $integrityCollector,
    +        SubresourceIntegrityRepositoryPool $integrityRepositoryPool
    +    ) {
    +        $this->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);
    +        }
    +    }
    +}
    
  • app/code/Magento/Csp/Test/Unit/Model/Mode/ConfigManagerTest.php+79 1 modified
    @@ -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');
    +    }
     }
    
  • app/code/Magento/Csp/Test/Unit/Model/SubresourceIntegrityRepositoryTest.php+130 0 added
    @@ -0,0 +1,130 @@
    +<?php
    +/**
    + * Copyright © Magento, Inc. All rights reserved.
    + * See COPYING.txt for license details.
    + */
    +declare(strict_types=1);
    +
    +namespace Magento\Csp\Test\Unit\Model;
    +
    +use Magento\Framework\App\CacheInterface;
    +use Magento\Framework\Serialize\SerializerInterface;
    +use PHPUnit\Framework\MockObject\MockObject;
    +use PHPUnit\Framework\TestCase;
    +use Magento\Csp\Model\SubresourceIntegrity;
    +use Magento\Csp\Model\SubresourceIntegrityRepository;
    +use Magento\Csp\Model\SubresourceIntegrityFactory;
    +
    +/**
    + * Unit Test for Class @see Magento\Csp\Model\SubresourceIntegrityRepository
    + *
    + */
    +class SubresourceIntegrityRepositoryTest extends TestCase
    +{
    +    /**
    +     * @var MockObject
    +     */
    +    private MockObject $cacheMock;
    +
    +    /**
    +     * @var MockObject
    +     */
    +    private MockObject $serializerMock;
    +
    +    /**
    +     * @var MockObject
    +     */
    +    private MockObject $integrityFactoryMock;
    +
    +    /**
    +     * Subject of testing.
    +     *
    +     * @var SubresourceIntegrityRepository|null
    +     */
    +    private ?SubresourceIntegrityRepository $subresourceIntegrityRepository = null;
    +
    +    /**
    +     * Initialize dependencies
    +     *
    +     * @return void
    +     */
    +    protected function setUp(): void
    +    {
    +        parent::setUp();
    +        $this->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));
    +    }
    +}
    
  • app/code/Magento/Csp/Test/Unit/Plugin/AddDefaultPropertiesToGroupPluginTest.php+113 0 added
    @@ -0,0 +1,113 @@
    +<?php
    +/**
    + * Copyright © Magento, Inc. All rights reserved.
    + * See COPYING.txt for license details.
    + */
    +declare(strict_types=1);
    +
    +namespace Magento\Csp\Test\Unit\Plugin;
    +
    +use Magento\Csp\Model\SubresourceIntegrity;
    +use Magento\Csp\Model\SubresourceIntegrityRepository;
    +use Magento\Csp\Model\SubresourceIntegrityRepositoryPool;
    +use PHPUnit\Framework\MockObject\MockObject;
    +use PHPUnit\Framework\TestCase;
    +use Magento\Csp\Plugin\AddDefaultPropertiesToGroupPlugin;
    +use Magento\Framework\View\Asset\File;
    +use Magento\Framework\View\Asset\GroupedCollection;
    +use Magento\Framework\App\State;
    +
    +/**
    + * Test for class Magento\Csp\Plugin\AddDefaultPropertiesToGroupPlugin
    + *
    + */
    +class AddDefaultPropertiesToGroupPluginTest extends TestCase
    +{
    +
    +    /**
    +     * @var MockObject
    +     */
    +    private MockObject $assetInterfaceMock;
    +
    +    /**
    +     * @var MockObject
    +     */
    +    private MockObject $integrityRepositoryPoolMock;
    +
    +    /**
    +     * @var MockObject
    +     */
    +    private MockObject $stateMock;
    +
    +    /**
    +     * @var AddDefaultPropertiesToGroupPlugin
    +     */
    +    private AddDefaultPropertiesToGroupPlugin $plugin;
    +
    +    /**
    +     * Initialize Dependencies
    +     *
    +     * @return void
    +     */
    +    protected function setUp(): void
    +    {
    +        parent::setUp();
    +        $this->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
    +            )
    +        );
    +    }
    +}
    
  • app/code/Magento/Csp/Test/Unit/Plugin/StoreAssetIntegrityHashesTest.php+96 0 added
    @@ -0,0 +1,96 @@
    +<?php
    +/**
    + * Copyright © Magento, Inc. All rights reserved.
    + * See COPYING.txt for license details.
    + */
    +declare(strict_types=1);
    +
    +namespace Magento\Csp\Test\Unit\Plugin;
    +
    +use Magento\Csp\Model\SubresourceIntegrity;
    +use Magento\Csp\Model\SubresourceIntegrityRepository;
    +use Magento\Csp\Model\SubresourceIntegrityRepositoryPool;
    +use Magento\Deploy\Service\DeployStaticContent;
    +use PHPUnit\Framework\MockObject\MockObject;
    +use Magento\Csp\Plugin\StoreAssetIntegrityHashes;
    +use Magento\Csp\Model\SubresourceIntegrityCollector;
    +use PHPUnit\Framework\TestCase;
    +
    +/**
    + * Plugin that removes existing integrity hashes for all assets.
    + */
    +class StoreAssetIntegrityHashesTest extends TestCase
    +{
    +    /**
    +     * @var MockObject
    +     */
    +    private MockObject $integrityRepositoryPoolMock;
    +
    +    /**
    +     * @var MockObject
    +     */
    +    private MockObject $integrityCollectorMock;
    +
    +    /**
    +     * @var StoreAssetIntegrityHashes
    +     */
    +    private StoreAssetIntegrityHashes $plugin;
    +
    +    /**
    +     * Initialize Dependencies
    +     *
    +     * @return void
    +     */
    +    protected function setUp(): void
    +    {
    +        parent::setUp();
    +        $this->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, []);
    +    }
    +}
    
  • app/code/Magento/Csp/view/adminhtml/layout/sales_order_create_index.xml+17 0 added
    @@ -0,0 +1,17 @@
    +<?xml version="1.0"?>
    +<!--
    +/**
    + * Copyright © Magento, Inc. All rights reserved.
    + * See COPYING.txt for license details.
    + */
    +-->
    +<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    +    <head>
    +        <link src="Magento_Csp::js/sri.js"/>
    +    </head>
    +    <body>
    +        <referenceBlock name="head.additional">
    +            <block class="Magento\Csp\Block\Sri\Hashes" name="admin.sri.hashes" template="Magento_Csp::sri/hashes.phtml"/>
    +        </referenceBlock>
    +    </body>
    +</page>
    
  • app/code/Magento/Csp/view/base/templates/sri/hashes.phtml+18 0 added
    @@ -0,0 +1,18 @@
    +<?php
    +/**
    + * Copyright © Magento, Inc. All rights reserved.
    + * See COPYING.txt for license details.
    + */
    +
    +/** @var \Magento\Csp\Block\Sri\Hashes $block */
    +/** @var \Magento\Framework\View\Helper\SecureHtmlRenderer $secureRenderer */
    +?>
    +
    +<?php $sriHashes = /* @noEscape */ $block->getSerialized();
    +$scriptString = <<<script
    +        window.sriHashes = {$sriHashes};
    +script;
    +?>
    +
    +<?= /* @noEscape */ $secureRenderer->renderTag('script', [], $scriptString, false) ?>
    +
    
  • app/code/Magento/Csp/view/base/web/js/sri.js+13 0 added
    @@ -0,0 +1,13 @@
    +/**
    + * Copyright © Magento, Inc. All rights reserved.
    + * See COPYING.txt for license details.
    + */
    +require.config({
    +    onNodeCreated: function (node, config, moduleName, url) {
    +        'use strict';
    +        if ('sriHashes' in window && url in window.sriHashes) {
    +            node.setAttribute('integrity', window.sriHashes[url]);
    +            node.setAttribute('crossorigin', 'anonymous');
    +        }
    +    }
    +});
    
  • app/code/Magento/Csp/view/frontend/layout/checkout_index_index.xml+17 0 added
    @@ -0,0 +1,17 @@
    +<?xml version="1.0"?>
    +<!--
    +/**
    + * Copyright © Magento, Inc. All rights reserved.
    + * See COPYING.txt for license details.
    + */
    +-->
    +<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="checkout" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    +    <head>
    +        <link src="Magento_Csp::js/sri.js"/>
    +    </head>
    +    <body>
    +        <referenceBlock name="head.additional">
    +            <block class="Magento\Csp\Block\Sri\Hashes" name="csp.sri.hashes" template="Magento_Csp::sri/hashes.phtml"/>
    +        </referenceBlock>
    +    </body>
    +</page>
    
  • app/code/Magento/CurrencySymbol/composer.json+13 11 modified
    @@ -1,23 +1,24 @@
     {
         "name": "magento/module-currency-symbol",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.3-p6",
         "require": {
             "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    -        "magento/module-backend": "*",
    -        "magento/module-config": "*",
    -        "magento/module-directory": "*",
    -        "magento/module-page-cache": "*",
    -        "magento/module-store": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-config": "101.2.*",
    +        "magento/module-directory": "100.4.*",
    +        "magento/module-page-cache": "100.4.*",
    +        "magento/module-store": "101.1.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -27,3 +28,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/CurrencySymbol/Test/Mftf/Test/AdminCheckCurrencyConverterApiConfigurationTest.xml+2 0 modified
    @@ -18,6 +18,8 @@
                 <testCaseId value="MC-28786"/>
                 <useCaseId value="MAGETWO-94919"/>
                 <group value="currency"/>
    +            <!--      Remove this group when Subscription is finalized or Mocking is enabled      -->
    +            <group value="pr_exclude" />
             </annotations>
             <before>
                 <!--Set currency configuration-->
    
  • app/code/Magento/CustomerAnalytics/composer.json+8 6 modified
    @@ -1,17 +1,18 @@
     {
         "name": "magento/module-customer-analytics",
         "description": "N/A",
    -    "require": {
    -        "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    -        "magento/module-customer": "*",
    -        "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-customer": "103.0.*",
    +        "magento/module-analytics": "100.4.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -21,3 +22,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Customer/Api/AccountManagementInterface.php+2 1 modified
    @@ -8,6 +8,7 @@
     namespace Magento\Customer\Api;
     
     use Magento\Framework\Exception\InputException;
    +use Magento\Framework\Exception\LocalizedException;
     
     /**
      * Interface for managing customers accounts.
    @@ -194,7 +195,7 @@ public function resendConfirmation($email, $websiteId, $redirectUrl = '');
          * Check if given email is associated with a customer account in given website.
          *
          * @param string $customerEmail
    -     * @param int $websiteId If not set, will use the current websiteId
    +     * @param int|null $websiteId If not set, will use the current websiteId
          * @return bool
          * @throws \Magento\Framework\Exception\LocalizedException
          */
    
  • app/code/Magento/Customer/composer.json+29 27 modified
    @@ -1,41 +1,42 @@
     {
         "name": "magento/module-customer",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "103.0.4-p9",
         "require": {
             "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    -        "magento/module-authorization": "*",
    -        "magento/module-backend": "*",
    -        "magento/module-catalog": "*",
    -        "magento/module-checkout": "*",
    -        "magento/module-config": "*",
    -        "magento/module-directory": "*",
    -        "magento/module-eav": "*",
    -        "magento/module-integration": "*",
    -        "magento/module-media-storage": "*",
    -        "magento/module-newsletter": "*",
    -        "magento/module-page-cache": "*",
    -        "magento/module-quote": "*",
    -        "magento/module-sales": "*",
    -        "magento/module-store": "*",
    -        "magento/module-tax": "*",
    -        "magento/module-theme": "*",
    -        "magento/module-ui": "*",
    -        "magento/module-wishlist": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-authorization": "100.4.*",
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-checkout": "100.4.*",
    +        "magento/module-config": "101.2.*",
    +        "magento/module-directory": "100.4.*",
    +        "magento/module-eav": "102.1.*",
    +        "magento/module-integration": "100.4.*",
    +        "magento/module-media-storage": "100.4.*",
    +        "magento/module-newsletter": "100.4.*",
    +        "magento/module-page-cache": "100.4.*",
    +        "magento/module-quote": "101.2.*",
    +        "magento/module-sales": "103.0.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/module-tax": "100.4.*",
    +        "magento/module-theme": "101.1.*",
    +        "magento/module-ui": "101.2.*",
    +        "magento/module-wishlist": "101.2.*"
         },
         "suggest": {
    -        "magento/module-cookie": "*",
    -        "magento/module-customer-sample-data": "*",
    -        "magento/module-webapi": "*"
    +        "magento/module-cookie": "100.4.*",
    +        "magento/module-customer-sample-data": "Sample Data version: 100.4.*",
    +        "magento/module-webapi": "100.4.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -45,3 +46,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Customer/Controller/Account/Confirmation.php+36 26 modified
    @@ -3,6 +3,8 @@
      * Copyright © Magento, Inc. All rights reserved.
      * See COPYING.txt for license details.
      */
    +declare(strict_types=1);
    +
     namespace Magento\Customer\Controller\Account;
     
     use Magento\Customer\Api\AccountManagementInterface;
    @@ -13,22 +15,26 @@
     use Magento\Framework\App\Action\HttpPostActionInterface as HttpPostActionInterface;
     use Magento\Framework\App\Action\Context;
     use Magento\Framework\App\ObjectManager;
    +use Magento\Framework\Controller\Result\Redirect;
    +use Magento\Framework\Exception\LocalizedException;
    +use Magento\Framework\Exception\NoSuchEntityException;
     use Magento\Framework\Exception\State\InvalidTransitionException;
    +use Magento\Framework\View\Result\Page;
     use Magento\Framework\View\Result\PageFactory;
     use Magento\Store\Model\StoreManagerInterface;
     
     /**
    - * Class Confirmation. Send confirmation link to specified email
    + * Send confirmation link to specified email
      */
     class Confirmation extends AbstractAccount implements HttpGetActionInterface, HttpPostActionInterface
     {
         /**
    -     * @var \Magento\Store\Model\StoreManagerInterface
    +     * @var StoreManagerInterface
          */
         protected $storeManager;
     
         /**
    -     * @var \Magento\Customer\Api\AccountManagementInterface
    +     * @var AccountManagementInterface
          */
         protected $customerAccountManagement;
     
    @@ -53,7 +59,7 @@ class Confirmation extends AbstractAccount implements HttpGetActionInterface, Ht
          * @param PageFactory $resultPageFactory
          * @param StoreManagerInterface $storeManager
          * @param AccountManagementInterface $customerAccountManagement
    -     * @param Url $customerUrl
    +     * @param Url|null $customerUrl
          */
         public function __construct(
             Context $context,
    @@ -74,48 +80,52 @@ public function __construct(
         /**
          * Send confirmation link to specified email
          *
    -     * @return \Magento\Framework\Controller\Result\Redirect|\Magento\Framework\View\Result\Page
    +     * @return Redirect|Page
    +     * @throws LocalizedException
          */
         public function execute()
         {
             if ($this->session->isLoggedIn()) {
    -            /** @var \Magento\Framework\Controller\Result\Redirect $resultRedirect */
    -            $resultRedirect = $this->resultRedirectFactory->create();
    -            $resultRedirect->setPath('*/*/');
    -            return $resultRedirect;
    +            return $this->getRedirect('*/*/');
             }
     
    -        // try to confirm by email
             $email = $this->getRequest()->getPost('email');
    -        if ($email) {
    -            /** @var \Magento\Framework\Controller\Result\Redirect $resultRedirect */
    -            $resultRedirect = $this->resultRedirectFactory->create();
     
    +        if ($email) {
                 try {
                     $this->customerAccountManagement->resendConfirmation(
                         $email,
                         $this->storeManager->getStore()->getWebsiteId()
                     );
                     $this->messageManager->addSuccessMessage(__('Please check your email for confirmation key.'));
    +                return $this->getRedirect('*/*/index', ['_secure' => true]);
                 } catch (InvalidTransitionException $e) {
                     $this->messageManager->addSuccessMessage(__('This email does not require confirmation.'));
    -            } catch (\Exception $e) {
    -                $this->messageManager->addExceptionMessage($e, __('Wrong email.'));
    -                $resultRedirect->setPath('*/*/*', ['email' => $email, '_secure' => true]);
    -                return $resultRedirect;
    +                return $this->getRedirect('*/*/index', ['_secure' => true]);
    +            } catch (NoSuchEntityException $e) {
    +                $this->messageManager->addErrorMessage(__('Wrong email.'));
                 }
    -            $this->session->setUsername($email);
    -            $resultRedirect->setPath('*/*/index', ['_secure' => true]);
    -            return $resultRedirect;
             }
     
    -        /** @var \Magento\Framework\View\Result\Page $resultPage */
             $resultPage = $this->resultPageFactory->create();
    -        $resultPage->getLayout()->getBlock('accountConfirmation')->setEmail(
    -            $this->getRequest()->getParam('email', $email)
    -        )->setLoginUrl(
    -            $this->customerUrl->getLoginUrl()
    -        );
    +        $resultPage->getLayout()->getBlock('accountConfirmation')
    +            ->setEmail($email)
    +            ->setLoginUrl($this->customerUrl->getLoginUrl());
             return $resultPage;
         }
    +
    +    /**
    +     * Returns redirect object
    +     *
    +     * @param string $path
    +     * @param array $params
    +     * @return Redirect
    +     */
    +    private function getRedirect(string $path, array $params = []): Redirect
    +    {
    +        $resultRedirect = $this->resultRedirectFactory->create();
    +        $resultRedirect->setPath($path, $params);
    +
    +        return $resultRedirect;
    +    }
     }
    
  • app/code/Magento/Customer/Controller/Account/Confirm.php+53 25 modified
    @@ -1,9 +1,10 @@
     <?php
     /**
    - *
      * Copyright © Magento, Inc. All rights reserved.
      * See COPYING.txt for license details.
      */
    +declare(strict_types=1);
    +
     namespace Magento\Customer\Controller\Account;
     
     use Magento\Customer\Api\AccountManagementInterface;
    @@ -15,11 +16,15 @@
     use Magento\Framework\App\Action\Context;
     use Magento\Framework\App\Action\HttpGetActionInterface as HttpGetActionInterface;
     use Magento\Framework\App\Config\ScopeConfigInterface;
    +use Magento\Framework\App\ObjectManager;
     use Magento\Framework\Controller\ResultFactory;
    +use Magento\Framework\Exception\NoSuchEntityException;
    +use Magento\Framework\Phrase;
     use Magento\Framework\UrlFactory;
     use Magento\Framework\Exception\StateException;
     use Magento\Store\Model\ScopeInterface;
     use Magento\Store\Model\StoreManagerInterface;
    +use Magento\Customer\Model\Logger as CustomerLogger;
     
     /**
      * Class Confirm
    @@ -75,6 +80,11 @@ class Confirm extends AbstractAccount implements HttpGetActionInterface
          */
         private $cookieMetadataManager;
     
    +    /**
    +     * @var CustomerLogger
    +     */
    +    private CustomerLogger $customerLogger;
    +
         /**
          * @param Context $context
          * @param Session $customerSession
    @@ -84,6 +94,7 @@ class Confirm extends AbstractAccount implements HttpGetActionInterface
          * @param CustomerRepositoryInterface $customerRepository
          * @param Address $addressHelper
          * @param UrlFactory $urlFactory
    +     * @param CustomerLogger|null $customerLogger
          */
         public function __construct(
             Context $context,
    @@ -93,7 +104,8 @@ public function __construct(
             AccountManagementInterface $customerAccountManagement,
             CustomerRepositoryInterface $customerRepository,
             Address $addressHelper,
    -        UrlFactory $urlFactory
    +        UrlFactory $urlFactory,
    +        ?CustomerLogger $customerLogger = null
         ) {
             $this->session = $customerSession;
             $this->scopeConfig = $scopeConfig;
    @@ -102,13 +114,13 @@ public function __construct(
             $this->customerRepository = $customerRepository;
             $this->addressHelper = $addressHelper;
             $this->urlModel = $urlFactory->create();
    +        $this->customerLogger = $customerLogger ?? ObjectManager::getInstance()->get(CustomerLogger::class);
             parent::__construct($context);
         }
     
         /**
          * Retrieve cookie manager
          *
    -     * @deprecated 101.0.0
          * @return \Magento\Framework\Stdlib\Cookie\PhpCookieManager
          */
         private function getCookieManager()
    @@ -124,7 +136,6 @@ private function getCookieManager()
         /**
          * Retrieve cookie metadata factory
          *
    -     * @deprecated 101.0.0
          * @return \Magento\Framework\Stdlib\Cookie\CookieMetadataFactory
          */
         private function getCookieMetadataFactory()
    @@ -152,25 +163,32 @@ public function execute()
                 return $resultRedirect;
             }
     
    -        $customerId = $this->getRequest()->getParam('id', false);
    +        $customerId = $this->getCustomerId();
             $key = $this->getRequest()->getParam('key', false);
             if (empty($customerId) || empty($key)) {
                 $this->messageManager->addErrorMessage(__('Bad request.'));
                 $url = $this->urlModel->getUrl('*/*/index', ['_secure' => true]);
    +            // phpcs:ignore Magento2.Legacy.ObsoleteResponse
                 return $resultRedirect->setUrl($this->_redirect->error($url));
             }
     
             try {
                 // log in and send greeting email
                 $customerEmail = $this->customerRepository->getById($customerId)->getEmail();
                 $customer = $this->customerAccountManagement->activate($customerEmail, $key);
    +            $successMessage = $this->getSuccessMessage();
                 $this->session->setCustomerDataAsLoggedIn($customer);
    +
                 if ($this->getCookieManager()->getCookie('mage-cache-sessid')) {
                     $metadata = $this->getCookieMetadataFactory()->createCookieMetadata();
                     $metadata->setPath('/');
                     $this->getCookieManager()->deleteCookie('mage-cache-sessid', $metadata);
                 }
    -            $this->messageManager->addSuccess($this->getSuccessMessage());
    +
    +            if ($successMessage) {
    +                $this->messageManager->addSuccess($successMessage);
    +            }
    +
                 $resultRedirect->setUrl($this->getSuccessRedirect());
                 return $resultRedirect;
             } catch (StateException $e) {
    @@ -180,36 +198,45 @@ public function execute()
             }
     
             $url = $this->urlModel->getUrl('*/*/index', ['_secure' => true]);
    +        // phpcs:ignore Magento2.Legacy.ObsoleteResponse
             return $resultRedirect->setUrl($this->_redirect->error($url));
         }
     
    +    /**
    +     * Returns customer id from request
    +     *
    +     * @return int
    +     */
    +    private function getCustomerId(): int
    +    {
    +        return (int)$this->getRequest()->getParam('id', 0);
    +    }
    +
         /**
          * Retrieve success message
          *
    -     * @return string
    +     * @return Phrase|null
    +     * @throws NoSuchEntityException
          */
         protected function getSuccessMessage()
         {
             if ($this->addressHelper->isVatValidationEnabled()) {
    -            if ($this->addressHelper->getTaxCalculationAddressType() == Address::TYPE_SHIPPING) {
    -                // @codingStandardsIgnoreStart
    -                $message = __(
    -                    'If you are a registered VAT customer, please click <a href="%1">here</a> to enter your shipping address for proper VAT calculation.',
    -                    $this->urlModel->getUrl('customer/address/edit')
    -                );
    -                // @codingStandardsIgnoreEnd
    -            } else {
    -                // @codingStandardsIgnoreStart
    -                $message = __(
    -                    'If you are a registered VAT customer, please click <a href="%1">here</a> to enter your billing address for proper VAT calculation.',
    -                    $this->urlModel->getUrl('customer/address/edit')
    -                );
    -                // @codingStandardsIgnoreEnd
    -            }
    -        } else {
    -            $message = __('Thank you for registering with %1.', $this->storeManager->getStore()->getFrontendName());
    +            return __(
    +                $this->addressHelper->getTaxCalculationAddressType() == Address::TYPE_SHIPPING
    +                    ? 'If you are a registered VAT customer, please click <a href="%1">here</a> to enter your '
    +                    .'shipping address for proper VAT calculation.'
    +                    :'If you are a registered VAT customer, please click <a href="%1">here</a> to enter your '
    +                    .'billing address for proper VAT calculation.',
    +                $this->urlModel->getUrl('customer/address/edit')
    +            );
             }
    -        return $message;
    +
    +        $customerId = $this->getCustomerId();
    +        if ($customerId && $this->customerLogger->get($customerId)->getLastLoginAt()) {
    +            return null;
    +        }
    +
    +        return __('Thank you for registering with %1.', $this->storeManager->getStore()->getFrontendName());
         }
     
         /**
    @@ -229,6 +256,7 @@ protected function getSuccessRedirect()
             } else {
                 $successUrl = $this->urlModel->getUrl('*/*/index', ['_secure' => true]);
             }
    +        // phpcs:ignore Magento2.Legacy.ObsoleteResponse
             return $this->_redirect->success($backUrl ? $backUrl : $successUrl);
         }
     }
    
  • app/code/Magento/Customer/Controller/Account/EditPost.php+73 35 modified
    @@ -1,6 +1,5 @@
     <?php
     /**
    - *
      * Copyright © Magento, Inc. All rights reserved.
      * See COPYING.txt for license details.
      */
    @@ -10,7 +9,9 @@
     
     use Magento\Customer\Api\Data\CustomerInterface;
     use Magento\Customer\Api\SessionCleanerInterface;
    +use Magento\Customer\Model\AccountConfirmation;
     use Magento\Customer\Model\AddressRegistry;
    +use Magento\Customer\Model\Url;
     use Magento\Framework\App\Action\HttpPostActionInterface as HttpPostActionInterface;
     use Magento\Customer\Model\AuthenticationInterface;
     use Magento\Customer\Model\Customer\Mapper;
    @@ -27,10 +28,12 @@
     use Magento\Customer\Model\Session;
     use Magento\Framework\App\Action\Context;
     use Magento\Framework\Escaper;
    +use Magento\Framework\Exception\FileSystemException;
     use Magento\Framework\Exception\InputException;
     use Magento\Framework\Exception\InvalidEmailOrPasswordException;
     use Magento\Framework\Exception\LocalizedException;
     use Magento\Framework\Exception\NoSuchEntityException;
    +use Magento\Framework\Exception\SessionException;
     use Magento\Framework\Exception\State\UserLockedException;
     use Magento\Customer\Controller\AbstractAccount;
     use Magento\Framework\Phrase;
    @@ -41,18 +44,19 @@
      * Customer edit account information controller
      *
      * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
    + * @SuppressWarnings(PHPMD.ExcessiveParameterList)
      */
     class EditPost extends AbstractAccount implements CsrfAwareActionInterface, HttpPostActionInterface
     {
         /**
          * Form code for data extractor
          */
    -    const FORM_DATA_EXTRACTOR_CODE = 'customer_account_edit';
    +    public const FORM_DATA_EXTRACTOR_CODE = 'customer_account_edit';
     
         /**
          * @var AccountManagementInterface
          */
    -    protected $customerAccountManagement;
    +    protected $accountManagement;
     
         /**
          * @var CustomerRepositoryInterface
    @@ -105,44 +109,61 @@ class EditPost extends AbstractAccount implements CsrfAwareActionInterface, Http
         private $filesystem;
     
         /**
    -     * @var SessionCleanerInterface|null
    +     * @var SessionCleanerInterface
          */
         private $sessionCleaner;
     
    +    /**
    +     * @var AccountConfirmation
    +     */
    +    private $accountConfirmation;
    +
    +    /**
    +     * @var Url
    +     */
    +    private Url $customerUrl;
    +
         /**
          * @param Context $context
          * @param Session $customerSession
    -     * @param AccountManagementInterface $customerAccountManagement
    +     * @param AccountManagementInterface $accountManagement
          * @param CustomerRepositoryInterface $customerRepository
          * @param Validator $formKeyValidator
          * @param CustomerExtractor $customerExtractor
          * @param Escaper|null $escaper
          * @param AddressRegistry|null $addressRegistry
    -     * @param Filesystem $filesystem
    +     * @param Filesystem|null $filesystem
          * @param SessionCleanerInterface|null $sessionCleaner
    +     * @param AccountConfirmation|null $accountConfirmation
    +     * @param Url|null $customerUrl
          */
         public function __construct(
             Context $context,
             Session $customerSession,
    -        AccountManagementInterface $customerAccountManagement,
    +        AccountManagementInterface $accountManagement,
             CustomerRepositoryInterface $customerRepository,
             Validator $formKeyValidator,
             CustomerExtractor $customerExtractor,
             ?Escaper $escaper = null,
    -        AddressRegistry $addressRegistry = null,
    -        Filesystem $filesystem = null,
    -        ?SessionCleanerInterface $sessionCleaner = null
    +        ?AddressRegistry $addressRegistry = null,
    +        ?Filesystem $filesystem = null,
    +        ?SessionCleanerInterface $sessionCleaner = null,
    +        ?AccountConfirmation $accountConfirmation = null,
    +        ?Url $customerUrl = null
         ) {
             parent::__construct($context);
             $this->session = $customerSession;
    -        $this->customerAccountManagement = $customerAccountManagement;
    +        $this->accountManagement = $accountManagement;
             $this->customerRepository = $customerRepository;
             $this->formKeyValidator = $formKeyValidator;
             $this->customerExtractor = $customerExtractor;
             $this->escaper = $escaper ?: ObjectManager::getInstance()->get(Escaper::class);
             $this->addressRegistry = $addressRegistry ?: ObjectManager::getInstance()->get(AddressRegistry::class);
             $this->filesystem = $filesystem ?: ObjectManager::getInstance()->get(Filesystem::class);
             $this->sessionCleaner = $sessionCleaner ?: ObjectManager::getInstance()->get(SessionCleanerInterface::class);
    +        $this->accountConfirmation = $accountConfirmation ?:
    +            ObjectManager::getInstance()->get(AccountConfirmation::class);
    +        $this->customerUrl = $customerUrl ?: ObjectManager::getInstance()->get(Url::class);
         }
     
         /**
    @@ -164,7 +185,6 @@ private function getAuthentication()
          * Get email notification
          *
          * @return EmailNotificationInterface
    -     * @deprecated 100.1.0
          */
         private function getEmailNotification()
         {
    @@ -180,7 +200,6 @@ private function getEmailNotification()
          */
         public function createCsrfValidationException(RequestInterface $request): ?InvalidRequestException
         {
    -        /** @var Redirect $resultRedirect */
             $resultRedirect = $this->resultRedirectFactory->create();
             $resultRedirect->setPath('*/*/edit');
     
    @@ -203,50 +222,49 @@ public function validateForCsrf(RequestInterface $request): ?bool
          *
          * @return Redirect
          * @SuppressWarnings(PHPMD.CyclomaticComplexity)
    +     * @throws SessionException
          */
         public function execute()
         {
    -        /** @var Redirect $resultRedirect */
             $resultRedirect = $this->resultRedirectFactory->create();
             $validFormKey = $this->formKeyValidator->validate($this->getRequest());
     
             if ($validFormKey && $this->getRequest()->isPost()) {
    -            $currentCustomerDataObject = $this->getCustomerDataObject($this->session->getCustomerId());
    -            $customerCandidateDataObject = $this->populateNewCustomerDataObject(
    -                $this->_request,
    -                $currentCustomerDataObject
    -            );
    +            $customer = $this->getCustomerDataObject($this->session->getCustomerId());
    +            $customerCandidate = $this->populateNewCustomerDataObject($this->_request, $customer);
     
                 $attributeToDelete = $this->_request->getParam('delete_attribute_value');
                 if ($attributeToDelete !== null) {
    -                $this->deleteCustomerFileAttribute(
    -                    $customerCandidateDataObject,
    -                    $attributeToDelete
    -                );
    +                $this->deleteCustomerFileAttribute($customerCandidate, $attributeToDelete);
                 }
     
                 try {
                     // whether a customer enabled change email option
    -                $isEmailChanged = $this->processChangeEmailRequest($currentCustomerDataObject);
    +                $isEmailChanged = $this->processChangeEmailRequest($customer);
     
                     // whether a customer enabled change password option
    -                $isPasswordChanged = $this->changeCustomerPassword($currentCustomerDataObject->getEmail());
    +                $isPasswordChanged = $this->changeCustomerPassword($customer->getEmail());
     
                     // No need to validate customer address while editing customer profile
    -                $this->disableAddressValidation($customerCandidateDataObject);
    +                $this->disableAddressValidation($customerCandidate);
    +
    +                $this->customerRepository->save($customerCandidate);
    +                $updatedCustomer = $this->customerRepository->getById($customerCandidate->getId());
     
    -                $this->customerRepository->save($customerCandidateDataObject);
                     $this->getEmailNotification()->credentialsChanged(
    -                    $customerCandidateDataObject,
    -                    $currentCustomerDataObject->getEmail(),
    +                    $updatedCustomer,
    +                    $customer->getEmail(),
                         $isPasswordChanged
                     );
    -                $this->dispatchSuccessEvent($customerCandidateDataObject);
    +
    +                $this->dispatchSuccessEvent($updatedCustomer);
                     $this->messageManager->addSuccessMessage(__('You saved the account information.'));
                     // logout from current session if password or email changed.
                     if ($isPasswordChanged || $isEmailChanged) {
                         $this->session->logout();
                         $this->session->start();
    +                    $this->addComplexSuccessMessage($customer, $updatedCustomer);
    +
                         return $resultRedirect->setPath('customer/account/login');
                     }
                     return $resultRedirect->setPath('customer/account');
    @@ -276,13 +294,32 @@ public function execute()
                 $this->session->setCustomerFormData($this->getRequest()->getPostValue());
             }
     
    -        /** @var Redirect $resultRedirect */
             $resultRedirect = $this->resultRedirectFactory->create();
             $resultRedirect->setPath('*/*/edit');
     
             return $resultRedirect;
         }
     
    +    /**
    +     * Adds a complex success message if email confirmation is required
    +     *
    +     * @param CustomerInterface $outdatedCustomer
    +     * @param CustomerInterface $updatedCustomer
    +     * @throws LocalizedException
    +     */
    +    private function addComplexSuccessMessage(
    +        CustomerInterface $outdatedCustomer,
    +        CustomerInterface $updatedCustomer
    +    ): void {
    +        if (($outdatedCustomer->getEmail() !== $updatedCustomer->getEmail())
    +            && $this->accountConfirmation->isCustomerEmailChangedConfirmRequired($updatedCustomer)) {
    +            $this->messageManager->addComplexSuccessMessage(
    +                'confirmAccountSuccessMessage',
    +                ['url' => $this->customerUrl->getEmailConfirmationUrl($updatedCustomer->getEmail())]
    +            );
    +        }
    +    }
    +
         /**
          * Account editing action completed successfully event
          *
    @@ -303,6 +340,8 @@ private function dispatchSuccessEvent(CustomerInterface $customerCandidateDataOb
          * @param int $customerId
          *
          * @return CustomerInterface
    +     * @throws LocalizedException
    +     * @throws NoSuchEntityException
          */
         private function getCustomerDataObject($customerId)
         {
    @@ -342,7 +381,7 @@ private function populateNewCustomerDataObject(
          *
          * @param string $email
          * @return boolean
    -     * @throws InvalidEmailOrPasswordException|InputException
    +     * @throws InvalidEmailOrPasswordException|InputException|LocalizedException
          */
         protected function changeCustomerPassword($email)
         {
    @@ -355,7 +394,7 @@ protected function changeCustomerPassword($email)
                     throw new InputException(__('Password confirmation doesn\'t match entered password.'));
                 }
     
    -            $isPasswordChanged = $this->customerAccountManagement->changePassword($email, $currPass, $newPass);
    +            $isPasswordChanged = $this->accountManagement->changePassword($email, $currPass, $newPass);
             }
     
             return $isPasswordChanged;
    @@ -393,8 +432,6 @@ private function processChangeEmailRequest(CustomerInterface $currentCustomerDat
          * Get Customer Mapper instance
          *
          * @return Mapper
    -     *
    -     * @deprecated 100.1.3
          */
         private function getCustomerMapper()
         {
    @@ -424,6 +461,7 @@ private function disableAddressValidation($customer)
          * @param CustomerInterface $customerCandidateDataObject
          * @param string $attributeToDelete
          * @return void
    +     * @throws FileSystemException
          */
         private function deleteCustomerFileAttribute(
             CustomerInterface $customerCandidateDataObject,
    
  • app/code/Magento/CustomerDownloadableGraphQl/composer.json+10 8 modified
    @@ -2,19 +2,20 @@
         "name": "magento/module-customer-downloadable-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/module-downloadable-graph-ql": "*",
    -        "magento/module-graph-ql": "*",
    -        "magento/framework": "*"
    +        "magento/module-downloadable-graph-ql": "100.4.*",
    +        "magento/module-graph-ql": "100.4.*",
    +        "magento/framework": "103.0.*"
         },
         "suggest": {
    -        "magento/module-catalog-graph-ql": "*"
    +        "magento/module-catalog-graph-ql": "100.4.*"
         },
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -24,3 +25,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Customer/etc/adminhtml/system.xml+4 0 modified
    @@ -193,6 +193,10 @@
                         <comment>Email template chosen based on theme fallback when "Default" option is selected.</comment>
                         <source_model>Magento\Config\Model\Config\Source\Email\Template</source_model>
                     </field>
    +                <field id="confirm" translate="label" type="select" sortOrder="30" showInDefault="1" showInWebsite="1" canRestore="1">
    +                    <label>Require email confirmation if email has been changed</label>
    +                    <source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
    +                </field>
                 </group>
                 <group id="address" translate="label" sortOrder="40" showInDefault="1" showInWebsite="1" showInStore="1">
                     <label>Name and Address Options</label>
    
  • app/code/Magento/Customer/etc/config.xml+1 0 modified
    @@ -32,6 +32,7 @@
                 <account_information>
                     <change_email_template>customer_account_information_change_email_template</change_email_template>
                     <change_email_and_password_template>customer_account_information_change_email_and_password_template</change_email_and_password_template>
    +                <confirm>0</confirm>
                 </account_information>
                 <password>
                     <forgot_email_identity>support</forgot_email_identity>
    
  • app/code/Magento/Customer/etc/frontend/di.xml+3 0 modified
    @@ -127,4 +127,7 @@
                 </argument>
             </arguments>
         </type>
    +    <type name="Magento\Customer\Model\Session">
    +        <plugin name="afterLogout" type="Magento\Customer\Model\Plugin\ClearSessionsAfterLogoutPlugin"/>
    +    </type>
     </config>
    
  • app/code/Magento/CustomerGraphQl/composer.json+16 14 modified
    @@ -2,24 +2,25 @@
         "name": "magento/module-customer-graph-ql",
         "description": "N/A",
         "type": "magento2-module",
    -    "require": {
    -        "php": "~7.4.0||~8.1.0",
    -        "magento/module-authorization": "*",
    -        "magento/module-customer": "*",
    -        "magento/module-eav": "*",
    -        "magento/module-graph-ql": "*",
    -        "magento/module-newsletter": "*",
    -        "magento/module-integration": "*",
    -        "magento/module-store": "*",
    -        "magento/framework": "*",
    -        "magento/module-directory": "*",
    -        "magento/module-tax": "*",
    -        "magento/module-graph-ql-cache": "*"
    -    },
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "100.4.4",
    +    "require": {
    +        "php": "~7.4.0||~8.1.0",
    +        "magento/module-authorization": "100.4.*",
    +        "magento/module-customer": "103.0.*",
    +        "magento/module-eav": "102.1.*",
    +        "magento/module-graph-ql": "100.4.*",
    +        "magento/module-newsletter": "100.4.*",
    +        "magento/module-integration": "100.4.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/framework": "103.0.*",
    +        "magento/module-directory": "100.4.*",
    +        "magento/module-tax": "100.4.*",
    +        "magento/module-graph-ql-cache": "100.4.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -29,3 +30,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/CustomerImportExport/composer.json+14 12 modified
    @@ -1,24 +1,25 @@
     {
         "name": "magento/module-customer-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-backend": "*",
    -        "magento/module-customer": "*",
    -        "magento/module-directory": "*",
    -        "magento/module-eav": "*",
    -        "magento/module-import-export": "*",
    -        "magento/module-store": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-customer": "103.0.*",
    +        "magento/module-directory": "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 @@
             }
         }
     }
    +
    
  • app/code/Magento/Customer/Model/AccountConfirmation.php+73 2 modified
    @@ -3,8 +3,11 @@
      * Copyright © Magento, Inc. All rights reserved.
      * See COPYING.txt for license details.
      */
    +declare(strict_types=1);
    +
     namespace Magento\Customer\Model;
     
    +use Magento\Customer\Api\Data\CustomerInterface;
     use Magento\Store\Model\ScopeInterface;
     use Magento\Framework\App\Config\ScopeConfigInterface;
     use Magento\Framework\Registry;
    @@ -15,9 +18,29 @@
     class AccountConfirmation
     {
         /**
    -     * Configuration path for email confirmation.
    +     * Configuration path for email confirmation when creating a new customer
    +     */
    +    public const XML_PATH_IS_CONFIRM = 'customer/create_account/confirm';
    +
    +    /**
    +     * Configuration path for email confirmation when updating an existing customer's email
    +     */
    +    public const XML_PATH_IS_CONFIRM_EMAIL_CHANGED = 'customer/account_information/confirm';
    +
    +    /**
    +     * Constant for confirmed status
    +     */
    +    private const ACCOUNT_CONFIRMED = 'account_confirmed';
    +
    +    /**
    +     * Constant for confirmation required status
          */
    -    const XML_PATH_IS_CONFIRM = 'customer/create_account/confirm';
    +    private const ACCOUNT_CONFIRMATION_REQUIRED = 'account_confirmation_required';
    +
    +    /**
    +     * Constant for confirmation not required status
    +     */
    +    private const ACCOUNT_CONFIRMATION_NOT_REQUIRED = 'account_confirmation_not_required';
     
         /**
          * @var ScopeConfigInterface
    @@ -64,6 +87,54 @@ public function isConfirmationRequired($websiteId, $customerId, $customerEmail):
             );
         }
     
    +    /**
    +     * Check if accounts confirmation is required if email has been changed
    +     *
    +     * @param int|null $websiteId
    +     * @param int|null $customerId
    +     * @param string|null $customerEmail
    +     * @return bool
    +     */
    +    public function isEmailChangedConfirmationRequired($websiteId, $customerId, $customerEmail): bool
    +    {
    +        return !$this->canSkipConfirmation($customerId, $customerEmail)
    +            && $this->scopeConfig->isSetFlag(
    +                self::XML_PATH_IS_CONFIRM_EMAIL_CHANGED,
    +                ScopeInterface::SCOPE_WEBSITES,
    +                $websiteId
    +            );
    +    }
    +
    +    /**
    +     * Returns an email confirmation status if email has been changed
    +     *
    +     * @param CustomerInterface $customer
    +     * @return string
    +     */
    +    private function getEmailChangedConfirmStatus(CustomerInterface $customer): string
    +    {
    +        $isEmailChangedConfirmationRequired = $this->isEmailChangedConfirmationRequired(
    +            (int)$customer->getWebsiteId(),
    +            (int)$customer->getId(),
    +            $customer->getEmail()
    +        );
    +
    +        return $isEmailChangedConfirmationRequired
    +            ? $customer->getConfirmation() ? self::ACCOUNT_CONFIRMATION_REQUIRED : self::ACCOUNT_CONFIRMED
    +            : self::ACCOUNT_CONFIRMATION_NOT_REQUIRED;
    +    }
    +
    +    /**
    +     * Checks if email confirmation is required for the customer
    +     *
    +     * @param CustomerInterface $customer
    +     * @return bool
    +     */
    +    public function isCustomerEmailChangedConfirmRequired(CustomerInterface $customer):bool
    +    {
    +        return $this->getEmailChangedConfirmStatus($customer) === self::ACCOUNT_CONFIRMATION_REQUIRED;
    +    }
    +
         /**
          * Check whether confirmation may be skipped when registering using certain email address.
          *
    
  • app/code/Magento/Customer/Model/AccountManagement.php+81 25 modified
    @@ -3,6 +3,7 @@
      * Copyright © Magento, Inc. All rights reserved.
      * See COPYING.txt for license details.
      */
    +declare(strict_types=1);
     
     namespace Magento\Customer\Model;
     
    @@ -56,6 +57,7 @@
     use Magento\Store\Model\ScopeInterface;
     use Magento\Store\Model\StoreManagerInterface;
     use Psr\Log\LoggerInterface as PsrLogger;
    +use Magento\Customer\Model\Logger as CustomerLogger;
     
     /**
      * Handle various customer account actions
    @@ -67,6 +69,11 @@
      */
     class AccountManagement implements AccountManagementInterface
     {
    +    /**
    +     * System Configuration Path for Enable/Disable Login at Guest Checkout
    +     */
    +    public const GUEST_CHECKOUT_LOGIN_OPTION_SYS_CONFIG = 'checkout/options/enable_guest_checkout_login';
    +
         /**
          * Configuration paths for create account email template
          *
    @@ -219,7 +226,7 @@ class AccountManagement implements AccountManagementInterface
         private $customerFactory;
     
         /**
    -     * @var \Magento\Customer\Api\Data\ValidationResultsInterfaceFactory
    +     * @var ValidationResultsInterfaceFactory
          */
         private $validationResultsDataFactory;
     
    @@ -229,7 +236,7 @@ class AccountManagement implements AccountManagementInterface
         private $eventManager;
     
         /**
    -     * @var \Magento\Store\Model\StoreManagerInterface
    +     * @var StoreManagerInterface
          */
         private $storeManager;
     
    @@ -299,7 +306,7 @@ class AccountManagement implements AccountManagementInterface
         protected $dataProcessor;
     
         /**
    -     * @var \Magento\Framework\Registry
    +     * @var Registry
          */
         protected $registry;
     
    @@ -319,7 +326,7 @@ class AccountManagement implements AccountManagementInterface
         protected $objectFactory;
     
         /**
    -     * @var \Magento\Framework\Api\ExtensibleDataObjectConverter
    +     * @var ExtensibleDataObjectConverter
          */
         protected $extensibleDataObjectConverter;
     
    @@ -339,7 +346,7 @@ class AccountManagement implements AccountManagementInterface
         private $emailNotification;
     
         /**
    -     * @var \Magento\Eav\Model\Validator\Attribute\Backend
    +     * @var Backend
          */
         private $eavValidator;
     
    @@ -388,6 +395,11 @@ class AccountManagement implements AccountManagementInterface
          */
         private $authorization;
     
    +    /**
    +     * @var CustomerLogger
    +     */
    +    private CustomerLogger $customerLogger;
    +
         /**
          * @param CustomerFactory $customerFactory
          * @param ManagerInterface $eventManager
    @@ -426,6 +438,7 @@ class AccountManagement implements AccountManagementInterface
          * @param AuthorizationInterface|null $authorization
          * @param AuthenticationInterface|null $authentication
          * @param Backend|null $eavValidator
    +     * @param CustomerLogger|null $customerLogger
          * @SuppressWarnings(PHPMD.CyclomaticComplexity)
          * @SuppressWarnings(PHPMD.ExcessiveParameterList)
          * @SuppressWarnings(PHPMD.NPathComplexity)
    @@ -469,7 +482,8 @@ public function __construct(
             SessionCleanerInterface $sessionCleaner = null,
             AuthorizationInterface $authorization = null,
             AuthenticationInterface $authentication = null,
    -        Backend $eavValidator = null
    +        Backend $eavValidator = null,
    +        ?CustomerLogger $customerLogger = null
         ) {
             $this->customerFactory = $customerFactory;
             $this->eventManager = $eventManager;
    @@ -512,6 +526,7 @@ public function __construct(
             $this->authorization = $authorization ?? $objectManager->get(AuthorizationInterface::class);
             $this->authentication = $authentication ?? $objectManager->get(AuthenticationInterface::class);
             $this->eavValidator = $eavValidator ?? $objectManager->get(Backend::class);
    +        $this->customerLogger = $customerLogger ?? $objectManager->get(CustomerLogger::class);
         }
     
         /**
    @@ -562,9 +577,9 @@ public function activateById($customerId, $confirmationKey)
         /**
          * Activate a customer account using a key that was sent in a confirmation email.
          *
    -     * @param \Magento\Customer\Api\Data\CustomerInterface $customer
    +     * @param CustomerInterface $customer
          * @param string $confirmationKey
    -     * @return \Magento\Customer\Api\Data\CustomerInterface
    +     * @return CustomerInterface
          * @throws InputException
          * @throws InputMismatchException
          * @throws InvalidTransitionException
    @@ -586,12 +601,17 @@ private function activateCustomer($customer, $confirmationKey)
             // No need to validate customer and customer address while activating customer
             $this->setIgnoreValidationFlag($customer);
             $this->customerRepository->save($customer);
    -        $this->getEmailNotification()->newAccount(
    -            $customer,
    -            'confirmed',
    -            '',
    -            $this->storeManager->getStore()->getId()
    -        );
    +
    +        $customerLastLoginAt = $this->customerLogger->get((int)$customer->getId())->getLastLoginAt();
    +        if (!$customerLastLoginAt) {
    +            $this->getEmailNotification()->newAccount(
    +                $customer,
    +                'confirmed',
    +                '',
    +                $this->storeManager->getStore()->getId()
    +            );
    +        }
    +
             return $customer;
         }
     
    @@ -615,7 +635,9 @@ public function authenticate($username, $password)
             } catch (InvalidEmailOrPasswordException $e) {
                 throw new InvalidEmailOrPasswordException(__('Invalid login or password.'));
             }
    -        if ($customer->getConfirmation() && $this->isConfirmationRequired($customer)) {
    +
    +        if ($customer->getConfirmation()
    +            && ($this->isConfirmationRequired($customer) || $this->isEmailChangedConfirmationRequired($customer))) {
                 throw new EmailNotConfirmedException(__("This account isn't confirmed. Verify and try again."));
             }
     
    @@ -630,6 +652,21 @@ public function authenticate($username, $password)
             return $customer;
         }
     
    +    /**
    +     * Checks if account confirmation is required if the email address has been changed
    +     *
    +     * @param CustomerInterface $customer
    +     * @return bool
    +     */
    +    private function isEmailChangedConfirmationRequired(CustomerInterface $customer): bool
    +    {
    +        return $this->accountConfirmation->isEmailChangedConfirmationRequired(
    +            (int)$customer->getWebsiteId(),
    +            (int)$customer->getId(),
    +            $customer->getEmail()
    +        );
    +    }
    +
         /**
          * @inheritdoc
          */
    @@ -715,7 +752,7 @@ public function resetPassword($email, $resetToken, $newPassword)
             $this->setIgnoreValidationFlag($customer);
     
             //Validate Token and new password strength
    -        $this->validateResetPasswordToken($customer->getId(), $resetToken);
    +        $this->validateResetPasswordToken((int)$customer->getId(), $resetToken);
             $this->credentialsValidator->checkPasswordDifferentFromEmail(
                 $email,
                 $newPassword
    @@ -826,13 +863,10 @@ public function getConfirmationStatus($customerId)
         {
             // load customer by id
             $customer = $this->customerRepository->getById($customerId);
    -        if ($this->isConfirmationRequired($customer)) {
    -            if (!$customer->getConfirmation()) {
    -                return self::ACCOUNT_CONFIRMED;
    -            }
    -            return self::ACCOUNT_CONFIRMATION_REQUIRED;
    -        }
    -        return self::ACCOUNT_CONFIRMATION_NOT_REQUIRED;
    +
    +        return $this->isConfirmationRequired($customer)
    +            ? $customer->getConfirmation() ? self::ACCOUNT_CONFIRMATION_REQUIRED : self::ACCOUNT_CONFIRMED
    +            : self::ACCOUNT_CONFIRMATION_NOT_REQUIRED;
         }
     
         /**
    @@ -1102,9 +1136,24 @@ public function validate(CustomerInterface $customer)
     
         /**
          * @inheritdoc
    +     *
    +     * @param string $customerEmail
    +     * @param int|null $websiteId
    +     * @return bool
    +     * @throws LocalizedException
          */
         public function isEmailAvailable($customerEmail, $websiteId = null)
         {
    +        $guestLoginConfig = $this->scopeConfig->getValue(
    +            self::GUEST_CHECKOUT_LOGIN_OPTION_SYS_CONFIG,
    +            ScopeInterface::SCOPE_WEBSITE,
    +            $websiteId
    +        );
    +
    +        if (!$guestLoginConfig) {
    +            return true;
    +        }
    +
             try {
                 if ($websiteId === null) {
                     $websiteId = $this->storeManager->getStore()->getWebsiteId();
    @@ -1213,6 +1262,7 @@ public function isReadonly($customerId)
          * @return $this
          * @throws LocalizedException
          * @deprecated 100.1.0
    +     * @see EmailNotification::newAccount()
          */
         protected function sendNewAccountEmail(
             $customer,
    @@ -1256,6 +1306,7 @@ protected function sendNewAccountEmail(
          * @throws LocalizedException
          * @throws NoSuchEntityException
          * @deprecated 100.1.0
    +     * @see EmailNotification::credentialsChanged()
          */
         protected function sendPasswordResetNotificationEmail($customer)
         {
    @@ -1269,6 +1320,7 @@ protected function sendPasswordResetNotificationEmail($customer)
          * @param int|string|null $defaultStoreId
          * @return int
          * @deprecated 100.1.0
    +     * @see StoreManagerInterface::getWebsite()
          * @throws LocalizedException
          */
         protected function getWebsiteStoreId($customer, $defaultStoreId = null)
    @@ -1286,6 +1338,7 @@ protected function getWebsiteStoreId($customer, $defaultStoreId = null)
          *
          * @return array
          * @deprecated 100.1.0
    +     * @see EmailNotification::TEMPLATE_TYPES
          */
         protected function getTemplateTypes()
         {
    @@ -1319,6 +1372,7 @@ protected function getTemplateTypes()
          * @return $this
          * @throws MailException
          * @deprecated 100.1.0
    +     * @see EmailNotification::sendEmailTemplate()
          */
         protected function sendEmailTemplate(
             $customer,
    @@ -1473,6 +1527,7 @@ public function changeResetPasswordLinkToken(CustomerInterface $customer, string
          * @throws LocalizedException
          * @throws NoSuchEntityException
          * @deprecated 100.1.0
    +     * @see EmailNotification::passwordReminder()
          */
         public function sendPasswordReminderEmail($customer)
         {
    @@ -1502,6 +1557,7 @@ public function sendPasswordReminderEmail($customer)
          * @throws LocalizedException
          * @throws NoSuchEntityException
          * @deprecated 100.1.0
    +     * @see EmailNotification::passwordResetConfirmation()
          */
         public function sendPasswordResetConfirmationEmail($customer)
         {
    @@ -1547,6 +1603,7 @@ protected function getAddressById(CustomerInterface $customer, $addressId)
          * @return Data\CustomerSecure
          * @throws NoSuchEntityException
          * @deprecated 100.1.0
    +     * @see EmailNotification::getFullCustomerObject()
          */
         protected function getFullCustomerObject($customer)
         {
    @@ -1555,7 +1612,7 @@ protected function getFullCustomerObject($customer)
             $mergedCustomerData = $this->customerRegistry->retrieveSecureData($customer->getId());
             $customerData = $this->dataProcessor->buildOutputDataArray(
                 $customer,
    -            \Magento\Customer\Api\Data\CustomerInterface::class
    +            CustomerInterface::class
             );
             $mergedCustomerData->addData($customerData);
             $mergedCustomerData->setData('name', $this->customerViewHelper->getCustomerName($customer));
    @@ -1591,7 +1648,6 @@ private function disableAddressValidation($customer)
          * Get email notification
          *
          * @return EmailNotificationInterface
    -     * @deprecated 100.1.0
          */
         private function getEmailNotification()
         {
    
  • app/code/Magento/Customer/Model/EmailNotification.php+58 15 modified
    @@ -9,6 +9,8 @@
     
     use Magento\Framework\App\Config\ScopeConfigInterface;
     use Magento\Framework\App\ObjectManager;
    +use Magento\Framework\Exception\MailException;
    +use Magento\Framework\Exception\NoSuchEntityException;
     use Magento\Framework\Mail\Template\SenderResolverInterface;
     use Magento\Store\Model\App\Emulation;
     use Magento\Store\Model\StoreManagerInterface;
    @@ -30,28 +32,28 @@ class EmailNotification implements EmailNotificationInterface
         /**#@+
          * Configuration paths for email templates and identities
          */
    -    const XML_PATH_FORGOT_EMAIL_IDENTITY = 'customer/password/forgot_email_identity';
    +    public const XML_PATH_FORGOT_EMAIL_IDENTITY = 'customer/password/forgot_email_identity';
     
    -    const XML_PATH_RESET_PASSWORD_TEMPLATE = 'customer/password/reset_password_template';
    +    public const XML_PATH_RESET_PASSWORD_TEMPLATE = 'customer/password/reset_password_template';
     
    -    const XML_PATH_CHANGE_EMAIL_TEMPLATE = 'customer/account_information/change_email_template';
    +    public const XML_PATH_CHANGE_EMAIL_TEMPLATE = 'customer/account_information/change_email_template';
     
    -    const XML_PATH_CHANGE_EMAIL_AND_PASSWORD_TEMPLATE =
    +    public const XML_PATH_CHANGE_EMAIL_AND_PASSWORD_TEMPLATE =
             'customer/account_information/change_email_and_password_template';
     
    -    const XML_PATH_FORGOT_EMAIL_TEMPLATE = 'customer/password/forgot_email_template';
    +    public const XML_PATH_FORGOT_EMAIL_TEMPLATE = 'customer/password/forgot_email_template';
     
    -    const XML_PATH_REMIND_EMAIL_TEMPLATE = 'customer/password/remind_email_template';
    +    public const XML_PATH_REMIND_EMAIL_TEMPLATE = 'customer/password/remind_email_template';
     
    -    const XML_PATH_REGISTER_EMAIL_IDENTITY = 'customer/create_account/email_identity';
    +    public const XML_PATH_REGISTER_EMAIL_IDENTITY = 'customer/create_account/email_identity';
     
    -    const XML_PATH_REGISTER_EMAIL_TEMPLATE = 'customer/create_account/email_template';
    +    public const XML_PATH_REGISTER_EMAIL_TEMPLATE = 'customer/create_account/email_template';
     
    -    const XML_PATH_REGISTER_NO_PASSWORD_EMAIL_TEMPLATE = 'customer/create_account/email_no_password_template';
    +    public const XML_PATH_REGISTER_NO_PASSWORD_EMAIL_TEMPLATE = 'customer/create_account/email_no_password_template';
     
    -    const XML_PATH_CONFIRM_EMAIL_TEMPLATE = 'customer/create_account/email_confirmation_template';
    +    public const XML_PATH_CONFIRM_EMAIL_TEMPLATE = 'customer/create_account/email_confirmation_template';
     
    -    const XML_PATH_CONFIRMED_EMAIL_TEMPLATE = 'customer/create_account/email_confirmed_template';
    +    public const XML_PATH_CONFIRMED_EMAIL_TEMPLATE = 'customer/create_account/email_confirmed_template';
     
         /**
          * self::NEW_ACCOUNT_EMAIL_REGISTERED               welcome email, when confirmation is disabled
    @@ -62,7 +64,7 @@ class EmailNotification implements EmailNotificationInterface
          *                                                  and password is set
          * self::NEW_ACCOUNT_EMAIL_CONFIRMATION             email with confirmation link
          */
    -    const TEMPLATE_TYPES = [
    +    public const TEMPLATE_TYPES = [
             self::NEW_ACCOUNT_EMAIL_REGISTERED => self::XML_PATH_REGISTER_EMAIL_TEMPLATE,
             self::NEW_ACCOUNT_EMAIL_REGISTERED_NO_PASSWORD => self::XML_PATH_REGISTER_NO_PASSWORD_EMAIL_TEMPLATE,
             self::NEW_ACCOUNT_EMAIL_CONFIRMED => self::XML_PATH_CONFIRMED_EMAIL_TEMPLATE,
    @@ -71,7 +73,9 @@ class EmailNotification implements EmailNotificationInterface
     
         /**#@-*/
     
    -    /**#@-*/
    +    /**
    +     * @var CustomerRegistry
    +     */
         private $customerRegistry;
     
         /**
    @@ -109,6 +113,11 @@ class EmailNotification implements EmailNotificationInterface
          */
         private $emulation;
     
    +    /**
    +     * @var AccountConfirmation
    +     */
    +    private AccountConfirmation $accountConfirmation;
    +
         /**
          * @param CustomerRegistry $customerRegistry
          * @param StoreManagerInterface $storeManager
    @@ -118,6 +127,7 @@ class EmailNotification implements EmailNotificationInterface
          * @param ScopeConfigInterface $scopeConfig
          * @param SenderResolverInterface|null $senderResolver
          * @param Emulation|null $emulation
    +     * @param AccountConfirmation|null $accountConfirmation
          */
         public function __construct(
             CustomerRegistry $customerRegistry,
    @@ -127,7 +137,8 @@ public function __construct(
             DataObjectProcessor $dataProcessor,
             ScopeConfigInterface $scopeConfig,
             SenderResolverInterface $senderResolver = null,
    -        Emulation $emulation =null
    +        Emulation $emulation = null,
    +        ?AccountConfirmation $accountConfirmation = null
         ) {
             $this->customerRegistry = $customerRegistry;
             $this->storeManager = $storeManager;
    @@ -137,6 +148,8 @@ public function __construct(
             $this->scopeConfig = $scopeConfig;
             $this->senderResolver = $senderResolver ?? ObjectManager::getInstance()->get(SenderResolverInterface::class);
             $this->emulation = $emulation ?? ObjectManager::getInstance()->get(Emulation::class);
    +        $this->accountConfirmation = $accountConfirmation ?? ObjectManager::getInstance()
    +                ->get(AccountConfirmation::class);
         }
     
         /**
    @@ -146,13 +159,15 @@ public function __construct(
          * @param string $origCustomerEmail
          * @param bool $isPasswordChanged
          * @return void
    +     * @throws LocalizedException
          */
         public function credentialsChanged(
             CustomerInterface $savedCustomer,
             $origCustomerEmail,
             $isPasswordChanged = false
         ): void {
             if ($origCustomerEmail != $savedCustomer->getEmail()) {
    +            $this->emailChangedConfirmation($savedCustomer);
                 if ($isPasswordChanged) {
                     $this->emailAndPasswordChanged($savedCustomer, $origCustomerEmail);
                     $this->emailAndPasswordChanged($savedCustomer, $savedCustomer->getEmail());
    @@ -175,6 +190,8 @@ public function credentialsChanged(
          * @param CustomerInterface $customer
          * @param string $email
          * @return void
    +     * @throws MailException
    +     * @throws NoSuchEntityException|LocalizedException
          */
         private function emailAndPasswordChanged(CustomerInterface $customer, $email): void
         {
    @@ -201,6 +218,8 @@ private function emailAndPasswordChanged(CustomerInterface $customer, $email): v
          * @param CustomerInterface $customer
          * @param string $email
          * @return void
    +     * @throws MailException
    +     * @throws NoSuchEntityException|LocalizedException
          */
         private function emailChanged(CustomerInterface $customer, $email): void
         {
    @@ -226,6 +245,8 @@ private function emailChanged(CustomerInterface $customer, $email): void
          *
          * @param CustomerInterface $customer
          * @return void
    +     * @throws MailException
    +     * @throws NoSuchEntityException|LocalizedException
          */
         private function passwordReset(CustomerInterface $customer): void
         {
    @@ -255,7 +276,7 @@ private function passwordReset(CustomerInterface $customer): void
          * @param int|null $storeId
          * @param string $email
          * @return void
    -     * @throws \Magento\Framework\Exception\MailException
    +     * @throws MailException|LocalizedException
          */
         private function sendEmailTemplate(
             $customer,
    @@ -293,6 +314,7 @@ private function sendEmailTemplate(
          *
          * @param CustomerInterface $customer
          * @return CustomerSecure
    +     * @throws NoSuchEntityException
          */
         private function getFullCustomerObject($customer): CustomerSecure
         {
    @@ -312,6 +334,7 @@ private function getFullCustomerObject($customer): CustomerSecure
          * @param CustomerInterface $customer
          * @param int|string|null $defaultStoreId
          * @return int
    +     * @throws LocalizedException
          */
         private function getWebsiteStoreId($customer, $defaultStoreId = null): int
         {
    @@ -327,6 +350,9 @@ private function getWebsiteStoreId($customer, $defaultStoreId = null): int
          *
          * @param CustomerInterface $customer
          * @return void
    +     * @throws LocalizedException
    +     * @throws MailException
    +     * @throws NoSuchEntityException
          */
         public function passwordReminder(CustomerInterface $customer): void
         {
    @@ -351,6 +377,9 @@ public function passwordReminder(CustomerInterface $customer): void
          *
          * @param CustomerInterface $customer
          * @return void
    +     * @throws LocalizedException
    +     * @throws MailException
    +     * @throws NoSuchEntityException
          */
         public function passwordResetConfirmation(CustomerInterface $customer): void
         {
    @@ -412,4 +441,18 @@ public function newAccount(
                 $storeId
             );
         }
    +
    +    /**
    +     * Sending an email to confirm the email address in case the email address has been changed
    +     *
    +     * @param CustomerInterface $customer
    +     * @throws LocalizedException
    +     */
    +    private function emailChangedConfirmation(CustomerInterface $customer): void
    +    {
    +        if (!$this->accountConfirmation->isCustomerEmailChangedConfirmRequired($customer)) {
    +            return;
    +        }
    +        $this->newAccount($customer, self::NEW_ACCOUNT_EMAIL_CONFIRMATION, null, $customer->getStoreId());
    +    }
     }
    
  • app/code/Magento/Customer/Model/Plugin/ClearSessionsAfterLogoutPlugin.php+108 0 added
    @@ -0,0 +1,108 @@
    +<?php
    +/**
    + * Copyright © Magento, Inc. All rights reserved.
    + * See COPYING.txt for license details.
    + */
    +declare(strict_types=1);
    +
    +namespace Magento\Customer\Model\Plugin;
    +
    +use Magento\Customer\Model\Session;
    +use Magento\Framework\App\Area;
    +use Magento\Framework\App\State;
    +use Magento\Framework\Session\SaveHandlerInterface;
    +use Magento\Framework\Session\StorageInterface;
    +use Magento\Framework\Exception\SessionException;
    +use Psr\Log\LoggerInterface;
    +use Magento\Framework\Exception\LocalizedException;
    +
    +/**
    + * Clears previous active sessions after logout
    + *
    + * @SuppressWarnings(PHPMD.CookieAndSessionMisuse)
    + */
    +class ClearSessionsAfterLogoutPlugin
    +{
    +    /**
    +     * Array key for all active previous session ids.
    +     */
    +    private const PREVIOUS_ACTIVE_SESSIONS = 'previous_active_sessions';
    +
    +    /**
    +     * @var Session
    +     */
    +    private Session $session;
    +
    +    /**
    +     * @var SaveHandlerInterface
    +     */
    +    private SaveHandlerInterface $saveHandler;
    +
    +    /**
    +     * @var StorageInterface
    +     */
    +    private StorageInterface $storage;
    +
    +    /**
    +     * @var State
    +     */
    +    private State $state;
    +
    +    /**
    +     * @var LoggerInterface
    +     */
    +    private LoggerInterface $logger;
    +
    +    /**
    +     * Initialize Dependencies
    +     *
    +     * @param Session $customerSession
    +     * @param SaveHandlerInterface $saveHandler
    +     * @param StorageInterface $storage
    +     * @param State $state
    +     * @param LoggerInterface $logger
    +     */
    +    public function __construct(
    +        Session $customerSession,
    +        SaveHandlerInterface $saveHandler,
    +        StorageInterface $storage,
    +        State $state,
    +        LoggerInterface $logger
    +    ) {
    +        $this->session = $customerSession;
    +        $this->saveHandler = $saveHandler;
    +        $this->storage = $storage;
    +        $this->state = $state;
    +        $this->logger = $logger;
    +    }
    +
    +    /**
    +     * Plugin to clear session after logout
    +     *
    +     * @param Session $subject
    +     * @param Session $result
    +     * @return Session
    +     * @throws LocalizedException
    +     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
    +     */
    +    public function afterLogout(Session $subject, Session $result): Session
    +    {
    +        $isAreaFrontEnd = $this->state->getAreaCode() === Area::AREA_FRONTEND;
    +        $previousSessions = $this->storage->getData(self::PREVIOUS_ACTIVE_SESSIONS);
    +
    +        if ($isAreaFrontEnd && !empty($previousSessions)) {
    +            foreach ($previousSessions as $sessionId) {
    +                try {
    +                    $this->session->start();
    +                    $this->saveHandler->destroy($sessionId);
    +                    $this->session->writeClose();
    +                } catch (SessionException $e) {
    +                    $this->logger->error($e);
    +                }
    +
    +            }
    +            $this->storage->setData(self::PREVIOUS_ACTIVE_SESSIONS, []);
    +        }
    +        return $result;
    +    }
    +}
    
  • app/code/Magento/Customer/Model/Plugin/CustomerNotification.php+52 18 modified
    @@ -3,6 +3,7 @@
      * Copyright © Magento, Inc. All rights reserved.
      * See COPYING.txt for license details.
      */
    +declare(strict_types=1);
     
     namespace Magento\Customer\Model\Plugin;
     
    @@ -16,13 +17,23 @@
     use Magento\Framework\App\RequestInterface;
     use Magento\Framework\App\State;
     use Magento\Framework\Exception\NoSuchEntityException;
    +use Magento\Framework\Session\StorageInterface;
     use Psr\Log\LoggerInterface;
    +use Magento\Framework\Exception\LocalizedException;
    +use Magento\Framework\App\Request\Http;
     
     /**
      * Refresh the Customer session if `UpdateSession` notification registered
    + *
    + * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
      */
     class CustomerNotification
     {
    +    /**
    +     * Array key for all active previous session ids.
    +     */
    +    private const PREVIOUS_ACTIVE_SESSIONS = 'previous_active_sessions';
    +
         /**
          * @var Session
          */
    @@ -49,10 +60,15 @@ class CustomerNotification
         private $logger;
     
         /**
    -     * @var RequestInterface|\Magento\Framework\App\Request\Http
    +     * @var RequestInterface|Http
          */
         private $request;
     
    +    /**
    +     * @var StorageInterface
    +     */
    +    private StorageInterface $storage;
    +
         /**
          * Initialize dependencies.
          *
    @@ -61,46 +77,64 @@ class CustomerNotification
          * @param State $state
          * @param CustomerRepositoryInterface $customerRepository
          * @param LoggerInterface $logger
    -     * @param RequestInterface|null $request
    +     * @param RequestInterface $request
    +     * @param StorageInterface|null $storage
          */
         public function __construct(
             Session $session,
             NotificationStorage $notificationStorage,
             State $state,
             CustomerRepositoryInterface $customerRepository,
             LoggerInterface $logger,
    -        RequestInterface $request
    +        RequestInterface $request,
    +        StorageInterface $storage = null
         ) {
             $this->session = $session;
             $this->notificationStorage = $notificationStorage;
             $this->state = $state;
             $this->customerRepository = $customerRepository;
             $this->logger = $logger;
             $this->request = $request;
    +        $this->storage = $storage ?? ObjectManager::getInstance()->get(StorageInterface::class);
         }
     
         /**
          * Refresh the customer session on frontend post requests if an update session notification is registered.
          *
          * @param ActionInterface $subject
          * @return void
    -     * @throws \Magento\Framework\Exception\LocalizedException
    +     * @throws LocalizedException
          * @SuppressWarnings(PHPMD.UnusedFormalParameter)
          */
         public function beforeExecute(ActionInterface $subject)
         {
    -        $customerId = $this->session->getCustomerId();
    -
    -        if ($this->isFrontendRequest() && $this->isPostRequest() && $this->isSessionUpdateRegisteredFor($customerId)) {
    -            try {
    -                $this->session->regenerateId();
    -                $customer = $this->customerRepository->getById($customerId);
    -                $this->session->setCustomerData($customer);
    -                $this->session->setCustomerGroupId($customer->getGroupId());
    -                $this->notificationStorage->remove(NotificationStorage::UPDATE_CUSTOMER_SESSION, $customer->getId());
    -            } catch (NoSuchEntityException $e) {
    -                $this->logger->error($e);
    +        $customerId = (int)$this->session->getCustomerId();
    +
    +        if (!$this->isFrontendRequest()
    +            || !$this->isPostRequest()
    +            || !$this->isSessionUpdateRegisteredFor($customerId)) {
    +            return;
    +        }
    +
    +        try {
    +            $oldSessionId = $this->session->getSessionId();
    +            $previousSessions = $this->storage->getData(self::PREVIOUS_ACTIVE_SESSIONS);
    +
    +            if (empty($previousSessions)) {
    +                $previousSessions = [];
                 }
    +            $previousSessions[] = $oldSessionId;
    +            $this->storage->setData(self::PREVIOUS_ACTIVE_SESSIONS, $previousSessions);
    +            $this->session->regenerateId();
    +            $customer = $this->customerRepository->getById($customerId);
    +            $this->session->setCustomerData($customer);
    +            $this->session->setCustomerGroupId($customer->getGroupId());
    +            $this->notificationStorage->remove(
    +                NotificationStorage::UPDATE_CUSTOMER_SESSION,
    +                $customer->getId()
    +            );
    +        } catch (NoSuchEntityException $e) {
    +            $this->logger->error($e);
             }
         }
     
    @@ -118,7 +152,7 @@ private function isPostRequest(): bool
          * Check if the current application area is frontend.
          *
          * @return bool
    -     * @throws \Magento\Framework\Exception\LocalizedException
    +     * @throws LocalizedException
          */
         private function isFrontendRequest(): bool
         {
    @@ -131,8 +165,8 @@ private function isFrontendRequest(): bool
          * @param int $customerId
          * @return bool
          */
    -    private function isSessionUpdateRegisteredFor($customerId): bool
    +    private function isSessionUpdateRegisteredFor(int $customerId): bool
         {
    -        return $this->notificationStorage->isExists(NotificationStorage::UPDATE_CUSTOMER_SESSION, $customerId);
    +        return (bool)$this->notificationStorage->isExists(NotificationStorage::UPDATE_CUSTOMER_SESSION, $customerId);
         }
     }
    
  • app/code/Magento/Customer/Model/Plugin/UpdateCustomer.php+21 3 modified
    @@ -11,6 +11,8 @@
     use Magento\Framework\Webapi\Rest\Request as RestRequest;
     use Magento\Customer\Api\Data\CustomerInterface;
     use Magento\Customer\Api\CustomerRepositoryInterface;
    +use Magento\Authorization\Model\UserContextInterface;
    +use Magento\Framework\App\ObjectManager;
     
     /**
      * Update customer by id from request param
    @@ -22,12 +24,19 @@ class UpdateCustomer
          */
         private $request;
     
    +    /**
    +     * @var UserContextInterface
    +     */
    +    private $userContext;
    +
         /**
          * @param RestRequest $request
    +     * @param UserContextInterface|null $userContext
          */
    -    public function __construct(RestRequest $request)
    +    public function __construct(RestRequest $request, ?UserContextInterface $userContext = null)
         {
             $this->request = $request;
    +        $this->userContext = $userContext ?? ObjectManager::getInstance()->get(UserContextInterface::class);
         }
     
         /**
    @@ -43,9 +52,18 @@ public function beforeSave(
             CustomerInterface $customer,
             ?string $passwordHash = null
         ): array {
    -        $customerId = $this->request->getParam('customerId');
    +        $userType = $this->userContext->getUserType();
    +        $customerSessionId = (int)$this->userContext->getUserId();
    +        $customerId = (int)$this->request->getParam('customerId');
             $bodyParams = $this->request->getBodyParams();
    -        if (!isset($bodyParams['customer']['Id']) && $customerId) {
    +
    +        if ($userType === UserContextInterface::USER_TYPE_CUSTOMER &&
    +            !isset($bodyParams['customer']['Id']) &&
    +            $customerId &&
    +            $customerId === $customerSessionId
    +        ) {
    +            $customer = $this->getUpdatedCustomer($customerRepository->getById($customerId), $customer);
    +        } elseif ($userType === UserContextInterface::USER_TYPE_ADMIN && $customerId) {
                 $customer = $this->getUpdatedCustomer($customerRepository->getById($customerId), $customer);
             }
     
    
  • app/code/Magento/Customer/Model/ResourceModel/Customer.php+92 40 modified
    @@ -7,12 +7,25 @@
     
     namespace Magento\Customer\Model\ResourceModel;
     
    +use Magento\Customer\Api\Data\CustomerInterface;
     use Magento\Customer\Model\AccountConfirmation;
     use Magento\Customer\Model\Customer\NotificationStorage;
    +use Magento\Eav\Model\Entity\Context;
    +use Magento\Eav\Model\Entity\VersionControl\AbstractEntity;
    +use Magento\Framework\App\Config\ScopeConfigInterface;
     use Magento\Framework\App\ObjectManager;
    +use Magento\Framework\DataObject;
    +use Magento\Framework\DB\Select;
     use Magento\Framework\Exception\AlreadyExistsException;
    +use Magento\Framework\Exception\LocalizedException;
    +use Magento\Framework\Exception\NoSuchEntityException;
    +use Magento\Framework\Model\ResourceModel\Db\VersionControl\RelationComposite;
    +use Magento\Framework\Model\ResourceModel\Db\VersionControl\Snapshot;
    +use Magento\Framework\Stdlib\DateTime;
     use Magento\Framework\Validator\Exception as ValidatorException;
     use Magento\Framework\Encryption\EncryptorInterface;
    +use Magento\Framework\Validator\Factory;
    +use Magento\Store\Model\StoreManagerInterface;
     
     /**
      * Customer entity resource model
    @@ -21,27 +34,27 @@
      * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
      * @since 100.0.2
      */
    -class Customer extends \Magento\Eav\Model\Entity\VersionControl\AbstractEntity
    +class Customer extends AbstractEntity
     {
         /**
    -     * @var \Magento\Framework\Validator\Factory
    +     * @var Factory
          */
         protected $_validatorFactory;
     
         /**
          * Core store config
          *
    -     * @var \Magento\Framework\App\Config\ScopeConfigInterface
    +     * @var ScopeConfigInterface
          */
         protected $_scopeConfig;
     
         /**
    -     * @var \Magento\Framework\Stdlib\DateTime
    +     * @var DateTime
          */
         protected $dateTime;
     
         /**
    -     * @var \Magento\Store\Model\StoreManagerInterface
    +     * @var StoreManagerInterface
          */
         protected $storeManager;
     
    @@ -63,26 +76,26 @@ class Customer extends \Magento\Eav\Model\Entity\VersionControl\AbstractEntity
         /**
          * Customer constructor.
          *
    -     * @param \Magento\Eav\Model\Entity\Context $context
    -     * @param \Magento\Framework\Model\ResourceModel\Db\VersionControl\Snapshot $entitySnapshot
    -     * @param \Magento\Framework\Model\ResourceModel\Db\VersionControl\RelationComposite $entityRelationComposite
    -     * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig
    -     * @param \Magento\Framework\Validator\Factory $validatorFactory
    -     * @param \Magento\Framework\Stdlib\DateTime $dateTime
    -     * @param \Magento\Store\Model\StoreManagerInterface $storeManager
    +     * @param Context $context
    +     * @param Snapshot $entitySnapshot
    +     * @param RelationComposite $entityRelationComposite
    +     * @param ScopeConfigInterface $scopeConfig
    +     * @param Factory $validatorFactory
    +     * @param DateTime $dateTime
    +     * @param StoreManagerInterface $storeManager
          * @param array $data
    -     * @param AccountConfirmation $accountConfirmation
    +     * @param AccountConfirmation|null $accountConfirmation
          * @param EncryptorInterface|null $encryptor
          * @SuppressWarnings(PHPMD.ExcessiveParameterList)
          */
         public function __construct(
    -        \Magento\Eav\Model\Entity\Context $context,
    -        \Magento\Framework\Model\ResourceModel\Db\VersionControl\Snapshot $entitySnapshot,
    -        \Magento\Framework\Model\ResourceModel\Db\VersionControl\RelationComposite $entityRelationComposite,
    -        \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig,
    -        \Magento\Framework\Validator\Factory $validatorFactory,
    -        \Magento\Framework\Stdlib\DateTime $dateTime,
    -        \Magento\Store\Model\StoreManagerInterface $storeManager,
    +        Context $context,
    +        Snapshot $entitySnapshot,
    +        RelationComposite $entityRelationComposite,
    +        ScopeConfigInterface $scopeConfig,
    +        Factory $validatorFactory,
    +        DateTime $dateTime,
    +        StoreManagerInterface $storeManager,
             $data = [],
             AccountConfirmation $accountConfirmation = null,
             EncryptorInterface $encryptor = null
    @@ -120,16 +133,16 @@ protected function _getDefaultAttributes()
         /**
          * Check customer scope, email and confirmation key before saving
          *
    -     * @param \Magento\Framework\DataObject|\Magento\Customer\Api\Data\CustomerInterface $customer
    +     * @param DataObject|CustomerInterface $customer
          *
          * @return $this
          * @throws AlreadyExistsException
          * @throws ValidatorException
    -     * @throws \Magento\Framework\Exception\NoSuchEntityException
    +     * @throws NoSuchEntityException
          * @SuppressWarnings(PHPMD.CyclomaticComplexity)
          * @SuppressWarnings(PHPMD.NPathComplexity)
          */
    -    protected function _beforeSave(\Magento\Framework\DataObject $customer)
    +    protected function _beforeSave(DataObject $customer)
         {
             /** @var \Magento\Customer\Model\Customer $customer */
             if ($customer->getStoreId() === null) {
    @@ -169,13 +182,7 @@ protected function _beforeSave(\Magento\Framework\DataObject $customer)
             }
     
             // set confirmation key logic
    -        if (!$customer->getId() &&
    -            $this->accountConfirmation->isConfirmationRequired(
    -                $customer->getWebsiteId(),
    -                $customer->getId(),
    -                $customer->getEmail()
    -            )
    -        ) {
    +        if ($this->isConfirmationRequired($customer)) {
                 $customer->setConfirmation($customer->getRandomConfirmationKey());
             }
             // remove customer confirmation key from database, if empty
    @@ -195,6 +202,51 @@ protected function _beforeSave(\Magento\Framework\DataObject $customer)
             return $this;
         }
     
    +    /**
    +     * Checks if customer email verification is required
    +     *
    +     * @param DataObject|CustomerInterface $customer
    +     * @return bool
    +     */
    +    private function isConfirmationRequired(DataObject $customer): bool
    +    {
    +        return $this->isNewCustomerConfirmationRequired($customer)
    +            || $this->isExistingCustomerConfirmationRequired($customer);
    +    }
    +
    +    /**
    +     * Checks if customer email verification is required for a new customer
    +     *
    +     * @param DataObject|CustomerInterface $customer
    +     * @return bool
    +     */
    +    private function isNewCustomerConfirmationRequired(DataObject $customer): bool
    +    {
    +        return !$customer->getId()
    +            && $this->accountConfirmation->isConfirmationRequired(
    +                $customer->getWebsiteId(),
    +                $customer->getId(),
    +                $customer->getEmail()
    +            );
    +    }
    +
    +    /**
    +     * Checks if customer email verification is required for an existing customer
    +     *
    +     * @param DataObject|CustomerInterface $customer
    +     * @return bool
    +     */
    +    private function isExistingCustomerConfirmationRequired(DataObject $customer): bool
    +    {
    +         return $customer->getId()
    +             && $customer->dataHasChangedFor('email')
    +             && $this->accountConfirmation->isEmailChangedConfirmationRequired(
    +                 (int)$customer->getWebsiteId(),
    +                 (int)$customer->getId(),
    +                 $customer->getEmail()
    +             );
    +    }
    +
         /**
          * Validate customer entity
          *
    @@ -231,10 +283,10 @@ private function getNotificationStorage()
         /**
          * Save customer addresses and set default addresses in attributes backend
          *
    -     * @param \Magento\Framework\DataObject $customer
    +     * @param DataObject $customer
          * @return $this
          */
    -    protected function _afterSave(\Magento\Framework\DataObject $customer)
    +    protected function _afterSave(DataObject $customer)
         {
             $this->getNotificationStorage()->add(
                 NotificationStorage::UPDATE_CUSTOMER_SESSION,
    @@ -250,9 +302,9 @@ protected function _afterSave(\Magento\Framework\DataObject $customer)
         /**
          * Retrieve select object for loading base entity row
          *
    -     * @param \Magento\Framework\DataObject $object
    +     * @param DataObject $object
          * @param string|int $rowId
    -     * @return \Magento\Framework\DB\Select
    +     * @return Select
          */
         protected function _getLoadRowSelect($object, $rowId)
         {
    @@ -270,7 +322,7 @@ protected function _getLoadRowSelect($object, $rowId)
          * @param \Magento\Customer\Model\Customer $customer
          * @param string $email
          * @return $this
    -     * @throws \Magento\Framework\Exception\LocalizedException
    +     * @throws LocalizedException
          */
         public function loadByEmail(\Magento\Customer\Model\Customer $customer, $email)
         {
    @@ -285,7 +337,7 @@ public function loadByEmail(\Magento\Customer\Model\Customer $customer, $email)
     
             if ($customer->getSharingConfig()->isWebsiteScope()) {
                 if (!$customer->hasData('website_id')) {
    -                throw new \Magento\Framework\Exception\LocalizedException(
    +                throw new LocalizedException(
                         __("A customer website ID wasn't specified. The ID must be specified to use the website scope.")
                     );
                 }
    @@ -390,10 +442,10 @@ public function getWebsiteId($customerId)
         /**
          * Custom setter of increment ID if its needed
          *
    -     * @param \Magento\Framework\DataObject $object
    +     * @param DataObject $object
          * @return $this
          */
    -    public function setNewIncrementId(\Magento\Framework\DataObject $object)
    +    public function setNewIncrementId(DataObject $object)
         {
             if ($this->_scopeConfig->getValue(
                 \Magento\Customer\Model\Customer::XML_PATH_GENERATE_HUMAN_FRIENDLY_ID,
    @@ -419,7 +471,7 @@ public function changeResetPasswordLinkToken(\Magento\Customer\Model\Customer $c
             if (is_string($passwordLinkToken) && !empty($passwordLinkToken)) {
                 $customer->setRpToken($passwordLinkToken);
                 $customer->setRpTokenCreatedAt(
    -                (new \DateTime())->format(\Magento\Framework\Stdlib\DateTime::DATETIME_PHP_FORMAT)
    +                (new \DateTime())->format(DateTime::DATETIME_PHP_FORMAT)
                 );
             }
             return $this;
    @@ -469,7 +521,7 @@ public function updateSessionCutOff(int $customerId, int $timestamp): void
         /**
          * @inheritDoc
          */
    -    protected function _afterLoad(\Magento\Framework\DataObject $customer)
    +    protected function _afterLoad(DataObject $customer)
         {
             if ($customer->getData('rp_token')) {
                 $rpToken = $customer->getData('rp_token');
    
  • app/code/Magento/Customer/Model/ResourceModel/CustomerRepository.php+41 1 modified
    @@ -4,6 +4,8 @@
      * See COPYING.txt for license details.
      */
     
    +declare(strict_types=1);
    +
     namespace Magento\Customer\Model\ResourceModel;
     
     use Magento\Customer\Api\CustomerMetadataInterface;
    @@ -28,6 +30,7 @@
     use Magento\Framework\Api\SearchCriteriaInterface;
     use Magento\Framework\App\ObjectManager;
     use Magento\Framework\Event\ManagerInterface;
    +use Magento\Framework\Exception\InputException;
     use Magento\Framework\Exception\LocalizedException;
     use Magento\Framework\Exception\NoSuchEntityException;
     use Magento\Store\Model\StoreManagerInterface;
    @@ -196,12 +199,19 @@ public function __construct(
          * @throws \Magento\Framework\Exception\LocalizedException
          * @SuppressWarnings(PHPMD.CyclomaticComplexity)
          * @SuppressWarnings(PHPMD.NPathComplexity)
    +     * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
          */
         public function save(CustomerInterface $customer, $passwordHash = null)
         {
             /** @var NewOperation|null $delegatedNewOperation */
             $delegatedNewOperation = !$customer->getId() ? $this->delegatedStorage->consumeNewOperation() : null;
             $prevCustomerData = $prevCustomerDataArr = null;
    +        if ($customer->getDefaultBilling()) {
    +            $this->validateDefaultAddress($customer, CustomerInterface::DEFAULT_BILLING);
    +        }
    +        if ($customer->getDefaultShipping()) {
    +            $this->validateDefaultAddress($customer, CustomerInterface::DEFAULT_SHIPPING);
    +        }
             if ($customer->getId()) {
                 $prevCustomerData = $this->getById($customer->getId());
                 $prevCustomerDataArr = $prevCustomerData->__toArray();
    @@ -227,7 +237,7 @@ public function save(CustomerInterface $customer, $passwordHash = null)
                     $prevCustomerData ? $prevCustomerData->getStoreId() : $this->storeManager->getStore()->getId()
                 );
             }
    -        $this->validateGroupId($customer->getGroupId());
    +        $this->validateGroupId((int)$customer->getGroupId());
             $this->setCustomerGroupId($customerModel, $customerArr, $prevCustomerDataArr);
             // Need to use attribute set or future updates can cause data loss
             if (!$customerModel->getAttributeSetId()) {
    @@ -511,4 +521,34 @@ private function setCustomerGroupId($customerModel, $customerArr, $prevCustomerD
                 $customerModel->setGroupId($prevCustomerDataArr['group_id']);
             }
         }
    +
    +    /**
    +     * To validate default address
    +     *
    +     * @param CustomerInterface $customer
    +     * @param string $defaultAddressType
    +     * @return void
    +     * @throws InputException
    +     */
    +    private function validateDefaultAddress(
    +        CustomerInterface $customer,
    +        string $defaultAddressType
    +    ): void {
    +        $addressId = $defaultAddressType === CustomerInterface::DEFAULT_BILLING ? $customer->getDefaultBilling()
    +            : $customer->getDefaultShipping();
    +        if ($customer->getAddresses()) {
    +            foreach ($customer->getAddresses() as $address) {
    +                if ((int) $addressId === (int) $address->getId()) {
    +                    return;
    +                }
    +            }
    +
    +            throw new InputException(
    +                __(
    +                    'The %fieldName value is invalid. Set the correct value and try again.',
    +                    ['fieldName' => $defaultAddressType]
    +                )
    +            );
    +        }
    +    }
     }
    
  • app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminClickFirstRowEditLinkOnCustomerGridActionGroup.xml+0 1 modified
    @@ -12,7 +12,6 @@
             <annotations>
                 <description>Click edit link for first row on the grid.</description>
             </annotations>
    -
             <click selector="{{AdminCustomerGridSection.firstRowEditLink}}" stepKey="clickOnEditLink"/>
             <waitForPageLoad stepKey="waitForPageLoading"/>
         </actionGroup>
    
  • app/code/Magento/Customer/Test/Mftf/Test/AdminChangeSingleCustomerGroupViaGridTest.xml+4 0 modified
    @@ -26,8 +26,12 @@
                 <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/>
                 <actionGroup ref="AdminSystemStoreOpenPageActionGroup" stepKey="navigateToStores"/>
                 <actionGroup ref="AdminDeleteMultipleWebsitesActionGroup" stepKey="deleteWebsites"/>
    +            <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +                <argument name="indices" value=""/>
    +            </actionGroup>  
             </before>
             <after>
    +            <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="logoutCustomer"/>
                 <!--Delete created data-->
                 <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/>
                 <deleteData createDataKey="createCustomerGroup" stepKey="deleteCustomerGroup"/>
    
  • app/code/Magento/Customer/Test/Mftf/Test/AdminDeleteCustomerTest.xml+8 0 modified
    @@ -28,6 +28,14 @@
                 <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/>
             </after>
     
    +        <!-- Reindex and flush cache -->
    +        <actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
    +            <argument name="indices" value=""/>
    +        </actionGroup>
    +        <actionGroup ref="CliCacheFlushActionGroup" stepKey="flushCache">
    +            <argument name="tags" value=""/>
    +        </actionGroup>
    +        
             <!-- Delete created customer -->
             <actionGroup ref="DeleteCustomerByEmailActionGroup" stepKey="deleteCustomer">
                 <argument name="email" value="$$createCustomer.email$$"/>
    
  • app/code/Magento/Customer/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml+2 0 modified
    @@ -20,6 +20,7 @@
                 <group value="SearchEngineElasticsearch"/>
             </annotations>
             <before>
    +            <magentoCLI command="config:set checkout/options/enable_guest_checkout_login 1" stepKey="EnablingGuestCheckoutLogin"/>
                 <resetCookie userInput="PHPSESSID" stepKey="resetCookieForCart"/>
                 <actionGroup ref="AdminLoginActionGroup" after="resetCookieForCart" stepKey="loginAsAdmin"/>
             </before>
    @@ -29,6 +30,7 @@
                 <actionGroup ref="DeleteCustomerFromAdminActionGroup" stepKey="deleteCustomerFromAdmin"/>
                 <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearProductsGridFilters"/>
                 <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/>
    +            <magentoCLI command="config:set checkout/options/enable_guest_checkout_login 0" stepKey="DisablingGuestCheckoutLogin"/>
             </after>
             <!-- Step 0: User signs up an account -->
             <comment userInput="Start of signing up user account" stepKey="startOfSigningUpUserAccount" />
    
  • app/code/Magento/Customer/Test/Unit/Controller/Account/ConfirmTest.php+67 19 modified
    @@ -12,6 +12,8 @@
     use Magento\Customer\Api\Data\CustomerInterface;
     use Magento\Customer\Controller\Account\Confirm;
     use Magento\Customer\Helper\Address;
    +use Magento\Customer\Model\Logger as CustomerLogger;
    +use Magento\Customer\Model\Log;
     use Magento\Customer\Model\Session;
     use Magento\Customer\Model\Url;
     use Magento\Framework\App\Action\Context;
    @@ -123,6 +125,16 @@ class ConfirmTest extends TestCase
          */
         protected $redirectResultMock;
     
    +    /**
    +     * @var CustomerLogger|MockObject
    +     */
    +    private $customerLoggerMock;
    +
    +    /**
    +     * @var Log|MockObject
    +     */
    +    private $logMock;
    +
         /**
          * @inheritdoc
          */
    @@ -143,6 +155,9 @@ protected function setUp(): void
                 ->method('create')
                 ->willReturn($this->urlMock);
     
    +        $this->customerLoggerMock = $this->createMock(CustomerLogger::class);
    +        $this->logMock = $this->createMock(Log::class);
    +
             $this->customerAccountManagementMock =
                 $this->getMockForAbstractClass(AccountManagementInterface::class);
             $this->customerDataMock = $this->getMockForAbstractClass(CustomerInterface::class);
    @@ -195,7 +210,8 @@ protected function setUp(): void
                     'customerAccountManagement' => $this->customerAccountManagementMock,
                     'customerRepository' => $this->customerRepositoryMock,
                     'addressHelper' => $this->addressHelperMock,
    -                'urlFactory' => $urlFactoryMock
    +                'urlFactory' => $urlFactoryMock,
    +                'customerLogger' => $this->customerLoggerMock
                 ]
             );
         }
    @@ -218,6 +234,8 @@ public function testIsLoggedIn(): void
         }
     
         /**
    +     * @param $customerId
    +     * @param $key
          * @return void
          * @dataProvider getParametersDataProvider
          */
    @@ -271,7 +289,8 @@ public function getParametersDataProvider(): array
          * @param $key
          * @param $vatValidationEnabled
          * @param $addressType
    -     * @param Phrase $successMessage
    +     * @param $lastLoginAt
    +     * @param $successMessage
          *
          * @return void
          * @dataProvider getSuccessMessageDataProvider
    @@ -282,7 +301,8 @@ public function testSuccessMessage(
             $key,
             $vatValidationEnabled,
             $addressType,
    -        Phrase $successMessage
    +        $lastLoginAt,
    +        $successMessage
         ): void {
             $this->customerSessionMock->expects($this->once())
                 ->method('isLoggedIn')
    @@ -292,7 +312,7 @@ public function testSuccessMessage(
                 ->method('getParam')
                 ->willReturnMap(
                     [
    -                    ['id', false, $customerId],
    +                    ['id', 0, $customerId],
                         ['key', false, $key]
                     ]
                 );
    @@ -333,6 +353,14 @@ public function testSuccessMessage(
                     ['*/*/admin', ['_secure' => true], 'http://store.web/back']
                 ]);
     
    +        $this->logMock->expects($vatValidationEnabled ? $this->never() : $this->once())
    +            ->method('getLastLoginAt')
    +            ->willReturn($lastLoginAt);
    +        $this->customerLoggerMock->expects($vatValidationEnabled ? $this->never() : $this->once())
    +            ->method('get')
    +            ->with(1)
    +            ->willReturn($this->logMock);
    +
             $this->addressHelperMock->expects($this->once())
                 ->method('isVatValidationEnabled')
                 ->willReturn($vatValidationEnabled);
    @@ -388,12 +416,14 @@ public function testSuccessMessage(
         public function getSuccessMessageDataProvider(): array
         {
             return [
    -            [1, 1, false, null, __('Thank you for registering with %1.', 'frontend')],
    +            [1, 1, false, null, 'some-datetime', null],
    +            [1, 1, false, null, null, __('Thank you for registering with %1.', 'frontend')],
                 [
                     1,
                     1,
                     true,
                     Address::TYPE_BILLING,
    +                null,
                     __(
                         'If you are a registered VAT customer, please click <a href="%1">here</a>'
                         . ' to enter your billing address for proper VAT calculation.',
    @@ -405,12 +435,13 @@ public function getSuccessMessageDataProvider(): array
                     1,
                     true,
                     Address::TYPE_SHIPPING,
    +                null,
                     __(
                         'If you are a registered VAT customer, please click <a href="%1">here</a>'
                         . ' to enter your shipping address for proper VAT calculation.',
                         'http://store.web/customer/address/edit'
                     )
    -            ]
    +            ],
             ];
         }
     
    @@ -421,7 +452,8 @@ public function getSuccessMessageDataProvider(): array
          * @param $successUrl
          * @param $resultUrl
          * @param $isSetFlag
    -     * @param Phrase $successMessage
    +     * @param $successMessage
    +     * @param $lastLoginAt
          *
          * @return void
          * @dataProvider getSuccessRedirectDataProvider
    @@ -433,7 +465,8 @@ public function testSuccessRedirect(
             $successUrl,
             $resultUrl,
             $isSetFlag,
    -        Phrase $successMessage
    +        $lastLoginAt,
    +        $successMessage
         ): void {
             $this->customerSessionMock->expects($this->once())
                 ->method('isLoggedIn')
    @@ -443,7 +476,7 @@ public function testSuccessRedirect(
                 ->method('getParam')
                 ->willReturnMap(
                     [
    -                    ['id', false, $customerId],
    +                    ['id', 0, $customerId],
                         ['key', false, $key],
                         ['back_url', false, $backUrl]
                     ]
    @@ -469,23 +502,28 @@ public function testSuccessRedirect(
                 ->with($this->customerDataMock)
                 ->willReturnSelf();
     
    -        $this->messageManagerMock
    -            ->method('addSuccess')
    +        $this->messageManagerMock->method('addSuccess')
                 ->with($successMessage)
                 ->willReturnSelf();
     
    -        $this->messageManagerMock
    -            ->expects($this->never())
    +        $this->messageManagerMock->expects($this->never())
                 ->method('addException');
     
    -        $this->urlMock
    -            ->method('getUrl')
    +        $this->urlMock->method('getUrl')
                 ->willReturnMap([
                     ['customer/address/edit', null, 'http://store.web/customer/address/edit'],
                     ['*/*/admin', ['_secure' => true], 'http://store.web/back'],
                     ['*/*/index', ['_secure' => true], $successUrl]
                 ]);
     
    +        $this->logMock->expects($this->once())
    +            ->method('getLastLoginAt')
    +            ->willReturn($lastLoginAt);
    +        $this->customerLoggerMock->expects($this->once())
    +            ->method('get')
    +            ->with(1)
    +            ->willReturn($this->logMock);
    +
             $this->storeMock->expects($this->any())
                 ->method('getFrontendName')
                 ->willReturn('frontend');
    @@ -500,10 +538,7 @@ public function testSuccessRedirect(
     
             $this->scopeConfigMock->expects($this->any())
                 ->method('isSetFlag')
    -            ->with(
    -                Url::XML_PATH_CUSTOMER_STARTUP_REDIRECT_TO_DASHBOARD,
    -                ScopeInterface::SCOPE_STORE
    -            )
    +            ->with(Url::XML_PATH_CUSTOMER_STARTUP_REDIRECT_TO_DASHBOARD, ScopeInterface::SCOPE_STORE)
                 ->willReturn($isSetFlag);
     
             $cookieMetadataManager = $this->getMockBuilder(PhpCookieManager::class)
    @@ -535,6 +570,7 @@ public function getSuccessRedirectDataProvider(): array
                     null,
                     'http://example.com/back',
                     true,
    +                null,
                     __('Thank you for registering with %1.', 'frontend'),
                 ],
                 [
    @@ -544,6 +580,7 @@ public function getSuccessRedirectDataProvider(): array
                     'http://example.com/success',
                     'http://example.com/success',
                     true,
    +                null,
                     __('Thank you for registering with %1.', 'frontend'),
                 ],
                 [
    @@ -553,7 +590,18 @@ public function getSuccessRedirectDataProvider(): array
                     'http://example.com/success',
                     'http://example.com/success',
                     false,
    +                null,
                     __('Thank you for registering with %1.', 'frontend'),
    +            ],
    +            [
    +                1,
    +                1,
    +                null,
    +                'http://example.com/success',
    +                'http://example.com/success',
    +                false,
    +                'some data',
    +                null,
                 ]
             ];
         }
    
  • app/code/Magento/Customer/Test/Unit/Model/Plugin/CustomerNotificationTest.php+24 1 modified
    @@ -20,7 +20,13 @@
     use PHPUnit\Framework\MockObject\MockObject;
     use PHPUnit\Framework\TestCase;
     use Psr\Log\LoggerInterface;
    +use Magento\Framework\Session\StorageInterface;
     
    +/**
    + * Unit test for CustomerNotification plugin
    + *
    + * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
    + */
     class CustomerNotificationTest extends TestCase
     {
         private const STUB_CUSTOMER_ID = 1;
    @@ -65,6 +71,11 @@ class CustomerNotificationTest extends TestCase
          */
         private $plugin;
     
    +    /**
    +     * @var StorageInterface|MockObject
    +     */
    +    private $storage;
    +
         protected function setUp(): void
         {
             $this->sessionMock = $this->createMock(Session::class);
    @@ -87,19 +98,27 @@ protected function setUp(): void
                 ->with(NotificationStorage::UPDATE_CUSTOMER_SESSION, self::STUB_CUSTOMER_ID)
                 ->willReturn(true);
     
    +        $this->storage = $this
    +            ->getMockBuilder(StorageInterface::class)
    +            ->addMethods(['getData', 'setData'])
    +            ->disableOriginalConstructor()
    +            ->getMockForAbstractClass();
    +
             $this->plugin = new CustomerNotification(
                 $this->sessionMock,
                 $this->notificationStorageMock,
                 $this->appStateMock,
                 $this->customerRepositoryMock,
                 $this->loggerMock,
    -            $this->requestMock
    +            $this->requestMock,
    +            $this->storage
             );
         }
     
         public function testBeforeExecute()
         {
             $customerGroupId = 1;
    +        $testSessionId = [uniqid()];
     
             $customerMock = $this->getMockForAbstractClass(CustomerInterface::class);
             $customerMock->method('getGroupId')->willReturn($customerGroupId);
    @@ -116,6 +135,10 @@ public function testBeforeExecute()
             $this->sessionMock->expects($this->once())->method('setCustomerData')->with($customerMock);
             $this->sessionMock->expects($this->once())->method('setCustomerGroupId')->with($customerGroupId);
             $this->sessionMock->expects($this->once())->method('regenerateId');
    +        $this->storage->expects($this->once())->method('getData')->willReturn($testSessionId);
    +        $this->storage
    +            ->expects($this->once())
    +            ->method('setData');
     
             $this->plugin->beforeExecute($this->actionMock);
         }
    
  • app/code/Magento/Deploy/composer.json+12 10 modified
    @@ -1,22 +1,23 @@
     {
         "name": "magento/module-deploy",
         "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-config": "*",
    -        "magento/module-require-js": "*",
    -        "magento/module-store": "*",
    -        "magento/module-user": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-config": "101.2.*",
    +        "magento/module-require-js": "100.4.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/module-user": "101.2.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "cli_commands.php",
    @@ -27,3 +28,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Developer/composer.json+10 8 modified
    @@ -1,20 +1,21 @@
     {
         "name": "magento/module-developer",
         "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-config": "*",
    -        "magento/module-store": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-config": "101.2.*",
    +        "magento/module-store": "101.1.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -24,3 +25,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Dhl/composer.json+18 16 modified
    @@ -1,31 +1,32 @@
     {
         "name": "magento/module-dhl",
         "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",
             "lib-libxml": "*",
    -        "magento/framework": "*",
    -        "magento/module-backend": "*",
    -        "magento/module-catalog": "*",
    -        "magento/module-catalog-inventory": "*",
    -        "magento/module-config": "*",
    -        "magento/module-directory": "*",
    -        "magento/module-quote": "*",
    -        "magento/module-sales": "*",
    -        "magento/module-shipping": "*",
    -        "magento/module-store": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-catalog-inventory": "100.4.*",
    +        "magento/module-config": "101.2.*",
    +        "magento/module-directory": "100.4.*",
    +        "magento/module-quote": "101.2.*",
    +        "magento/module-sales": "103.0.*",
    +        "magento/module-shipping": "100.4.*",
    +        "magento/module-store": "101.1.*"
         },
         "suggest": {
    -        "magento/module-checkout": "*"
    +        "magento/module-checkout": "100.4.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -35,3 +36,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Directory/composer.json+11 9 modified
    @@ -1,22 +1,23 @@
     {
         "name": "magento/module-directory",
         "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",
             "lib-libxml": "*",
    -        "magento/framework": "*",
    -        "magento/module-backend": "*",
    -        "magento/module-config": "*",
    -        "magento/module-store": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-config": "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 @@
             }
         }
     }
    +
    
  • app/code/Magento/DirectoryGraphQl/composer.json+9 7 modified
    @@ -2,17 +2,18 @@
         "name": "magento/module-directory-graph-ql",
         "description": "N/A",
         "type": "magento2-module",
    -    "require": {
    -        "php": "~7.4.0||~8.1.0",
    -        "magento/module-directory": "*",
    -        "magento/module-store": "*",
    -        "magento/module-graph-ql": "*",
    -        "magento/framework": "*"
    -    },
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "100.4.2",
    +    "require": {
    +        "php": "~7.4.0||~8.1.0",
    +        "magento/module-directory": "100.4.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/module-graph-ql": "100.4.*",
    +        "magento/framework": "103.0.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -22,3 +23,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Downloadable/composer.json+25 23 modified
    @@ -1,37 +1,38 @@
     {
         "name": "magento/module-downloadable",
         "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-inventory": "*",
    -        "magento/module-checkout": "*",
    -        "magento/module-config": "*",
    -        "magento/module-customer": "*",
    -        "magento/module-directory": "*",
    -        "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-theme": "*",
    -        "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-config": "101.2.*",
    +        "magento/module-customer": "103.0.*",
    +        "magento/module-directory": "100.4.*",
    +        "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-theme": "101.1.*",
    +        "magento/module-ui": "101.2.*"
         },
         "suggest": {
    -        "magento/module-downloadable-sample-data": "*"
    +        "magento/module-downloadable-sample-data": "Sample Data version: 100.4.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -41,3 +42,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/DownloadableGraphQl/composer.json+15 13 modified
    @@ -2,24 +2,25 @@
         "name": "magento/module-downloadable-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-store": "*",
    -        "magento/module-catalog": "*",
    -        "magento/module-downloadable": "*",
    -        "magento/module-quote": "*",
    -        "magento/module-sales": "*",
    -        "magento/module-quote-graph-ql": "*",
    -        "magento/framework": "*"
    +        "magento/module-store": "101.1.*",
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-downloadable": "100.4.*",
    +        "magento/module-quote": "101.2.*",
    +        "magento/module-sales": "103.0.*",
    +        "magento/module-quote-graph-ql": "100.4.*",
    +        "magento/framework": "103.0.*"
         },
         "suggest": {
    -        "magento/module-catalog-graph-ql": "*",
    -        "magento/module-sales-graph-ql": "*"
    +        "magento/module-catalog-graph-ql": "100.4.*",
    +        "magento/module-sales-graph-ql": "100.4.*"
         },
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -29,3 +30,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/DownloadableImportExport/composer.json+14 12 modified
    @@ -1,24 +1,25 @@
     {
         "name": "magento/module-downloadable-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-catalog": "*",
    -        "magento/module-catalog-import-export": "*",
    -        "magento/module-downloadable": "*",
    -        "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-downloadable": "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 @@
             }
         }
     }
    +
    
  • app/code/Magento/Eav/composer.json+13 11 modified
    @@ -1,23 +1,24 @@
     {
         "name": "magento/module-eav",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "102.1.4",
         "require": {
             "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    -        "magento/module-backend": "*",
    -        "magento/module-catalog": "*",
    -        "magento/module-config": "*",
    -        "magento/module-media-storage": "*",
    -        "magento/module-store": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-config": "101.2.*",
    +        "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"
    @@ -27,3 +28,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/EavGraphQl/composer.json+9 7 modified
    @@ -2,18 +2,19 @@
         "name": "magento/module-eav-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-eav": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-eav": "102.1.*"
         },
         "suggest": {
    -        "magento/module-graph-ql": "*"
    +        "magento/module-graph-ql": "100.4.*"
         },
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -23,3 +24,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Elasticsearch6/composer.json+13 11 modified
    @@ -1,23 +1,24 @@
     {
         "name": "magento/module-elasticsearch-6",
         "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/framework": "*",
    -        "magento/module-advanced-search": "*",
    -        "magento/module-catalog-search": "*",
    -        "magento/module-search": "*",
    -        "magento/module-elasticsearch": "*",
    +        "magento/framework": "103.0.*",
    +        "magento/module-advanced-search": "100.4.*",
    +        "magento/module-catalog-search": "102.0.*",
    +        "magento/module-search": "101.1.*",
    +        "magento/module-elasticsearch": "101.0.*",
             "elasticsearch/elasticsearch": "~7.16.0"
         },
         "suggest": {
    -        "magento/module-config": "*"
    +        "magento/module-config": "101.2.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -27,3 +28,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Elasticsearch7/composer.json+13 11 modified
    @@ -1,23 +1,24 @@
     {
         "name": "magento/module-elasticsearch-7",
         "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/framework": "*",
    -        "magento/module-elasticsearch": "*",
    +        "magento/framework": "103.0.*",
    +        "magento/module-elasticsearch": "101.0.*",
             "elasticsearch/elasticsearch": "~7.16.0",
    -        "magento/module-advanced-search": "*",
    -        "magento/module-catalog-search": "*"
    +        "magento/module-advanced-search": "100.4.*",
    +        "magento/module-catalog-search": "102.0.*"
         },
         "suggest": {
    -        "magento/module-config": "*",
    -        "magento/module-search": "*"
    +        "magento/module-config": "101.2.*",
    +        "magento/module-search": "101.1.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -27,3 +28,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Elasticsearch/composer.json+17 15 modified
    @@ -1,27 +1,28 @@
     {
         "name": "magento/module-elasticsearch",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
    +    "version": "101.0.4-p1",
         "require": {
             "php": "~7.4.0||~8.1.0",
    -        "magento/module-advanced-search": "*",
    -        "magento/module-catalog": "*",
    -        "magento/module-catalog-search": "*",
    -        "magento/module-customer": "*",
    -        "magento/module-eav": "*",
    -        "magento/module-search": "*",
    -        "magento/module-store": "*",
    -        "magento/module-catalog-inventory": "*",
    -        "magento/framework": "*",
    +        "magento/module-advanced-search": "100.4.*",
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-catalog-search": "102.0.*",
    +        "magento/module-customer": "103.0.*",
    +        "magento/module-eav": "102.1.*",
    +        "magento/module-search": "101.1.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/module-catalog-inventory": "100.4.*",
    +        "magento/framework": "103.0.*",
             "elasticsearch/elasticsearch": "~7.16.0"
         },
         "suggest": {
    -        "magento/module-config": "*"
    +        "magento/module-config": "101.2.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -31,3 +32,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Elasticsearch/SearchAdapter/Query/Builder/Sort.php+2 1 modified
    @@ -89,7 +89,8 @@ public function getSort(RequestInterface $request)
                 if (in_array($item['field'], $this->skippedFields)) {
                     continue;
                 }
    -            $attribute = $this->attributeAdapterProvider->getByAttributeCode($item['field']);
    +            $itemField = $item['field'] != null ? $item['field'] : '';
    +            $attribute = $this->attributeAdapterProvider->getByAttributeCode($itemField);
                 $fieldName = $this->fieldNameResolver->getFieldName($attribute);
                 if (isset($this->map[$fieldName])) {
                     $fieldName = $this->map[$fieldName];
    
  • app/code/Magento/Email/composer.json+18 16 modified
    @@ -1,30 +1,31 @@
     {
         "name": "magento/module-email",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "101.1.4-p7",
         "require": {
             "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    -        "magento/module-backend": "*",
    -        "magento/module-cms": "*",
    -        "magento/module-config": "*",
    -        "magento/module-store": "*",
    -        "magento/module-theme": "*",
    -        "magento/module-require-js": "*",
    -        "magento/module-media-storage": "*",
    -        "magento/module-variable": "*",
    -        "magento/module-ui": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-cms": "104.0.*",
    +        "magento/module-config": "101.2.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/module-theme": "101.1.*",
    +        "magento/module-require-js": "100.4.*",
    +        "magento/module-media-storage": "100.4.*",
    +        "magento/module-variable": "100.4.*",
    +        "magento/module-ui": "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"
    @@ -34,3 +35,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Email/Model/Template/Filter.php+10 3 modified
    @@ -53,12 +53,12 @@ class Filter extends Template
         /**
          * The name used in the {{trans}} directive
          */
    -    const TRANS_DIRECTIVE_NAME = 'trans';
    +    public const TRANS_DIRECTIVE_NAME = 'trans';
     
         /**
          * The regex to match interior portion of a {{trans "foo"}} translation directive
          */
    -    const TRANS_DIRECTIVE_REGEX = '/^\s*([\'"])([^\1]*?)(?<!\\\)\1(\s.*)?$/si';
    +    public const TRANS_DIRECTIVE_REGEX = '/^\s*([\'"])([^\1]*?)(?<!\\\)\1(\s.*)?$/si';
     
         /**
          * @var bool
    @@ -68,12 +68,14 @@ class Filter extends Template
         /**
          * @var bool
          * @deprecated SID is not being used as query parameter anymore.
    +     * @see Session ID's in URL
          */
         protected $_useSessionInUrl = false;
     
         /**
          * @var array
          * @deprecated 101.0.4 Use the new Directive Processor interfaces
    +     * @see Directive Processor interfaces
          */
         protected $_modifiers = ['nl2br' => ''];
     
    @@ -119,7 +121,7 @@ class Filter extends Template
     
         /**
          * Core store config
    -     * Variable factory
    +     * Factory for Variable model
          *
          * @var VariableFactory
          */
    @@ -273,6 +275,7 @@ public function setUseAbsoluteLinks($flag)
          * @return $this
          * @SuppressWarnings(PHPMD.UnusedFormalParameter)
          * @deprecated SID query parameter is not used in URLs anymore.
    +     * @see SessionId's in URL
          */
         public function setUseSessionInUrl($flag)
         {
    @@ -396,6 +399,7 @@ public function blockDirective($construction)
         {
             $skipParams = ['class', 'id', 'output'];
             $blockParameters = $this->getParameters($construction[2]);
    +
             $block = null;
     
             if (isset($blockParameters['class'])) {
    @@ -679,6 +683,7 @@ public function varDirective($construction)
          * @param string $default assumed modifier if none present
          * @return array
          * @deprecated 101.0.4 Use the new FilterApplier or Directive Processor interfaces
    +     * @see Directive Processor Interfaces
          */
         protected function explodeModifiers($value, $default = null)
         {
    @@ -698,6 +703,7 @@ protected function explodeModifiers($value, $default = null)
          * @param string $modifiers
          * @return string
          * @deprecated 101.0.4 Use the new FilterApplier or Directive Processor interfaces
    +     * @see Directive Processor Interfaces
          */
         protected function applyModifiers($value, $modifiers)
         {
    @@ -726,6 +732,7 @@ protected function applyModifiers($value, $modifiers)
          * @param string $type
          * @return string
          * @deprecated 101.0.4 Use the new FilterApplier or Directive Processor interfaces
    +     * @see Directive Processor Interfacees
          */
         public function modifierEscape($value, $type = 'html')
         {
    
  • app/code/Magento/EncryptionKey/composer.json+10 8 modified
    @@ -1,20 +1,21 @@
     {
         "name": "magento/module-encryption-key",
         "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-backend": "*",
    -        "magento/module-config": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-config": "101.2.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -24,3 +25,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Fedex/composer.json+16 14 modified
    @@ -1,27 +1,28 @@
     {
         "name": "magento/module-fedex",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.3-p4",
         "require": {
             "php": "~7.4.0||~8.1.0",
             "lib-libxml": "*",
    -        "magento/framework": "*",
    -        "magento/module-catalog": "*",
    -        "magento/module-catalog-inventory": "*",
    -        "magento/module-config": "*",
    -        "magento/module-directory": "*",
    -        "magento/module-quote": "*",
    -        "magento/module-sales": "*",
    -        "magento/module-shipping": "*",
    -        "magento/module-store": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-catalog-inventory": "100.4.*",
    +        "magento/module-config": "101.2.*",
    +        "magento/module-directory": "100.4.*",
    +        "magento/module-quote": "101.2.*",
    +        "magento/module-sales": "103.0.*",
    +        "magento/module-shipping": "100.4.*",
    +        "magento/module-store": "101.1.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -31,3 +32,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Fedex/etc/adminhtml/system.xml+2 0 modified
    @@ -40,12 +40,14 @@
                     </field>
                     <field id="production_webservices_url" translate="label" type="text" sortOrder="90" showInDefault="1" showInWebsite="1" canRestore="1">
                         <label>Web-Services URL (Production)</label>
    +                    <backend_model>Magento\Fedex\Model\Config\Backend\FedexUrl</backend_model>
                         <depends>
                             <field id="sandbox_mode">0</field>
                         </depends>
                     </field>
                     <field id="sandbox_webservices_url" translate="label" type="text" sortOrder="100" showInDefault="1" showInWebsite="1" canRestore="1">
                         <label>Web-Services URL (Sandbox)</label>
    +                    <backend_model>Magento\Fedex\Model\Config\Backend\FedexUrl</backend_model>
                         <depends>
                             <field id="sandbox_mode">1</field>
                         </depends>
    
  • app/code/Magento/Fedex/i18n/en_US.csv+1 0 modified
    @@ -78,3 +78,4 @@ Debug,Debug
     "Show Method if Not Applicable","Show Method if Not Applicable"
     "Sort Order","Sort Order"
     "Can't convert a shipping cost from ""%1-%2"" for FedEx carrier.","Can't convert a shipping cost from ""%1-%2"" for FedEx carrier."
    +"Fedex API endpoint URL\'s must use fedex.com","Fedex API endpoint URL\'s must use fedex.com"
    
  • app/code/Magento/Fedex/Model/Config/Backend/FedexUrl.php+75 0 added
    @@ -0,0 +1,75 @@
    +<?php
    +/**
    + * Copyright © Magento, Inc. All rights reserved.
    + * See COPYING.txt for license details.
    + */
    +
    +declare(strict_types=1);
    +
    +namespace Magento\Fedex\Model\Config\Backend;
    +
    +use Magento\Framework\App\Cache\TypeListInterface;
    +use Magento\Framework\App\Config\ScopeConfigInterface;
    +use Magento\Framework\App\Config\Value;
    +use Magento\Framework\Data\Collection\AbstractDb;
    +use Magento\Framework\Exception\ValidatorException;
    +use Magento\Framework\Model\Context;
    +use Magento\Framework\Model\ResourceModel\AbstractResource;
    +use Magento\Framework\Registry;
    +use Magento\Framework\Validator\Url;
    +use Magento\Framework\Model\AbstractModel;
    +
    +/**
    + * Represents a config URL that may point to a Fedex endpoint
    + */
    +class FedexUrl extends Value
    +{
    +    /**
    +     * @var Url
    +     */
    +    private Url $url;
    +
    +    /**
    +     * @param Url $url
    +     * @param Context $context
    +     * @param Registry $registry
    +     * @param ScopeConfigInterface $config
    +     * @param TypeListInterface $cacheTypeList
    +     * @param AbstractResource|null $resource
    +     * @param AbstractDb|null $resourceCollection
    +     * @param array $data
    +     */
    +    public function __construct(
    +        Url $url,
    +        Context $context,
    +        Registry $registry,
    +        ScopeConfigInterface $config,
    +        TypeListInterface $cacheTypeList,
    +        AbstractResource $resource = null,
    +        AbstractDb $resourceCollection = null,
    +        array $data = []
    +    ) {
    +        $this->url = $url;
    +        parent::__construct($context, $registry, $config, $cacheTypeList, $resource, $resourceCollection, $data);
    +    }
    +
    +    /**
    +     * @inheritdoc
    +     *
    +     * @throws ValidatorException
    +     */
    +    public function beforeSave(): AbstractModel
    +    {
    +        $isValid = $this->url->isValid($this->getValue());
    +        if ($isValid) {
    +            // phpcs:ignore Magento2.Functions.DiscouragedFunction
    +            $host = parse_url((string)$this->getValue(), \PHP_URL_HOST);
    +
    +            if (!empty($host) && !preg_match('/(?:.+\.|^)fedex\.com$/i', $host)) {
    +                throw new ValidatorException(__('Fedex API endpoint URL\'s must use fedex.com'));
    +            }
    +        }
    +
    +        return parent::beforeSave();
    +    }
    +}
    
  • app/code/Magento/Fedex/Test/Unit/Model/Config/Backend/FedexUrlTest.php+131 0 added
    @@ -0,0 +1,131 @@
    +<?php
    +/**
    + * Copyright © Magento, Inc. All rights reserved.
    + * See COPYING.txt for license details.
    + */
    +
    +declare(strict_types=1);
    +
    +namespace Magento\Fedex\Test\Unit\Model\Config\Backend;
    +
    +use Magento\Fedex\Model\Config\Backend\FedexUrl;
    +use Magento\Framework\App\Cache\TypeListInterface;
    +use Magento\Framework\App\Config\ScopeConfigInterface;
    +use Magento\Framework\Event\ManagerInterface;
    +use Magento\Framework\Exception\ValidatorException;
    +use Magento\Framework\Registry;
    +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
    +use Magento\Rule\Model\ResourceModel\AbstractResource;
    +use Magento\Framework\Data\Collection\AbstractDb;
    +use PHPUnit\Framework\MockObject\MockObject;
    +use PHPUnit\Framework\TestCase;
    +use Magento\Framework\Validator\Url;
    +use Magento\Framework\Model\Context;
    +
    +/**
    + * Verify behavior of FedexUrl backend type
    + */
    +class FedexUrlTest extends TestCase
    +{
    +
    +    /**
    +     * @var FedexUrl
    +     */
    +    private $urlConfig;
    +
    +    /**
    +     * @var Url
    +     */
    +    private $url;
    +
    +    /**
    +     * @var Context|MockObject
    +     */
    +    private $contextMock;
    +
    +    protected function setUp(): void
    +    {
    +        $objectManager = new ObjectManager($this);
    +        $this->contextMock = $this->createMock(Context::class);
    +        $registry = $this->createMock(Registry::class);
    +        $config = $this->createMock(ScopeConfigInterface::class);
    +        $cacheTypeList = $this->createMock(TypeListInterface::class);
    +        $this->url = $this->createMock(Url::class);
    +        $resource = $this->createMock(AbstractResource::class);
    +        $resourceCollection = $this->createMock(AbstractDb::class);
    +        $eventManagerMock = $this->getMockForAbstractClass(ManagerInterface::class);
    +        $eventManagerMock->expects($this->any())->method('dispatch');
    +        $this->contextMock->expects($this->any())->method('getEventDispatcher')->willReturn($eventManagerMock);
    +
    +        $this->urlConfig = $objectManager->getObject(
    +            FedexUrl::class,
    +            [
    +                'url' => $this->url,
    +                'context' => $this->contextMock,
    +                'registry' => $registry,
    +                'config' => $config,
    +                'cacheTypeList' => $cacheTypeList,
    +                'resource' => $resource,
    +                'resourceCollection' => $resourceCollection,
    +            ]
    +        );
    +    }
    +
    +    /**
    +     * @dataProvider validDataProvider
    +     * @param string|null $data The valid data
    +     * @throws ValidatorException
    +     */
    +    public function testBeforeSave(string $data = null): void
    +    {
    +        $this->url->expects($this->any())->method('isValid')->willReturn(true);
    +        $this->urlConfig->setValue($data);
    +        $this->urlConfig->beforeSave();
    +        $this->assertTrue($this->url->isValid($data));
    +    }
    +
    +    /**
    +     * @dataProvider invalidDataProvider
    +     * @param string $data The invalid data
    +     */
    +    public function testBeforeSaveErrors(string $data): void
    +    {
    +        $this->url->expects($this->any())->method('isValid')->willReturn(true);
    +        $this->expectException('Magento\Framework\Exception\ValidatorException');
    +        $this->expectExceptionMessage('Fedex API endpoint URL\'s must use fedex.com');
    +        $this->urlConfig->setValue($data);
    +        $this->urlConfig->beforeSave();
    +    }
    +
    +    /**
    +     * Validator Data Provider
    +     *
    +     * @return array
    +     */
    +    public function validDataProvider(): array
    +    {
    +        return [
    +            [],
    +            [null],
    +            [''],
    +            ['http://fedex.com'],
    +            ['https://foo.fedex.com'],
    +            ['http://foo.fedex.com/foo/bar?baz=bash&fizz=buzz'],
    +        ];
    +    }
    +
    +    /**
    +     * @return \string[][]
    +     */
    +    public function invalidDataProvider(): array
    +    {
    +        return [
    +            ['http://fedexfoo.com'],
    +            ['https://foofedex.com'],
    +            ['https://fedex.com.fake.com'],
    +            ['https://fedex.info'],
    +            ['http://fedex.com.foo.com/foo/bar?baz=bash&fizz=buzz'],
    +            ['http://foofedex.com/foo/bar?baz=bash&fizz=buzz'],
    +        ];
    +    }
    +}
    
  • app/code/Magento/GiftMessage/composer.json+18 16 modified
    @@ -1,30 +1,31 @@
     {
         "name": "magento/module-gift-message",
         "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-backend": "*",
    -        "magento/module-catalog": "*",
    -        "magento/module-checkout": "*",
    -        "magento/module-customer": "*",
    -        "magento/module-quote": "*",
    -        "magento/module-sales": "*",
    -        "magento/module-store": "*",
    -        "magento/module-ui": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-checkout": "100.4.*",
    +        "magento/module-customer": "103.0.*",
    +        "magento/module-quote": "101.2.*",
    +        "magento/module-sales": "103.0.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/module-ui": "101.2.*"
         },
         "suggest": {
    -        "magento/module-eav": "*",
    -        "magento/module-multishipping": "*"
    +        "magento/module-eav": "102.1.*",
    +        "magento/module-multishipping": "100.4.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -34,3 +35,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/GiftMessageGraphQl/composer.json+9 7 modified
    @@ -2,18 +2,19 @@
         "name": "magento/module-gift-message-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-gift-message": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-gift-message": "100.4.*"
         },
         "suggest": {
    -        "magento/module-graph-ql": "*"
    +        "magento/module-graph-ql": "100.4.*"
         },
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -23,3 +24,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/GoogleAdwords/composer.json+10 8 modified
    @@ -1,20 +1,21 @@
     {
         "name": "magento/module-google-adwords",
         "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-sales": "*",
    -        "magento/module-store": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-sales": "103.0.*",
    +        "magento/module-store": "101.1.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -24,3 +25,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/GoogleAnalytics/composer.json+12 10 modified
    @@ -1,24 +1,25 @@
     {
         "name": "magento/module-google-analytics",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.1",
         "require": {
             "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    -        "magento/module-cookie": "*",
    -        "magento/module-sales": "*",
    -        "magento/module-store": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-cookie": "100.4.*",
    +        "magento/module-sales": "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"
    @@ -28,3 +29,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/GoogleOptimizer/composer.json+14 12 modified
    @@ -1,24 +1,25 @@
     {
         "name": "magento/module-google-optimizer",
         "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-backend": "*",
    -        "magento/module-catalog": "*",
    -        "magento/module-cms": "*",
    -        "magento/module-google-analytics": "*",
    -        "magento/module-store": "*",
    -        "magento/module-ui": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-cms": "104.0.*",
    +        "magento/module-google-analytics": "100.4.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/module-ui": "101.2.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -28,3 +29,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/GraphQlCache/composer.json+10 8 modified
    @@ -2,18 +2,19 @@
         "name": "magento/module-graph-ql-cache",
         "description": "N/A",
         "type": "magento2-module",
    -    "require": {
    -        "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    -        "magento/module-page-cache": "*",
    -        "magento/module-graph-ql": "*",
    -        "magento/module-authorization": "*",
    -        "magento/module-integration": "*"
    -    },
         "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-page-cache": "100.4.*",
    +        "magento/module-graph-ql": "100.4.*",
    +        "magento/module-authorization": "100.4.*",
    +        "magento/module-integration": "100.4.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -23,3 +24,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/GraphQl/composer.json+12 10 modified
    @@ -2,22 +2,23 @@
         "name": "magento/module-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/framework": "*",
    -        "magento/module-webapi": "*",
    -        "magento/module-new-relic-reporting": "*",
    -        "magento/module-authorization": "*",
    +        "magento/module-eav": "102.1.*",
    +        "magento/framework": "103.0.*",
    +        "magento/module-webapi": "100.4.*",
    +        "magento/module-new-relic-reporting": "100.4.*",
    +        "magento/module-authorization": "100.4.*",
             "webonyx/graphql-php": "~14.11.3"
         },
         "suggest": {
    -        "magento/module-graph-ql-cache": "*"
    +        "magento/module-graph-ql-cache": "100.4.*"
         },
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -27,3 +28,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/GroupedCatalogInventory/composer.json+11 9 modified
    @@ -1,21 +1,22 @@
     {
         "name": "magento/module-grouped-catalog-inventory",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.1",
         "require": {
             "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    -        "magento/module-catalog": "*",
    -        "magento/module-catalog-inventory": "*",
    -        "magento/module-grouped-product": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-catalog-inventory": "100.4.*",
    +        "magento/module-grouped-product": "100.4.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -25,3 +26,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/GroupedImportExport/composer.json+13 11 modified
    @@ -1,23 +1,24 @@
     {
         "name": "magento/module-grouped-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-eav": "*",
    -        "magento/module-grouped-product": "*",
    -        "magento/module-import-export": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-catalog-import-export": "101.1.*",
    +        "magento/module-eav": "102.1.*",
    +        "magento/module-grouped-product": "100.4.*",
    +        "magento/module-import-export": "101.0.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -27,3 +28,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/GroupedProduct/composer.json+22 20 modified
    @@ -1,34 +1,35 @@
     {
         "name": "magento/module-grouped-product",
         "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-inventory": "*",
    -        "magento/module-checkout": "*",
    -        "magento/module-customer": "*",
    -        "magento/module-eav": "*",
    -        "magento/module-media-storage": "*",
    -        "magento/module-msrp": "*",
    -        "magento/module-quote": "*",
    -        "magento/module-sales": "*",
    -        "magento/module-store": "*",
    -        "magento/module-ui": "*",
    -        "magento/module-wishlist": "*"
    +        "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-msrp": "100.4.*",
    +        "magento/module-quote": "101.2.*",
    +        "magento/module-sales": "103.0.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/module-ui": "101.2.*",
    +        "magento/module-wishlist": "101.2.*"
         },
         "suggest": {
    -        "magento/module-grouped-product-sample-data": "*"
    +        "magento/module-grouped-product-sample-data": "Sample Data version: 100.4.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -38,3 +39,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/GroupedProductGraphQl/composer.json+9 7 modified
    @@ -2,17 +2,18 @@
         "name": "magento/module-grouped-product-graph-ql",
         "description": "N/A",
         "type": "magento2-module",
    -    "require": {
    -        "php": "~7.4.0||~8.1.0",
    -        "magento/module-grouped-product": "*",
    -        "magento/module-catalog": "*",
    -        "magento/module-catalog-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-grouped-product": "100.4.*",
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-catalog-graph-ql": "100.4.*",
    +        "magento/framework": "103.0.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -22,3 +23,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/ImportExport/composer.json+14 12 modified
    @@ -1,25 +1,26 @@
     {
         "name": "magento/module-import-export",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "101.0.4",
         "require": {
             "php": "~7.4.0||~8.1.0",
             "ext-ctype": "*",
    -        "magento/framework": "*",
    -        "magento/module-backend": "*",
    -        "magento/module-catalog": "*",
    -        "magento/module-eav": "*",
    -        "magento/module-media-storage": "*",
    -        "magento/module-store": "*",
    -        "magento/module-ui": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-eav": "102.1.*",
    +        "magento/module-media-storage": "100.4.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/module-ui": "101.2.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -29,3 +30,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Indexer/composer.json+9 7 modified
    @@ -1,19 +1,20 @@
     {
         "name": "magento/module-indexer",
         "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/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 @@
             }
         }
     }
    +
    
  • app/code/Magento/InstantPurchase/composer.json+10 8 modified
    @@ -6,16 +6,17 @@
             "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-customer": "*",
    -        "magento/module-sales": "*",
    -        "magento/module-shipping": "*",
    -        "magento/module-quote": "*",
    -        "magento/module-vault": "*",
    -        "magento/framework": "*"
    +        "magento/module-store": "101.1.*",
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-customer": "103.0.*",
    +        "magento/module-sales": "103.0.*",
    +        "magento/module-shipping": "100.4.*",
    +        "magento/module-quote": "101.2.*",
    +        "magento/module-vault": "101.2.*",
    +        "magento/framework": "103.0.*"
         },
         "autoload": {
             "files": [
    @@ -26,3 +27,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Integration/composer.json+15 13 modified
    @@ -1,25 +1,26 @@
     {
         "name": "magento/module-integration",
         "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-authorization": "*",
    -        "magento/module-backend": "*",
    -        "magento/module-customer": "*",
    -        "magento/module-security": "*",
    -        "magento/module-store": "*",
    -        "magento/module-user": "*",
    -        "magento/module-ui": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-authorization": "100.4.*",
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-customer": "103.0.*",
    +        "magento/module-security": "100.4.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/module-user": "101.2.*",
    +        "magento/module-ui": "101.2.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -29,3 +30,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/JwtFrameworkAdapter/composer.json+8 6 modified
    @@ -1,19 +1,20 @@
     {
         "name": "magento/module-jwt-framework-adapter",
         "description": "JWT Manager implementation based on jwt-framework",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.1",
         "require": {
             "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    +        "magento/framework": "103.0.*",
             "web-token/jwt-framework": "^v2.2.7"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -23,3 +24,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/JwtUserToken/composer.json+10 8 modified
    @@ -1,20 +1,21 @@
     {
         "name": "magento/module-jwt-user-token",
         "description": "Introduces JWT token support for web API authentication",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.0",
         "require": {
             "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    -        "magento/module-integration": "*",
    -        "magento/module-authorization": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-integration": "100.4.*",
    +        "magento/module-authorization": "100.4.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -24,3 +25,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/LayeredNavigation/composer.json+10 8 modified
    @@ -1,20 +1,21 @@
     {
         "name": "magento/module-layered-navigation",
         "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/framework": "103.0.*",
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-config": "101.2.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -24,3 +25,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/LoginAsCustomerAdminUi/composer.json+15 14 modified
    @@ -1,24 +1,24 @@
     {
         "name": "magento/module-login-as-customer-admin-ui",
    -    "description": "",
    -    "require": {
    -        "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    -        "magento/module-login-as-customer-api": "*",
    -        "magento/module-login-as-customer-frontend-ui": "*",
    -        "magento/module-backend": "*",
    -        "magento/module-customer": "*",
    -        "magento/module-sales": "*",
    -        "magento/module-store": "*"
    -    },
    -    "suggest": {
    -        "magento/module-login-as-customer": "*"
    -    },
         "type": "magento2-module",
         "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-login-as-customer-api": "100.4.*",
    +        "magento/module-login-as-customer-frontend-ui": "100.4.*",
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-customer": "103.0.*",
    +        "magento/module-sales": "103.0.*",
    +        "magento/module-store": "101.1.*"
    +    },
    +    "suggest": {
    +        "magento/module-login-as-customer": "100.4.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -28,3 +28,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/LoginAsCustomerApi/composer.json+6 4 modified
    @@ -1,15 +1,16 @@
     {
         "name": "magento/module-login-as-customer-api",
         "description": "Allow for admin to enter a customer account",
    -    "require": {
    -        "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*"
    -    },
         "type": "magento2-module",
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "100.4.3",
    +    "require": {
    +        "php": "~7.4.0||~8.1.0",
    +        "magento/framework": "103.0.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -19,3 +20,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/LoginAsCustomerAssistance/composer.json+15 14 modified
    @@ -1,24 +1,24 @@
     {
         "name": "magento/module-login-as-customer-assistance",
    -    "description": "",
    -    "require": {
    -        "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    -        "magento/module-authorization": "*",
    -        "magento/module-backend": "*",
    -        "magento/module-customer": "*",
    -        "magento/module-store": "*",
    -        "magento/module-login-as-customer": "*",
    -        "magento/module-login-as-customer-api": "*"
    -    },
    -    "suggest": {
    -        "magento/module-login-as-customer-admin-ui": "*"
    -    },
         "type": "magento2-module",
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "100.4.3",
    +    "require": {
    +        "php": "~7.4.0||~8.1.0",
    +        "magento/framework": "103.0.*",
    +        "magento/module-authorization": "100.4.*",
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-customer": "103.0.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/module-login-as-customer": "100.4.*",
    +        "magento/module-login-as-customer-api": "100.4.*"
    +    },
    +    "suggest": {
    +        "magento/module-login-as-customer-admin-ui": "100.4.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -28,3 +28,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/LoginAsCustomer/composer.json+12 10 modified
    @@ -1,21 +1,22 @@
     {
         "name": "magento/module-login-as-customer",
         "description": "Allow for admin to enter a customer account",
    -    "require": {
    -        "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    -        "magento/module-backend": "*",
    -        "magento/module-customer": "*",
    -        "magento/module-login-as-customer-api": "*"
    -    },
    -    "suggest": {
    -        "magento/module-backend": "*"
    -    },
         "type": "magento2-module",
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "100.4.4-p8",
    +    "require": {
    +        "php": "~7.4.0||~8.1.0",
    +        "magento/framework": "103.0.*",
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-customer": "103.0.*",
    +        "magento/module-login-as-customer-api": "100.4.*"
    +    },
    +    "suggest": {
    +        "magento/module-backend": "102.0.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -25,3 +26,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/LoginAsCustomerFrontendUi/composer.json+9 8 modified
    @@ -1,18 +1,18 @@
     {
         "name": "magento/module-login-as-customer-frontend-ui",
    -    "description": "",
    -    "require": {
    -        "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    -        "magento/module-login-as-customer-api": "*",
    -        "magento/module-customer": "*",
    -        "magento/module-store": "*"
    -    },
         "type": "magento2-module",
         "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-login-as-customer-api": "100.4.*",
    +        "magento/module-customer": "103.0.*",
    +        "magento/module-store": "101.1.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -22,3 +22,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/LoginAsCustomerGraphQl/composer.json+14 12 modified
    @@ -1,23 +1,24 @@
     {
         "name": "magento/module-login-as-customer-graph-ql",
         "description": "Flexible login as a customer so a merchant or merchant admin can log into an end customer's account to assist them with their account.",
    -    "require": {
    -        "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    -        "magento/module-login-as-customer-api": "*",
    -        "magento/module-login-as-customer-assistance": "*",
    -        "magento/module-integration": "*",
    -        "magento/module-store": "*",
    -        "magento/module-customer": "*"
    -    },
    -    "suggest": {
    -        "magento/module-login-as-customer": "*"
    -    },
         "type": "magento2-module",
         "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-login-as-customer-api": "100.4.*",
    +        "magento/module-login-as-customer-assistance": "100.4.*",
    +        "magento/module-integration": "100.4.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/module-customer": "103.0.*"
    +    },
    +    "suggest": {
    +        "magento/module-login-as-customer": "100.4.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -27,3 +28,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/LoginAsCustomerLog/composer.json+14 13 modified
    @@ -1,23 +1,23 @@
     {
         "name": "magento/module-login-as-customer-log",
    -    "description": "",
    -    "require": {
    -        "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    -        "magento/module-backend": "*",
    -        "magento/module-customer": "*",
    -        "magento/module-login-as-customer-api": "*",
    -        "magento/module-ui": "*",
    -        "magento/module-user": "*"
    -    },
    -    "suggest": {
    -        "magento/module-login-as-customer": "*"
    -    },
         "type": "magento2-module",
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "100.4.3",
    +    "require": {
    +        "php": "~7.4.0||~8.1.0",
    +        "magento/framework": "103.0.*",
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-customer": "103.0.*",
    +        "magento/module-login-as-customer-api": "100.4.*",
    +        "magento/module-ui": "101.2.*",
    +        "magento/module-user": "101.2.*"
    +    },
    +    "suggest": {
    +        "magento/module-login-as-customer": "100.4.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -27,3 +27,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/LoginAsCustomerPageCache/composer.json+11 10 modified
    @@ -1,20 +1,20 @@
     {
         "name": "magento/module-login-as-customer-page-cache",
    -    "description": "",
    -    "require": {
    -        "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    -        "magento/module-store": "*",
    -        "magento/module-login-as-customer-api": "*"
    -    },
    -    "suggest": {
    -        "magento/module-page-cache": "*"
    -    },
         "type": "magento2-module",
         "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-store": "101.1.*",
    +        "magento/module-login-as-customer-api": "100.4.*"
    +    },
    +    "suggest": {
    +        "magento/module-page-cache": "100.4.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -24,3 +24,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/LoginAsCustomerQuote/composer.json+12 11 modified
    @@ -1,21 +1,21 @@
     {
         "name": "magento/module-login-as-customer-quote",
    -    "description": "",
    -    "require": {
    -        "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    -        "magento/module-checkout": "*",
    -        "magento/module-customer": "*",
    -        "magento/module-quote": "*"
    -    },
    -    "suggest": {
    -        "magento/module-login-as-customer-api": "*"
    -    },
         "type": "magento2-module",
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "100.4.3",
    +    "require": {
    +        "php": "~7.4.0||~8.1.0",
    +        "magento/framework": "103.0.*",
    +        "magento/module-checkout": "100.4.*",
    +        "magento/module-customer": "103.0.*",
    +        "magento/module-quote": "101.2.*"
    +    },
    +    "suggest": {
    +        "magento/module-login-as-customer-api": "100.4.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -25,3 +25,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/LoginAsCustomerSales/composer.json+12 11 modified
    @@ -1,21 +1,21 @@
     {
         "name": "magento/module-login-as-customer-sales",
    -    "description": "",
    -    "require": {
    -        "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    -        "magento/module-backend": "*",
    -        "magento/module-user": "*",
    -        "magento/module-login-as-customer-api": "*"
    -    },
    -    "suggest": {
    -        "magento/module-sales": "*"
    -    },
         "type": "magento2-module",
         "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-backend": "102.0.*",
    +        "magento/module-user": "101.2.*",
    +        "magento/module-login-as-customer-api": "100.4.*"
    +    },
    +    "suggest": {
    +        "magento/module-sales": "103.0.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -25,3 +25,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/LoginAsCustomer/Test/Mftf/Test/AdminLoginAsCustomerLoggingFilterTest.xml+2 0 modified
    @@ -26,8 +26,10 @@
                             stepKey="enableLoginAsCustomerAutoDetection"/>
                 <createData entity="Simple_US_Customer_Assistance_Allowed" stepKey="createFirstCustomer"/>
                 <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsDefaultUser"/>
    +            <magentoCLI command="config:set general/locale/timezone UTC" stepKey="setTimezoneUTC"/>
             </before>
             <after>
    +            <magentoCLI command="config:set general/locale/timezone America/Los_Angeles" stepKey="setTimezoneDefault"/>
                 <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearFilterAfter"/>
                 <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutAsDefaultAdmin"/>
                 <deleteData createDataKey="createFirstCustomer" stepKey="deleteFirstCustomer"/>
    
  • app/code/Magento/Marketplace/composer.json+9 7 modified
    @@ -1,19 +1,20 @@
     {
         "name": "magento/module-marketplace",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.3-p5",
         "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 @@
             }
         }
     }
    +
    
  • app/code/Magento/Marketplace/view/adminhtml/templates/index.phtml+3 3 modified
    @@ -32,8 +32,8 @@
                     <h2 class="page-sub-title"><?= $block->escapeHtml(__('Partner search')) ?></h2>
                     <p>
                         <?= $block->escapeHtml(__(
    -                        'Magento has a thriving ecosystem of technology partners to help merchants and brands deliver ' .
    -                        'the best possible customer experiences. They are recognized as experts in eCommerce, ' .
    +                        'Magento has a thriving ecosystem of technology partners to help merchants and brands deliver '
    +                        . 'the best possible customer experiences. They are recognized as experts in eCommerce, ' .
                             'search, email marketing, payments, tax, fraud, optimization and analytics, fulfillment, ' .
                             'and more. Visit the Magento Partner Directory to see all of our trusted partners.'
                         )); ?>
    @@ -61,7 +61,7 @@
                     )); ?>
                 </p>
                 <a class="action-secondary" target="_blank"
    -               href="https://marketplace.magento.com/">
    +               href="https://commercemarketplace.adobe.com/">
                     <?= $block->escapeHtml(__('Visit Magento Marketplaces')) ?>
                 </a>
             </div>
    
  • app/code/Magento/MediaContentApi/composer.json+7 5 modified
    @@ -1,16 +1,17 @@
     {
         "name": "magento/module-media-content-api",
         "description": "Magento module provides the API interfaces for managing relations between content and media files used in that content",
    -    "require": {
    -        "php": "~7.4.0||~8.1.0",
    -        "magento/module-media-gallery-api": "*",
    -        "magento/framework": "*"
    -    },
         "type": "magento2-module",
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "100.4.3",
    +    "require": {
    +        "php": "~7.4.0||~8.1.0",
    +        "magento/module-media-gallery-api": "101.0.*",
    +        "magento/framework": "103.0.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -20,3 +21,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/MediaContentCatalog/composer.json+10 8 modified
    @@ -1,19 +1,20 @@
     {
         "name": "magento/module-media-content-catalog",
         "description": "Magento module provides the implementation of MediaContent functionality for Magento_Catalog module",
    -    "require": {
    -        "php": "~7.4.0||~8.1.0",
    -        "magento/module-media-content-api": "*",
    -        "magento/module-catalog": "*",
    -        "magento/module-eav": "*",
    -        "magento/module-store": "*",
    -        "magento/framework": "*"
    -    },
         "type": "magento2-module",
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "100.4.3",
    +    "require": {
    +        "php": "~7.4.0||~8.1.0",
    +        "magento/module-media-content-api": "100.4.*",
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-eav": "102.1.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/framework": "103.0.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -23,3 +24,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/MediaContentCms/composer.json+8 6 modified
    @@ -1,17 +1,18 @@
     {
         "name": "magento/module-media-content-cms",
         "description": "Magento module provides the implementation of MediaContent functionality for Magento_Cms module",
    -    "require": {
    -        "php": "~7.4.0||~8.1.0",
    -        "magento/module-media-content-api": "*",
    -        "magento/module-cms": "*",
    -        "magento/framework": "*"
    -    },
         "type": "magento2-module",
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "100.4.3",
    +    "require": {
    +        "php": "~7.4.0||~8.1.0",
    +        "magento/module-media-content-api": "100.4.*",
    +        "magento/module-cms": "104.0.*",
    +        "magento/framework": "103.0.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -21,3 +22,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/MediaContent/composer.json+8 6 modified
    @@ -1,17 +1,18 @@
     {
         "name": "magento/module-media-content",
         "description": "Magento module provides the implementation for managing relations between content and media files used in that content",
    -    "require": {
    -        "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    -        "magento/module-media-content-api": "*",
    -        "magento/module-media-gallery-api": "*"
    -    },
         "type": "magento2-module",
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "100.4.3",
    +    "require": {
    +        "php": "~7.4.0||~8.1.0",
    +        "magento/framework": "103.0.*",
    +        "magento/module-media-content-api": "100.4.*",
    +        "magento/module-media-gallery-api": "101.0.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -21,3 +22,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/MediaContentSynchronizationApi/composer.json+7 5 modified
    @@ -1,16 +1,17 @@
     {
         "name": "magento/module-media-content-synchronization-api",
         "description": "Magento module responsible for the media content synchronization implementation API",
    -    "require": {
    -        "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    -        "magento/module-media-content-api": "*"
    -    },
         "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-media-content-api": "100.4.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -20,3 +21,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/MediaContentSynchronizationCatalog/composer.json+9 7 modified
    @@ -1,18 +1,19 @@
     {
         "name": "magento/module-media-content-synchronization-catalog",
         "description": "Magento module provides the implementation of MediaContentSynchronization functionality for Magento_Catalog module",
    -    "require": {
    -        "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    -        "magento/module-media-content-synchronization-api": "*",
    -        "magento/module-media-gallery-synchronization-api": "*",
    -        "magento/module-media-content-api": "*"
    -    },
         "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-media-content-synchronization-api": "100.4.*",
    +        "magento/module-media-gallery-synchronization-api": "100.4.*",
    +        "magento/module-media-content-api": "100.4.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -22,3 +23,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/MediaContentSynchronizationCms/composer.json+9 7 modified
    @@ -1,18 +1,19 @@
     {
         "name": "magento/module-media-content-synchronization-cms",
         "description": "Magento module provides the implementation of MediaContentSynchronization functionality for Magento_Cms module",
    -    "require": {
    -        "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    -        "magento/module-media-content-synchronization-api": "*",
    -        "magento/module-media-gallery-synchronization-api": "*",
    -        "magento/module-media-content-api": "*"
    -    },
         "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-media-content-synchronization-api": "100.4.*",
    +        "magento/module-media-gallery-synchronization-api": "100.4.*",
    +        "magento/module-media-content-api": "100.4.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -22,3 +23,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/MediaContentSynchronization/composer.json+13 11 modified
    @@ -1,22 +1,23 @@
     {
         "name": "magento/module-media-content-synchronization",
         "description": "Magento module provides implementation of the media content data synchronization.",
    -    "require": {
    -        "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    -        "magento/framework-bulk": "*",
    -        "magento/module-media-content-synchronization-api": "*",
    -        "magento/module-media-content-api": "*",
    -        "magento/module-asynchronous-operations": "*"
    -    },
    -    "suggest": {
    -        "magento/module-media-gallery-synchronization": "*"
    -    },
         "type": "magento2-module",
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "100.4.3",
    +    "require": {
    +        "php": "~7.4.0||~8.1.0",
    +        "magento/framework": "103.0.*",
    +        "magento/framework-bulk": "101.0.*",
    +        "magento/module-media-content-synchronization-api": "100.4.*",
    +        "magento/module-media-content-api": "100.4.*",
    +        "magento/module-asynchronous-operations": "100.4.*"
    +    },
    +    "suggest": {
    +        "magento/module-media-gallery-synchronization": "100.4.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -26,3 +27,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/MediaGalleryApi/composer.json+6 4 modified
    @@ -1,15 +1,16 @@
     {
         "name": "magento/module-media-gallery-api",
         "description": "Magento module responsible for media gallery asset attributes storage and management",
    -    "require": {
    -        "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*"
    -    },
         "type": "magento2-module",
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "101.0.3",
    +    "require": {
    +        "php": "~7.4.0||~8.1.0",
    +        "magento/framework": "103.0.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -19,3 +20,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/MediaGalleryCatalog/composer.json+8 6 modified
    @@ -1,17 +1,18 @@
     {
         "name": "magento/module-media-gallery-catalog",
         "description": "Magento module responsible for catalog gallery processor delete operation handling",
    -    "require": {
    -        "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    -        "magento/module-media-gallery-api": "*",
    -        "magento/module-catalog": "*"
    -    },
         "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-media-gallery-api": "101.0.*",
    +        "magento/module-catalog": "104.0.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -21,3 +22,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/MediaGalleryCatalogIntegration/composer.json+13 11 modified
    @@ -1,22 +1,23 @@
     {
         "name": "magento/module-media-gallery-catalog-integration",
         "description": "Magento module responsible for extending catalog image uploader functionality",
    -    "require": {
    -        "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    -        "magento/module-cms": "*",
    -        "magento/module-media-gallery-api": "*",
    -        "magento/module-media-gallery-synchronization-api": "*",
    -        "magento/module-media-gallery-ui-api": "*"
    -    },
    -    "suggest": {
    -        "magento/module-catalog": "*"
    -    },
         "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-cms": "104.0.*",
    +        "magento/module-media-gallery-api": "101.0.*",
    +        "magento/module-media-gallery-synchronization-api": "100.4.*",
    +        "magento/module-media-gallery-ui-api": "100.4.*"
    +    },
    +    "suggest": {
    +        "magento/module-catalog": "104.0.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -26,3 +27,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/MediaGalleryCatalogUi/composer.json+11 9 modified
    @@ -1,20 +1,21 @@
     {
         "name": "magento/module-media-gallery-catalog-ui",
         "description": "Magento module that implement category grid for media gallery.",
    -    "require": {
    -        "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    -        "magento/module-cms": "*",
    -        "magento/module-backend": "*",
    -        "magento/module-catalog": "*",
    -        "magento/module-store": "*",
    -        "magento/module-ui": "*"
    -    },
         "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-cms": "104.0.*",
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/module-ui": "101.2.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -24,3 +25,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/MediaGalleryCmsUi/composer.json+8 6 modified
    @@ -1,17 +1,18 @@
     {
         "name": "magento/module-media-gallery-cms-ui",
         "description": "Cms related UI elements in the magento media gallery",
    -    "require": {
    -        "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    -        "magento/module-cms": "*",
    -        "magento/module-backend": "*"
    -    },
         "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-cms": "104.0.*",
    +        "magento/module-backend": "102.0.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -21,3 +22,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/MediaGallery/composer.json+8 6 modified
    @@ -1,17 +1,18 @@
     {
         "name": "magento/module-media-gallery",
         "description": "Magento module responsible for media handling",
    -    "require": {
    -        "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    -        "magento/module-media-gallery-api": "*",
    -        "magento/module-cms": "*"
    -    },
         "type": "magento2-module",
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "100.4.3",
    +    "require": {
    +        "php": "~7.4.0||~8.1.0",
    +        "magento/framework": "103.0.*",
    +        "magento/module-media-gallery-api": "101.0.*",
    +        "magento/module-cms": "104.0.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -21,3 +22,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/MediaGalleryIntegration/composer.json+17 15 modified
    @@ -1,32 +1,34 @@
     {
         "name": "magento/module-media-gallery-integration",
         "description": "Magento module responsible for integration of enhanced media gallery",
    -    "require": {
    -        "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    -        "magento/module-media-gallery-ui-api": "*",
    -        "magento/module-media-gallery-api": "*",
    -        "magento/module-media-gallery-synchronization-api": "*",
    -        "magento/module-ui": "*"
    -    },
    -    "require-dev": {
    -        "magento/module-cms": "*"
    -    },
    -    "suggest": {
    -        "magento/module-catalog": "*",
    -        "magento/module-cms": "*"
    -    },
         "type": "magento2-module",
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "100.4.3",
    +    "require": {
    +        "php": "~7.4.0||~8.1.0",
    +        "magento/framework": "103.0.*",
    +        "magento/module-media-gallery-ui-api": "100.4.*",
    +        "magento/module-media-gallery-api": "101.0.*",
    +        "magento/module-media-gallery-synchronization-api": "100.4.*",
    +        "magento/module-ui": "101.2.*"
    +    },
    +    "suggest": {
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-cms": "104.0.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
             ],
             "psr-4": {
                 "Magento\\MediaGalleryIntegration\\": ""
             }
    +    },
    +    "require-dev": {
    +        "magento/module-cms": "*"
         }
     }
    +
    
  • app/code/Magento/MediaGalleryMetadataApi/composer.json+6 4 modified
    @@ -1,15 +1,16 @@
     {
         "name": "magento/module-media-gallery-metadata-api",
         "description": "Magento module responsible for media gallery metadata implementation API",
    -    "require": {
    -        "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*"
    -    },
         "type": "magento2-module",
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "100.4.1",
    +    "require": {
    +        "php": "~7.4.0||~8.1.0",
    +        "magento/framework": "103.0.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -19,3 +20,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/MediaGalleryMetadata/composer.json+7 5 modified
    @@ -1,16 +1,17 @@
     {
         "name": "magento/module-media-gallery-metadata",
         "description": "Magento module responsible for images metadata processing",
    -    "require": {
    -        "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    -        "magento/module-media-gallery-metadata-api": "*"
    -    },
         "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-media-gallery-metadata-api": "100.4.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -20,3 +21,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/MediaGalleryRenditionsApi/composer.json+6 4 modified
    @@ -1,15 +1,16 @@
     {
         "name": "magento/module-media-gallery-renditions-api",
         "description": "Magento module that is responsible for the API implementation of Media Gallery Renditions.",
    -    "require": {
    -        "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*"
    -    },
         "type": "magento2-module",
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "100.4.1",
    +    "require": {
    +        "php": "~7.4.0||~8.1.0",
    +        "magento/framework": "103.0.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -19,3 +20,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/MediaGalleryRenditions/composer.json+13 11 modified
    @@ -1,22 +1,23 @@
     {
         "name": "magento/module-media-gallery-renditions",
         "description": "Magento module that implements height and width fields for for media gallery items.",
    -    "require": {
    -        "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    -        "magento/module-media-gallery-renditions-api": "*",
    -        "magento/module-media-gallery-api": "*",
    -        "magento/framework-message-queue": "*",
    -        "magento/module-cms": "*"
    -    },
    -    "suggest": {
    -        "magento/module-media-content-api": "*"
    -    },
         "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-media-gallery-renditions-api": "100.4.*",
    +        "magento/module-media-gallery-api": "101.0.*",
    +        "magento/framework-message-queue": "100.4.*",
    +        "magento/module-cms": "104.0.*"
    +    },
    +    "suggest": {
    +        "magento/module-media-content-api": "100.4.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -26,3 +27,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/MediaGallerySynchronizationApi/composer.json+7 5 modified
    @@ -1,16 +1,17 @@
     {
         "name": "magento/module-media-gallery-synchronization-api",
         "description": "Magento module responsible for the media gallery synchronization implementation API",
    -    "require": {
    -        "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    -        "magento/module-media-gallery-api": "*"
    -    },
         "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-media-gallery-api": "101.0.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -20,3 +21,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/MediaGallerySynchronization/composer.json+9 7 modified
    @@ -1,18 +1,19 @@
     {
         "name": "magento/module-media-gallery-synchronization",
         "description": "Magento module provides implementation of the media gallery data synchronization.",
    -    "require": {
    -        "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    -        "magento/module-media-gallery-api": "*",
    -        "magento/module-media-gallery-synchronization-api": "*",
    -        "magento/framework-message-queue": "*"
    -    },
         "type": "magento2-module",
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "100.4.3",
    +    "require": {
    +        "php": "~7.4.0||~8.1.0",
    +        "magento/framework": "103.0.*",
    +        "magento/module-media-gallery-api": "101.0.*",
    +        "magento/module-media-gallery-synchronization-api": "100.4.*",
    +        "magento/framework-message-queue": "100.4.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -22,3 +23,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/MediaGallerySynchronizationMetadata/composer.json+9 7 modified
    @@ -1,18 +1,19 @@
     {
         "name": "magento/module-media-gallery-synchronization-metadata",
         "description": "Magento module responsible for images metadata synchronization",
    -    "require": {
    -        "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    -        "magento/module-media-gallery-api": "*",
    -        "magento/module-media-gallery-metadata-api": "*",
    -        "magento/module-media-gallery-synchronization-api": "*"
    -    },
         "type": "magento2-module",
         "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-media-gallery-api": "101.0.*",
    +        "magento/module-media-gallery-metadata-api": "100.4.*",
    +        "magento/module-media-gallery-synchronization-api": "100.4.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -22,3 +23,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/MediaGalleryUiApi/composer.json+9 7 modified
    @@ -1,18 +1,19 @@
     {
         "name": "magento/module-media-gallery-ui-api",
         "description": "Magento module responsible for the media gallery UI implementation API",
    -    "require": {
    -        "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*"
    -    },
    -    "suggest": {
    -        "magento/module-cms": "*"
    -    },
         "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.*"
    +    },
    +    "suggest": {
    +        "magento/module-cms": "104.0.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -22,3 +23,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/MediaGalleryUi/composer.json+17 15 modified
    @@ -1,26 +1,27 @@
     {
         "name": "magento/module-media-gallery-ui",
         "description": "Magento module responsible for the media gallery UI implementation",
    -    "require": {
    -        "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    -        "magento/module-backend": "*",
    -        "magento/module-ui": "*",
    -        "magento/module-store": "*",
    -        "magento/module-media-gallery-ui-api": "*",
    -        "magento/module-media-gallery-api": "*",
    -        "magento/module-media-gallery-metadata-api": "*",
    -        "magento/module-media-gallery-synchronization-api": "*",
    -        "magento/module-media-content-api": "*",
    -        "magento/module-cms": "*",
    -        "magento/module-directory": "*",
    -        "magento/module-authorization": "*"
    -    },
         "type": "magento2-module",
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "100.4.3",
    +    "require": {
    +        "php": "~7.4.0||~8.1.0",
    +        "magento/framework": "103.0.*",
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-ui": "101.2.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/module-media-gallery-ui-api": "100.4.*",
    +        "magento/module-media-gallery-api": "101.0.*",
    +        "magento/module-media-gallery-metadata-api": "100.4.*",
    +        "magento/module-media-gallery-synchronization-api": "100.4.*",
    +        "magento/module-media-content-api": "100.4.*",
    +        "magento/module-cms": "104.0.*",
    +        "magento/module-directory": "100.4.*",
    +        "magento/module-authorization": "100.4.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -30,3 +31,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/MediaStorage/composer.json+16 14 modified
    @@ -1,26 +1,27 @@
     {
         "name": "magento/module-media-storage",
         "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-bulk": "*",
    -        "magento/module-backend": "*",
    -        "magento/module-config": "*",
    -        "magento/module-store": "*",
    -        "magento/module-catalog": "*",
    -        "magento/module-theme": "*",
    -        "magento/module-asynchronous-operations": "*",
    -        "magento/module-authorization": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/framework-bulk": "101.0.*",
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-config": "101.2.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-theme": "101.1.*",
    +        "magento/module-asynchronous-operations": "100.4.*",
    +        "magento/module-authorization": "100.4.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -30,3 +31,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/MessageQueue/composer.json+9 7 modified
    @@ -1,20 +1,21 @@
     {
         "name": "magento/module-message-queue",
         "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": "103.0.*",
    +        "magento/framework-message-queue": "100.4.*",
             "magento/magento-composer-installer": "*",
             "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 @@
             }
         }
     }
    +
    
  • app/code/Magento/Msrp/composer.json+15 13 modified
    @@ -1,27 +1,28 @@
     {
         "name": "magento/module-msrp",
         "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-catalog": "*",
    -        "magento/module-downloadable": "*",
    -        "magento/module-eav": "*",
    -        "magento/module-store": "*",
    -        "magento/module-tax": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-downloadable": "100.4.*",
    +        "magento/module-eav": "102.1.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/module-tax": "100.4.*"
         },
         "suggest": {
    -        "magento/module-bundle": "*",
    -        "magento/module-msrp-sample-data": "*"
    +        "magento/module-bundle": "101.0.*",
    +        "magento/module-msrp-sample-data": "Sample Data version: 100.4.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -31,3 +32,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/MsrpConfigurableProduct/composer.json+11 9 modified
    @@ -1,21 +1,22 @@
     {
         "name": "magento/module-msrp-configurable-product",
         "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-msrp": "*",
    -        "magento/module-configurable-product": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-msrp": "100.4.*",
    +        "magento/module-configurable-product": "100.4.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -25,3 +26,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/MsrpGroupedProduct/composer.json+11 9 modified
    @@ -1,21 +1,22 @@
     {
         "name": "magento/module-msrp-grouped-product",
         "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-msrp": "*",
    -        "magento/module-grouped-product": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-msrp": "100.4.*",
    +        "magento/module-grouped-product": "100.4.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -25,3 +26,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Multishipping/composer.json+18 16 modified
    @@ -1,28 +1,29 @@
     {
         "name": "magento/module-multishipping",
         "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-checkout": "*",
    -        "magento/module-customer": "*",
    -        "magento/module-directory": "*",
    -        "magento/module-payment": "*",
    -        "magento/module-quote": "*",
    -        "magento/module-sales": "*",
    -        "magento/module-store": "*",
    -        "magento/module-tax": "*",
    -        "magento/module-theme": "*",
    -        "magento/module-captcha": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-checkout": "100.4.*",
    +        "magento/module-customer": "103.0.*",
    +        "magento/module-directory": "100.4.*",
    +        "magento/module-payment": "100.4.*",
    +        "magento/module-quote": "101.2.*",
    +        "magento/module-sales": "103.0.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/module-tax": "100.4.*",
    +        "magento/module-theme": "101.1.*",
    +        "magento/module-captcha": "100.4.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -32,3 +33,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/MysqlMq/composer.json+10 8 modified
    @@ -1,21 +1,22 @@
     {
         "name": "magento/module-mysql-mq",
         "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-message-queue": "*",
    +        "magento/framework": "103.0.*",
    +        "magento/framework-message-queue": "100.4.*",
             "magento/magento-composer-installer": "*",
    -        "magento/module-store": "*",
    +        "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"
    @@ -25,3 +26,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/NewRelicReporting/composer.json+14 12 modified
    @@ -1,25 +1,26 @@
     {
         "name": "magento/module-new-relic-reporting",
         "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/framework": "103.0.*",
             "magento/magento-composer-installer": "*",
    -        "magento/module-backend": "*",
    -        "magento/module-catalog": "*",
    -        "magento/module-config": "*",
    -        "magento/module-configurable-product": "*",
    -        "magento/module-customer": "*",
    -        "magento/module-store": "*"
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-config": "101.2.*",
    +        "magento/module-configurable-product": "100.4.*",
    +        "magento/module-customer": "103.0.*",
    +        "magento/module-store": "101.1.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -29,3 +30,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Newsletter/composer.json+17 15 modified
    @@ -1,27 +1,28 @@
     {
         "name": "magento/module-newsletter",
         "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-cms": "*",
    -        "magento/module-customer": "*",
    -        "magento/module-eav": "*",
    -        "magento/module-email": "*",
    -        "magento/module-require-js": "*",
    -        "magento/module-store": "*",
    -        "magento/module-widget": "*",
    -        "magento/module-ui": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-cms": "104.0.*",
    +        "magento/module-customer": "103.0.*",
    +        "magento/module-eav": "102.1.*",
    +        "magento/module-email": "101.1.*",
    +        "magento/module-require-js": "100.4.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/module-widget": "101.2.*",
    +        "magento/module-ui": "101.2.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -31,3 +32,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/NewsletterGraphQl/composer.json+12 10 modified
    @@ -1,24 +1,25 @@
     {
         "name": "magento/module-newsletter-graph-ql",
         "description": "Provides GraphQl functionality for the newsletter subscriptions.",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    -    "type": "magento2-module",
    +    "version": "100.4.1",
         "require": {
             "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    -        "magento/module-customer": "*",
    -        "magento/module-newsletter": "*",
    -        "magento/module-store": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-customer": "103.0.*",
    +        "magento/module-newsletter": "100.4.*",
    +        "magento/module-store": "101.1.*"
         },
         "suggest": {
    -        "magento/module-graph-ql": "*"
    +        "magento/module-graph-ql": "100.4.*"
         },
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -28,3 +29,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Newsletter/Test/Mftf/ActionGroup/AdminMarketingDeleteNewsletterTemplateActionGroup.xml+2 0 modified
    @@ -10,7 +10,9 @@
                   xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd">
         <!--Delete Newsletter Template -->
         <actionGroup name="AdminMarketingDeleteNewsletterTemplateActionGroup">
    +        <waitForElementVisible selector="{{AdminNewsletterMainActionsSection.deleteTemplateButton}}" stepKey="waitForDeleteElementButtonToBeVisible"/>
             <click stepKey="clickDeleteButton" selector="{{AdminNewsletterMainActionsSection.deleteTemplateButton}}"/>
    +        <waitForElementVisible selector="{{AdminNewsletterMainActionsSection.confirmDelete}}" stepKey="waitForConfirmPopup"/>
             <click stepKey="confirmDelete" selector="{{AdminNewsletterMainActionsSection.confirmDelete}}"/>
             <waitForPageLoad stepKey="waitForPageLoading"/>
         </actionGroup>
    
  • app/code/Magento/Newsletter/Test/Mftf/ActionGroup/AdminMarketingOpenNewsletterTemplateFromGridActionGroup.xml+2 0 modified
    @@ -9,6 +9,8 @@
     <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                   xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd">
         <actionGroup name="AdminMarketingOpenNewsletterTemplateFromGridActionGroup">
    +        <waitForElementVisible selector="{{AdminNewsletterGridMainActionsSection.searchResultFirstRow}}" stepKey="waitForElementToBeVisible"/>
             <click stepKey="openTemplate" selector="{{AdminNewsletterGridMainActionsSection.searchResultFirstRow}}"/>
    +        <waitForPageLoad stepKey="waitForPageLoadAfterClick"/>
         </actionGroup>
     </actionGroups>
    
  • app/code/Magento/OfflinePayments/composer.json+12 10 modified
    @@ -1,24 +1,25 @@
     {
         "name": "magento/module-offline-payments",
         "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-checkout": "*",
    -        "magento/module-payment": "*",
    -        "magento/module-quote": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-checkout": "100.4.*",
    +        "magento/module-payment": "100.4.*",
    +        "magento/module-quote": "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"
    @@ -28,3 +29,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/OfflineShipping/composer.json+19 17 modified
    @@ -1,31 +1,32 @@
     {
         "name": "magento/module-offline-shipping",
         "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-backend": "*",
    -        "magento/module-catalog": "*",
    -        "magento/module-config": "*",
    -        "magento/module-directory": "*",
    -        "magento/module-quote": "*",
    -        "magento/module-sales": "*",
    -        "magento/module-sales-rule": "*",
    -        "magento/module-shipping": "*",
    -        "magento/module-store": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-config": "101.2.*",
    +        "magento/module-directory": "100.4.*",
    +        "magento/module-quote": "101.2.*",
    +        "magento/module-sales": "103.0.*",
    +        "magento/module-sales-rule": "101.2.*",
    +        "magento/module-shipping": "100.4.*",
    +        "magento/module-store": "101.1.*"
         },
         "suggest": {
    -        "magento/module-checkout": "*",
    -        "magento/module-offline-shipping-sample-data": "*"
    +        "magento/module-checkout": "100.4.*",
    +        "magento/module-offline-shipping-sample-data": "Sample Data version: 100.4.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -35,3 +36,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/PageCache/composer.json+11 9 modified
    @@ -1,21 +1,22 @@
     {
         "name": "magento/module-page-cache",
         "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-config": "*",
    -        "magento/module-store": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-config": "101.2.*",
    +        "magento/module-store": "101.1.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -25,3 +26,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/PageCache/Controller/Block.php+73 13 modified
    @@ -1,20 +1,26 @@
     <?php
     /**
    - * PageCache controller
      *
      * Copyright © Magento, Inc. All rights reserved.
      * See COPYING.txt for license details.
      */
    +declare(strict_types=1);
    +
     namespace Magento\PageCache\Controller;
     
    +use Magento\Framework\App\Action\Context;
     use Magento\Framework\Serialize\Serializer\Base64Json;
     use Magento\Framework\Serialize\Serializer\Json;
    +use Magento\Framework\Translate\InlineInterface;
    +use Magento\Framework\Validator\RegexFactory;
    +use Magento\Framework\App\ObjectManager;
     use Magento\Framework\View\Layout\LayoutCacheKeyInterface;
    +use Magento\Framework\App\Config\ScopeConfigInterface;
     
     abstract class Block extends \Magento\Framework\App\Action\Action
     {
         /**
    -     * @var \Magento\Framework\Translate\InlineInterface
    +     * @var InlineInterface
          */
         protected $translateInline;
     
    @@ -41,27 +47,54 @@ abstract class Block extends \Magento\Framework\App\Action\Action
         private $layoutCacheKeyName = 'mage_pagecache';
     
         /**
    -     * @param \Magento\Framework\App\Action\Context $context
    -     * @param \Magento\Framework\Translate\InlineInterface $translateInline
    -     * @param Json $jsonSerializer
    -     * @param Base64Json $base64jsonSerializer
    -     * @param LayoutCacheKeyInterface $layoutCacheKey
    +     * @var RegexFactory
    +     */
    +    private RegexFactory $regexValidatorFactory;
    +
    +    /**
    +     * Validation pattern for handles array
    +     */
    +    private const VALIDATION_RULE_PATTERN = '/^[a-z0-9]+[a-z0-9_]*$/i';
    +
    +    /**
    +     * @var ScopeConfigInterface
    +     */
    +    private $config;
    +
    +    /**
    +     * Handle size system name
    +     */
    +    private const XML_HANDLES_SIZE = 'system/full_page_cache/handles_size';
    +
    +    /**
    +     * @param Context $context
    +     * @param InlineInterface $translateInline
    +     * @param Json|null $jsonSerializer
    +     * @param Base64Json|null $base64jsonSerializer
    +     * @param LayoutCacheKeyInterface|null $layoutCacheKey
    +     * @param RegexFactory|null $regexValidatorFactory
    +     * @param ScopeConfigInterface|null $scopeConfig
          */
         public function __construct(
    -        \Magento\Framework\App\Action\Context $context,
    -        \Magento\Framework\Translate\InlineInterface $translateInline,
    +        Context $context,
    +        InlineInterface $translateInline,
             Json $jsonSerializer = null,
             Base64Json $base64jsonSerializer = null,
    -        LayoutCacheKeyInterface $layoutCacheKey = null
    +        LayoutCacheKeyInterface $layoutCacheKey = null,
    +        ?RegexFactory $regexValidatorFactory = null,
    +        ScopeConfigInterface $scopeConfig = null
         ) {
             parent::__construct($context);
             $this->translateInline = $translateInline;
             $this->jsonSerializer = $jsonSerializer
    -            ?: \Magento\Framework\App\ObjectManager::getInstance()->get(Json::class);
    +            ?: ObjectManager::getInstance()->get(Json::class);
             $this->base64jsonSerializer = $base64jsonSerializer
    -            ?: \Magento\Framework\App\ObjectManager::getInstance()->get(Base64Json::class);
    +            ?: ObjectManager::getInstance()->get(Base64Json::class);
             $this->layoutCacheKey = $layoutCacheKey
    -            ?: \Magento\Framework\App\ObjectManager::getInstance()->get(LayoutCacheKeyInterface::class);
    +            ?: ObjectManager::getInstance()->get(LayoutCacheKeyInterface::class);
    +        $this->regexValidatorFactory = $regexValidatorFactory
    +            ?: ObjectManager::getInstance()->get(RegexFactory::class);
    +        $this->config = $scopeConfig;
         }
     
         /**
    @@ -79,10 +112,19 @@ protected function _getBlocks()
             }
             $blocks = $this->jsonSerializer->unserialize($blocks);
             $handles = $this->base64jsonSerializer->unserialize($handles);
    +        $handlesSize = (int)$this->config->getValue(self::XML_HANDLES_SIZE);
    +        $handles = ($handlesSize && count($handles) > $handlesSize)
    +            ? array_splice($handles, 0, $handlesSize) : $handles;
    +
    +        if (!$this->validateHandleParam($handles)) {
    +            return [];
    +        }
     
             $layout = $this->_view->getLayout();
             $this->layoutCacheKey->addCacheKeys($this->layoutCacheKeyName);
     
    +        // loadLayout() is using actionHandlers flag which doesn't exist in Framework\View\Layout\Builder::build
    +        // phpcs:ignore
             $this->_view->loadLayout($handles, true, true, false);
             $data = [];
     
    @@ -95,4 +137,22 @@ protected function _getBlocks()
     
             return $data;
         }
    +
    +    /**
    +     * Validates handles parameter
    +     *
    +     * @param array $handles
    +     * @return bool
    +     */
    +    private function validateHandleParam($handles): bool
    +    {
    +        $validator = $this->regexValidatorFactory->create(['pattern' => self::VALIDATION_RULE_PATTERN]);
    +        foreach ($handles as $handle) {
    +            if ($handle && !$validator->isValid($handle)) {
    +                return false;
    +            }
    +        }
    +
    +        return true;
    +    }
     }
    
  • app/code/Magento/PageCache/etc/adminhtml/system.xml+4 0 modified
    @@ -78,6 +78,10 @@
                         <comment>Public content cache lifetime in seconds. If field is empty default value 86400 will be saved. </comment>
                         <backend_model>Magento\PageCache\Model\System\Config\Backend\Ttl</backend_model>
                     </field>
    +                <field id="handles_size" type="text" translate="label comment" sortOrder="5" showInDefault="1" canRestore="1">
    +                    <label>Handles params size</label>
    +                    <comment>Handles params size. For better performance use handles parameter size between 50 and 100. </comment>
    +                </field>
                 </group>
             </section>
         </system>
    
  • app/code/Magento/PageCache/etc/config.xml+1 0 modified
    @@ -24,6 +24,7 @@
                         <path>varnish4.vcl</path>
                     </varnish4>
                     <ttl>86400</ttl>
    +                <handles_size>100</handles_size>
                     <caching_application>1</caching_application>
                     <default>
                         <access_list>localhost</access_list>
    
  • app/code/Magento/PageCache/etc/di.xml+1 0 modified
    @@ -35,6 +35,7 @@
         <type name="Magento\PageCache\Controller\Block">
             <arguments>
                 <argument name="layoutCacheKey" xsi:type="object">Magento\Framework\View\Layout\LayoutCacheKeyInterface</argument>
    +            <argument name="scopeConfig" xsi:type="object">Magento\Framework\App\Config\ScopeConfigInterface\Proxy</argument>
             </arguments>
         </type>
         <type name="Magento\Framework\App\Cache\RuntimeStaleCacheStateModifier">
    
  • app/code/Magento/PageCache/Test/Unit/Controller/Block/EsiTest.php+19 1 modified
    @@ -18,6 +18,8 @@
     use Magento\Framework\View\Element\AbstractBlock;
     use Magento\Framework\View\Layout;
     use Magento\Framework\View\Layout\LayoutCacheKeyInterface;
    +use Magento\Framework\Validator\Regex;
    +use Magento\Framework\Validator\RegexFactory;
     use Magento\PageCache\Controller\Block;
     use Magento\PageCache\Controller\Block\Esi;
     use Magento\PageCache\Test\Unit\Block\Controller\StubBlock;
    @@ -64,6 +66,11 @@ class EsiTest extends TestCase
          */
         protected $translateInline;
     
    +    /**
    +     * Validation pattern for handles array
    +     */
    +    private const VALIDATION_RULE_PATTERN = '/^[a-z0-9]+[a-z0-9_]*$/i';
    +
         /**
          * Set up before test
          */
    @@ -98,6 +105,16 @@ protected function setUp(): void
     
             $this->translateInline = $this->getMockForAbstractClass(InlineInterface::class);
     
    +        $regexFactoryMock = $this->getMockBuilder(RegexFactory::class)
    +            ->disableOriginalConstructor()
    +            ->setMethods(['create'])
    +            ->getMock();
    +
    +        $regexObject = new Regex(self::VALIDATION_RULE_PATTERN);
    +
    +        $regexFactoryMock->expects($this->any())->method('create')
    +            ->willReturn($regexObject);
    +
             $helperObjectManager = new ObjectManager($this);
             $this->action = $helperObjectManager->getObject(
                 Esi::class,
    @@ -106,7 +123,8 @@ protected function setUp(): void
                     'translateInline' => $this->translateInline,
                     'jsonSerializer' => new Json(),
                     'base64jsonSerializer' => new Base64Json(),
    -                'layoutCacheKey' => $this->layoutCacheKeyMock
    +                'layoutCacheKey' => $this->layoutCacheKeyMock,
    +                'regexValidatorFactory' => $regexFactoryMock
                 ]
             );
         }
    
  • app/code/Magento/PageCache/Test/Unit/Controller/Block/RenderTest.php+19 1 modified
    @@ -18,6 +18,8 @@
     use Magento\Framework\View\Layout;
     use Magento\Framework\View\Layout\LayoutCacheKeyInterface;
     use Magento\Framework\View\Layout\ProcessorInterface;
    +use Magento\Framework\Validator\Regex;
    +use Magento\Framework\Validator\RegexFactory;
     use Magento\PageCache\Controller\Block;
     use Magento\PageCache\Controller\Block\Render;
     use Magento\PageCache\Test\Unit\Block\Controller\StubBlock;
    @@ -69,6 +71,11 @@ class RenderTest extends TestCase
          */
         protected $layoutCacheKeyMock;
     
    +    /**
    +     * Validation pattern for handles array
    +     */
    +    private const VALIDATION_RULE_PATTERN = '/^[a-z0-9]+[a-z0-9_]*$/i';
    +
         /**
          * @inheritDoc
          */
    @@ -111,6 +118,16 @@ protected function setUp(): void
     
             $this->translateInline = $this->getMockForAbstractClass(InlineInterface::class);
     
    +        $regexFactoryMock = $this->getMockBuilder(RegexFactory::class)
    +            ->disableOriginalConstructor()
    +            ->setMethods(['create'])
    +            ->getMock();
    +
    +        $regexObject = new Regex(self::VALIDATION_RULE_PATTERN);
    +
    +        $regexFactoryMock->expects($this->any())->method('create')
    +            ->willReturn($regexObject);
    +
             $helperObjectManager = new ObjectManager($this);
             $this->action = $helperObjectManager->getObject(
                 Render::class,
    @@ -119,7 +136,8 @@ protected function setUp(): void
                     'translateInline' => $this->translateInline,
                     'jsonSerializer' => new Json(),
                     'base64jsonSerializer' => new Base64Json(),
    -                'layoutCacheKey' => $this->layoutCacheKeyMock
    +                'layoutCacheKey' => $this->layoutCacheKeyMock,
    +                'regexValidatorFactory' => $regexFactoryMock
                 ]
             );
         }
    
  • app/code/Magento/Payment/composer.json+15 13 modified
    @@ -1,25 +1,26 @@
     {
         "name": "magento/module-payment",
         "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-checkout": "*",
    -        "magento/module-config": "*",
    -        "magento/module-directory": "*",
    -        "magento/module-quote": "*",
    -        "magento/module-sales": "*",
    -        "magento/module-store": "*",
    -        "magento/module-ui": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-checkout": "100.4.*",
    +        "magento/module-config": "101.2.*",
    +        "magento/module-directory": "100.4.*",
    +        "magento/module-quote": "101.2.*",
    +        "magento/module-sales": "103.0.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/module-ui": "101.2.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -29,3 +30,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/PaymentGraphQl/composer.json+10 8 modified
    @@ -2,19 +2,20 @@
         "name": "magento/module-payment-graph-ql",
         "description": "N/A",
         "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
    +    "version": "100.4.0",
         "require": {
             "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    -        "magento/module-payment": "*",
    -        "magento/module-graph-ql": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-payment": "100.4.*",
    +        "magento/module-graph-ql": "100.4.*"
         },
         "suggest": {
    -        "magento/module-store-graph-ql": "*"
    +        "magento/module-store-graph-ql": "100.4.*"
         },
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -24,3 +25,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Paypal/Block/PayLater/Banner.php+2 1 modified
    @@ -97,7 +97,8 @@ public function getJsLayout()
             $config['displayAmount'] = !$displayAmount || $this->payLaterConfig->isPPBillingAgreementEnabled()
                 ? false : true;
             $config['dataAttributes'] = [
    -            'data-partner-attribution-id' => $this->paypalConfig->getBuildNotationCode()
    +            'data-partner-attribution-id' => $this->paypalConfig->getBuildNotationCode(),
    +            'data-csp-nonce' => $this->paypalConfig->getCspNonce(),
             ];
     
             //Extend block component attributes with defaults
    
  • app/code/Magento/Paypal/Block/PayLater/LayoutProcessor.php+2 1 modified
    @@ -88,7 +88,8 @@ public function process($jsLayout)
                 $config['displayAmount'] = !$displayAmount || $this->payLaterConfig->isPPBillingAgreementEnabled()
                     ? false : true;
                 $config['dataAttributes'] = [
    -                'data-partner-attribution-id' => $this->paypalConfig->getBuildNotationCode()
    +                'data-partner-attribution-id' => $this->paypalConfig->getBuildNotationCode(),
    +                'data-csp-nonce' => $this->paypalConfig->getCspNonce(),
                 ];
     
                 $attributes = $this->payLaterConfig->getSectionConfig(
    
  • app/code/Magento/PaypalCaptcha/composer.json+12 10 modified
    @@ -1,24 +1,25 @@
     {
         "name": "magento/module-paypal-captcha",
         "description": "Provides CAPTCHA validation for PayPal Payflow Pro",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.2-p7",
         "require": {
             "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    -        "magento/module-captcha": "*",
    -        "magento/module-checkout": "*",
    -        "magento/module-store": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-captcha": "100.4.*",
    +        "magento/module-checkout": "100.4.*",
    +        "magento/module-store": "101.1.*"
         },
         "suggest": {
    -        "magento/module-paypal": "*"
    +        "magento/module-paypal": "101.0.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -28,3 +29,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/PaypalCaptcha/Test/Mftf/Test/StorefrontPaymentsCaptchaWithPayflowProTest.xml+2 0 modified
    @@ -18,6 +18,8 @@
                 <useCaseId value="MC-41572"/>
                 <severity value="AVERAGE"/>
                 <group value="captcha"/>
    +            <group value="3rd_party_integration" />
    +            <group value="pr_exclude" />
             </annotations>
             <before>
                 <!-- Configure Paypal Payflow Pro payment method -->
    
  • app/code/Magento/Paypal/composer.json+27 24 modified
    @@ -1,39 +1,41 @@
     {
         "name": "magento/module-paypal",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "101.0.4-p9",
         "require": {
             "php": "~7.4.0||~8.1.0",
             "lib-libxml": "*",
    -        "magento/framework": "*",
    -        "magento/module-authorization": "*",
    -        "magento/module-backend": "*",
    -        "magento/module-catalog": "*",
    -        "magento/module-checkout": "*",
    -        "magento/module-config": "*",
    -        "magento/module-customer": "*",
    -        "magento/module-directory": "*",
    -        "magento/module-eav": "*",
    -        "magento/module-instant-purchase": "*",
    -        "magento/module-payment": "*",
    -        "magento/module-quote": "*",
    -        "magento/module-sales": "*",
    -        "magento/module-store": "*",
    -        "magento/module-tax": "*",
    -        "magento/module-theme": "*",
    -        "magento/module-ui": "*",
    -        "magento/module-vault": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-authorization": "100.4.*",
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-checkout": "100.4.*",
    +        "magento/module-config": "101.2.*",
    +        "magento/module-customer": "103.0.*",
    +        "magento/module-directory": "100.4.*",
    +        "magento/module-eav": "102.1.*",
    +        "magento/module-instant-purchase": "100.4.*",
    +        "magento/module-payment": "100.4.*",
    +        "magento/module-quote": "101.2.*",
    +        "magento/module-sales": "103.0.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/module-tax": "100.4.*",
    +        "magento/module-theme": "101.1.*",
    +        "magento/module-ui": "101.2.*",
    +        "magento/module-vault": "101.2.*",
    +        "magento/module-csp": "100.4.*"
         },
         "suggest": {
    -        "magento/module-checkout-agreements": "*"
    +        "magento/module-checkout-agreements": "100.4.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -43,3 +45,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/PaypalGraphQl/composer.json+18 16 modified
    @@ -1,30 +1,31 @@
     {
         "name": "magento/module-paypal-graph-ql",
         "description": "GraphQl support for Paypal",
    +    "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-quote": "*",
    -        "magento/module-checkout": "*",
    -        "magento/module-paypal": "*",
    -        "magento/module-quote-graph-ql": "*",
    -        "magento/module-sales": "*",
    -        "magento/module-payment": "*",
    -        "magento/module-store": "*",
    -        "magento/module-vault": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-quote": "101.2.*",
    +        "magento/module-checkout": "100.4.*",
    +        "magento/module-paypal": "101.0.*",
    +        "magento/module-quote-graph-ql": "100.4.*",
    +        "magento/module-sales": "103.0.*",
    +        "magento/module-payment": "100.4.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/module-vault": "101.2.*"
         },
         "suggest": {
    -        "magento/module-graph-ql": "*",
    -        "magento/module-store-graph-ql": "*"
    +        "magento/module-graph-ql": "100.4.*",
    +        "magento/module-store-graph-ql": "100.4.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -34,3 +35,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Paypal/Model/Config.php+66 43 modified
    @@ -6,6 +6,8 @@
     
     namespace Magento\Paypal\Model;
     
    +use Magento\Csp\Helper\CspNonceProvider;
    +use Magento\Framework\App\ObjectManager;
     use Magento\Payment\Helper\Formatter;
     
     /**
    @@ -14,6 +16,7 @@
      * Works with PayPal-specific system configuration
      * @SuppressWarnings(PHPMD.ExcessivePublicCount)
      * @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
    + * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
      */
     class Config extends AbstractConfig
     {
    @@ -23,160 +26,160 @@ class Config extends AbstractConfig
         /**
          * PayPal Express
          */
    -    const METHOD_EXPRESS = 'paypal_express';
    +    public const METHOD_EXPRESS = 'paypal_express';
     
         /**
          * PayPal Standard - alias METHOD_WPP_EXPRESS
          */
    -    const METHOD_WPS_EXPRESS = 'wps_express';
    +    public const METHOD_WPS_EXPRESS = 'wps_express';
     
         /**
          * PayPal Standard Bml - alias METHOD_WPP_BML
          */
    -    const METHOD_WPS_BML = 'wps_express_bml';
    +    public const METHOD_WPS_BML = 'wps_express_bml';
     
         /**
          * PayPal Bill Me Later - Express Checkout
          */
    -    const METHOD_WPP_BML = 'paypal_express_bml';
    +    public const METHOD_WPP_BML = 'paypal_express_bml';
     
         /**
          * PayPal Website Payments Pro - Direct Payments
          */
    -    const METHOD_WPP_DIRECT = 'paypal_direct';
    +    public const METHOD_WPP_DIRECT = 'paypal_direct';
     
         /**
          * PayPal Website Payments Pro - Direct Payments - alias METHOD_PAYFLOWPRO
          */
    -    const METHOD_PAYMENT_PRO = 'paypal_payment_pro';
    +    public const METHOD_PAYMENT_PRO = 'paypal_payment_pro';
     
         /**
          * Express Checkout (Payflow Edition)
          */
    -    const METHOD_WPP_PE_EXPRESS = 'payflow_express';
    +    public const METHOD_WPP_PE_EXPRESS = 'payflow_express';
     
         /**
          * PayPal Bill Me Later - Express Checkout (Payflow Edition)
          */
    -    const METHOD_WPP_PE_BML = 'payflow_express_bml';
    +    public const METHOD_WPP_PE_BML = 'payflow_express_bml';
     
         /**
          * Payflow Pro Gateway
          */
    -    const METHOD_PAYFLOWPRO = 'payflowpro';
    +    public const METHOD_PAYFLOWPRO = 'payflowpro';
     
    -    const METHOD_PAYFLOWLINK = 'payflow_link';
    +    public const METHOD_PAYFLOWLINK = 'payflow_link';
     
    -    const METHOD_PAYFLOWADVANCED = 'payflow_advanced';
    +    public const METHOD_PAYFLOWADVANCED = 'payflow_advanced';
     
    -    const METHOD_HOSTEDPRO = 'hosted_pro';
    +    public const METHOD_HOSTEDPRO = 'hosted_pro';
     
    -    const METHOD_BILLING_AGREEMENT = 'paypal_billing_agreement';
    +    public const METHOD_BILLING_AGREEMENT = 'paypal_billing_agreement';
     
         /**#@+
          * Buttons and images
          */
    -    const EC_FLAVOR_DYNAMIC = 'dynamic';
    +    public const EC_FLAVOR_DYNAMIC = 'dynamic';
     
    -    const EC_FLAVOR_STATIC = 'static';
    +    public const EC_FLAVOR_STATIC = 'static';
     
    -    const EC_BUTTON_TYPE_SHORTCUT = 'ecshortcut';
    +    public const EC_BUTTON_TYPE_SHORTCUT = 'ecshortcut';
     
    -    const EC_BUTTON_TYPE_MARK = 'ecmark';
    +    public const EC_BUTTON_TYPE_MARK = 'ecmark';
     
    -    const PAYMENT_MARK_SMALL = 'small';
    +    public const PAYMENT_MARK_SMALL = 'small';
     
    -    const PAYMENT_MARK_MEDIUM = 'medium';
    +    public const PAYMENT_MARK_MEDIUM = 'medium';
     
    -    const PAYMENT_MARK_LARGE = 'large';
    +    public const PAYMENT_MARK_LARGE = 'large';
     
         /**#@-*/
    -    const DEFAULT_LOGO_TYPE = 'wePrefer_150x60';
    +    public const DEFAULT_LOGO_TYPE = 'wePrefer_150x60';
     
         /**#@+
          * Payment actions
          */
    -    const AUTHORIZATION_AMOUNT_ONE = 1;
    +    public const AUTHORIZATION_AMOUNT_ONE = 1;
     
    -    const AUTHORIZATION_AMOUNT_FULL = 2;
    +    public const AUTHORIZATION_AMOUNT_FULL = 2;
     
         /**#@-*/
     
         /**#@+
          * Require Billing Address
          */
    -    const REQUIRE_BILLING_ADDRESS_NO = 0;
    +    public const REQUIRE_BILLING_ADDRESS_NO = 0;
     
    -    const REQUIRE_BILLING_ADDRESS_ALL = 1;
    +    public const REQUIRE_BILLING_ADDRESS_ALL = 1;
     
    -    const REQUIRE_BILLING_ADDRESS_VIRTUAL = 2;
    +    public const REQUIRE_BILLING_ADDRESS_VIRTUAL = 2;
     
         /**#@-*/
     
         /**#@+
          * Fraud management actions
          */
    -    const FRAUD_ACTION_ACCEPT = 'Acept';
    +    public const FRAUD_ACTION_ACCEPT = 'Acept';
     
    -    const FRAUD_ACTION_DENY = 'Deny';
    +    public const FRAUD_ACTION_DENY = 'Deny';
     
         /**#@-*/
     
         /**#@+
          * Refund types
          */
    -    const REFUND_TYPE_FULL = 'Full';
    +    public const REFUND_TYPE_FULL = 'Full';
     
    -    const REFUND_TYPE_PARTIAL = 'Partial';
    +    public const REFUND_TYPE_PARTIAL = 'Partial';
     
         /**#@-*/
     
         /**#@+
          * Express Checkout flows
          */
    -    const EC_SOLUTION_TYPE_SOLE = 'Sole';
    +    public const EC_SOLUTION_TYPE_SOLE = 'Sole';
     
    -    const EC_SOLUTION_TYPE_MARK = 'Mark';
    +    public const EC_SOLUTION_TYPE_MARK = 'Mark';
     
         /**#@-*/
     
         /**#@+
          * Payment data transfer methods (Standard)
          */
    -    const WPS_TRANSPORT_IPN = 'ipn';
    +    public const WPS_TRANSPORT_IPN = 'ipn';
     
    -    const WPS_TRANSPORT_PDT = 'pdt';
    +    public const WPS_TRANSPORT_PDT = 'pdt';
     
    -    const WPS_TRANSPORT_IPN_PDT = 'ipn_n_pdt';
    +    public const WPS_TRANSPORT_IPN_PDT = 'ipn_n_pdt';
     
         /**#@-*/
     
         /**#@+
          * Billing Agreement Signup type
          */
    -    const EC_BA_SIGNUP_AUTO = 'auto';
    +    public const EC_BA_SIGNUP_AUTO = 'auto';
     
    -    const EC_BA_SIGNUP_ASK = 'ask';
    +    public const EC_BA_SIGNUP_ASK = 'ask';
     
    -    const EC_BA_SIGNUP_NEVER = 'never';
    +    public const EC_BA_SIGNUP_NEVER = 'never';
     
         /**
          * Paypal setting
          */
    -    const TRANSFER_CART_LINE_ITEMS = 'lineItemsEnabled';
    -    const TRANSFER_SHIPPING_OPTIONS = 'transferShippingOptions';
    +    public const TRANSFER_CART_LINE_ITEMS = 'lineItemsEnabled';
    +    public const TRANSFER_SHIPPING_OPTIONS = 'transferShippingOptions';
     
         /**#@-*/
     
         /**
          * Config path for enabling/disabling order review step in express checkout
          */
    -    const XML_PATH_PAYPAL_EXPRESS_SKIP_ORDER_REVIEW_STEP_FLAG = 'payment/paypal_express/skip_order_review_step';
    +    public const XML_PATH_PAYPAL_EXPRESS_SKIP_ORDER_REVIEW_STEP_FLAG = 'payment/paypal_express/skip_order_review_step';
     
         /**
          * PayPal PayLater
          */
    -    const PAYLATER = 'paypal_paylater';
    +    public const PAYLATER = 'paypal_paylater';
     
         /**
          *
    @@ -599,21 +602,28 @@ class Config extends AbstractConfig
          */
         protected $_certFactory;
     
    +    /**
    +     * @var CspNonceProvider
    +     */
    +    protected $cspNonceProvider;
    +
         /**
          * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig
          * @param \Magento\Directory\Helper\Data $directoryHelper
          * @param \Magento\Store\Model\StoreManagerInterface $storeManager
          * @param \Magento\Payment\Model\Source\CctypeFactory $cctypeFactory
          * @param CertFactory $certFactory
          * @param array $params
    +     * @param CspNonceProvider|null $cspNonceProvider
          */
         public function __construct(
             \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig,
             \Magento\Directory\Helper\Data $directoryHelper,
             \Magento\Store\Model\StoreManagerInterface $storeManager,
             \Magento\Payment\Model\Source\CctypeFactory $cctypeFactory,
             \Magento\Paypal\Model\CertFactory $certFactory,
    -        $params = []
    +        $params = [],
    +        CspNonceProvider $cspNonceProvider = null
         ) {
             parent::__construct($scopeConfig);
             $this->directoryHelper = $directoryHelper;
    @@ -628,6 +638,8 @@ public function __construct(
                     $this->setStoreId($storeId);
                 }
             }
    +
    +        $this->cspNonceProvider = $cspNonceProvider ?: ObjectManager::getInstance()->get(CspNonceProvider::class);
         }
     
         /**
    @@ -1845,4 +1857,15 @@ public function getPayLaterConfigValue($fieldName)
                 $this->_storeId
             );
         }
    +
    +    /**
    +     * Get a cps nonce for the current request
    +     *
    +     * @return string
    +     * @throws \Magento\Framework\Exception\LocalizedException
    +     */
    +    public function getCspNonce(): string
    +    {
    +        return $this->cspNonceProvider->generateNonce();
    +    }
     }
    
  • app/code/Magento/Paypal/Model/SmartButtonConfig.php+2 2 modified
    @@ -11,7 +11,6 @@
     use Magento\Framework\App\Config\ScopeConfigInterface;
     use Magento\Framework\Locale\ResolverInterface;
     use Magento\Store\Model\ScopeInterface;
    -use Magento\Store\Model\StoreManagerInterface;
     use Magento\Paypal\Model\Config as PaypalConfig;
     
     /**
    @@ -92,7 +91,8 @@ public function getConfig(string $page): array
                 'isGuestCheckoutAllowed'  => $isGuestCheckoutAllowed,
                 'sdkUrl' => $this->sdkUrl->getUrl(),
                 'dataAttributes' => [
    -                'data-partner-attribution-id' => $this->paypalConfig->getBuildNotationCode()
    +                'data-partner-attribution-id' => $this->paypalConfig->getBuildNotationCode(),
    +                'data-csp-nonce' => $this->paypalConfig->getCspNonce(),
                 ]
             ];
         }
    
  • app/code/Magento/Paypal/Test/Unit/Model/_files/expected_style_config.php+8 4 modified
    @@ -29,7 +29,8 @@
                 'isGuestCheckoutAllowed' => true,
                 'sdkUrl' => 'http://mock.url',
                 'dataAttributes' => [
    -                'data-partner-attribution-id' => ''
    +                'data-partner-attribution-id' => '',
    +                'data-csp-nonce' => ''
                 ]
             ]
         ],
    @@ -56,7 +57,8 @@
                 'isGuestCheckoutAllowed' => true,
                 'sdkUrl' => 'http://mock.url',
                 'dataAttributes' => [
    -                'data-partner-attribution-id' => ''
    +                'data-partner-attribution-id' => '',
    +                'data-csp-nonce' => ''
                 ]
             ]
         ],
    @@ -82,7 +84,8 @@
                 'isGuestCheckoutAllowed' => true,
                 'sdkUrl' => 'http://mock.url',
                 'dataAttributes' => [
    -                'data-partner-attribution-id' => ''
    +                'data-partner-attribution-id' => '',
    +                'data-csp-nonce' => ''
                 ]
             ]
         ],
    @@ -108,7 +111,8 @@
                 'isGuestCheckoutAllowed' => true,
                 'sdkUrl' => 'http://mock.url',
                 'dataAttributes' => [
    -                'data-partner-attribution-id' => ''
    +                'data-partner-attribution-id' => '',
    +                'data-csp-nonce' => ''
                 ]
             ]
         ],
    
  • app/code/Magento/Persistent/composer.json+14 12 modified
    @@ -1,24 +1,25 @@
     {
         "name": "magento/module-persistent",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.4-p6",
         "require": {
             "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    -        "magento/module-checkout": "*",
    -        "magento/module-cron": "*",
    -        "magento/module-customer": "*",
    -        "magento/module-page-cache": "*",
    -        "magento/module-quote": "*",
    -        "magento/module-store": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-checkout": "100.4.*",
    +        "magento/module-cron": "100.4.*",
    +        "magento/module-customer": "103.0.*",
    +        "magento/module-page-cache": "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"
    @@ -28,3 +29,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Persistent/Test/Mftf/Test/StorefrontVerifyShoppingCartPersistenceUnderLongTermCookieTest.xml+1 0 modified
    @@ -152,6 +152,7 @@
             <actionGroup ref="AssertStorefrontCustomerLogoutSuccessPageActionGroup" stepKey="seeLogoutSuccessPageUrlAfterLogOutJohnSmithCustomerSecondTime"/>
             <waitForPageLoad stepKey="waitForHomePageLoadAfter5Seconds"/>
             <waitForText selector="{{StorefrontCMSPageSection.mainContent}}" userInput="CMS homepage content goes here." stepKey="waitForLoadMainContentMessageOnHomePage"/>
    +        <waitForElementVisible selector="{{StorefrontPanelHeaderSection.notYouLink}}" stepKey="waitForNotYouLink"/>
             <click selector="{{StorefrontPanelHeaderSection.notYouLink}}" stepKey="clickOnNotYouLink" />
             <waitForPageLoad stepKey="waitForCustomerLoginPageLoad"/>
             <actionGroup ref="AssertMiniCartEmptyActionGroup" stepKey="assertMiniCartEmptyAfterJohnDoeSignOut" />
    
  • app/code/Magento/ProductAlert/composer.json+17 15 modified
    @@ -1,29 +1,30 @@
     {
         "name": "magento/module-product-alert",
         "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-bulk": "*",
    -        "magento/module-asynchronous-operations": "*",
    -        "magento/module-authorization": "*",
    -        "magento/module-backend": "*",
    -        "magento/module-catalog": "*",
    -        "magento/module-customer": "*",
    -        "magento/module-store": "*",
    -        "magento/module-theme": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/framework-bulk": "101.0.*",
    +        "magento/module-asynchronous-operations": "100.4.*",
    +        "magento/module-authorization": "100.4.*",
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-customer": "103.0.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/module-theme": "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"
    @@ -33,3 +34,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/ProductVideo/composer.json+15 13 modified
    @@ -1,28 +1,29 @@
     {
         "name": "magento/module-product-video",
         "description": "Add Video to Products",
    +    "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/framework": "103.0.*",
             "magento/magento-composer-installer": "*",
    -        "magento/module-backend": "*",
    -        "magento/module-catalog": "*",
    -        "magento/module-eav": "*",
    -        "magento/module-media-storage": "*",
    -        "magento/module-store": "*"
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-eav": "102.1.*",
    +        "magento/module-media-storage": "100.4.*",
    +        "magento/module-store": "101.1.*"
         },
         "suggest": {
    -        "magento/module-customer": "*",
    -        "magento/module-config": "*"
    +        "magento/module-customer": "103.0.*",
    +        "magento/module-config": "101.2.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -32,3 +33,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/QuoteAnalytics/composer.json+8 6 modified
    @@ -1,17 +1,18 @@
     {
         "name": "magento/module-quote-analytics",
         "description": "N/A",
    -    "require": {
    -        "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    -        "magento/module-quote": "*",
    -        "magento/module-analytics": "*"
    -    },
         "type": "magento2-module",
         "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-quote": "101.2.*",
    +        "magento/module-analytics": "100.4.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -21,3 +22,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/QuoteBundleOptions/composer.json+7 5 modified
    @@ -1,16 +1,17 @@
     {
         "name": "magento/module-quote-bundle-options",
         "description": "Magento module provides data provider for creating buy request for bundle products",
    -    "require": {
    -        "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    -        "magento/module-quote": "*"
    -    },
         "type": "magento2-module",
         "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-quote": "101.2.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -20,3 +21,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Quote/composer.json+23 21 modified
    @@ -1,35 +1,36 @@
     {
         "name": "magento/module-quote",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "101.2.4-p9",
         "require": {
             "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    -        "magento/module-authorization": "*",
    -        "magento/module-backend": "*",
    -        "magento/module-catalog": "*",
    -        "magento/module-catalog-inventory": "*",
    -        "magento/module-checkout": "*",
    -        "magento/module-customer": "*",
    -        "magento/module-directory": "*",
    -        "magento/module-eav": "*",
    -        "magento/module-payment": "*",
    -        "magento/module-sales": "*",
    -        "magento/module-sales-sequence": "*",
    -        "magento/module-shipping": "*",
    -        "magento/module-store": "*",
    -        "magento/module-tax": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-authorization": "100.4.*",
    +        "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-directory": "100.4.*",
    +        "magento/module-eav": "102.1.*",
    +        "magento/module-payment": "100.4.*",
    +        "magento/module-sales": "103.0.*",
    +        "magento/module-sales-sequence": "100.4.*",
    +        "magento/module-shipping": "100.4.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/module-tax": "100.4.*"
         },
         "suggest": {
    -        "magento/module-webapi": "*"
    +        "magento/module-webapi": "100.4.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -39,3 +40,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/QuoteConfigurableOptions/composer.json+7 5 modified
    @@ -1,16 +1,17 @@
     {
         "name": "magento/module-quote-configurable-options",
         "description": "Magento module provides data provider for creating buy request for configurable products",
    -    "require": {
    -        "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    -        "magento/module-quote": "*"
    -    },
         "type": "magento2-module",
         "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-quote": "101.2.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -20,3 +21,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/QuoteDownloadableLinks/composer.json+7 5 modified
    @@ -1,16 +1,17 @@
     {
         "name": "magento/module-quote-downloadable-links",
         "description": "Magento module provides data provider for creating buy request for links of downloadable products",
    -    "require": {
    -        "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    -        "magento/module-quote": "*"
    -    },
         "type": "magento2-module",
         "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-quote": "101.2.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -20,3 +21,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Quote/etc/webapi_rest/di.xml+4 0 modified
    @@ -18,5 +18,9 @@
         </type>
         <type name="Magento\Quote\Model\Quote">
             <plugin name="updateQuoteStoreId" type="Magento\Quote\Model\Quote\Plugin\UpdateQuoteStoreId" />
    +        <plugin name="validateQuoteAddress" type="Magento\Quote\Plugin\QuoteAddress" />
    +    </type>
    +    <type name="Magento\Quote\Api\CartRepositoryInterface">
    +        <plugin name="quoteValidateOrderId" type="Magento\Quote\Plugin\ValidateQuoteOrigOrder"/>
         </type>
     </config>
    
  • app/code/Magento/QuoteGraphQl/composer.json+21 19 modified
    @@ -2,30 +2,31 @@
         "name": "magento/module-quote-graph-ql",
         "description": "N/A",
         "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
    +    "version": "100.4.4-p4",
         "require": {
             "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    -        "magento/module-quote": "*",
    -        "magento/module-checkout": "*",
    -        "magento/module-catalog": "*",
    -        "magento/module-store": "*",
    -        "magento/module-customer": "*",
    -        "magento/module-customer-graph-ql": "*",
    -        "magento/module-sales": "*",
    -        "magento/module-directory": "*",
    -        "magento/module-graph-ql": "*",
    -        "magento/module-gift-message": "*",
    -        "magento/module-catalog-inventory": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-quote": "101.2.*",
    +        "magento/module-checkout": "100.4.*",
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/module-customer": "103.0.*",
    +        "magento/module-customer-graph-ql": "100.4.*",
    +        "magento/module-sales": "103.0.*",
    +        "magento/module-directory": "100.4.*",
    +        "magento/module-graph-ql": "100.4.*",
    +        "magento/module-gift-message": "100.4.*",
    +        "magento/module-catalog-inventory": "100.4.*"
         },
         "suggest": {
    -        "magento/module-graph-ql-cache": "*",
    -        "magento/module-catalog-inventory-graph-ql": "*",
    -        "magento/module-payment-graph-ql": "*"
    +        "magento/module-graph-ql-cache": "100.4.*",
    +        "magento/module-catalog-inventory-graph-ql": "100.4.*",
    +        "magento/module-payment-graph-ql": "100.4.*"
         },
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -35,3 +36,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/QuoteGraphQl/Model/Resolver/RemoveItemFromCart.php+2 1 modified
    @@ -86,6 +86,8 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value
             $itemId = $processedArgs['input']['cart_item_id'];
     
             $storeId = (int)$context->getExtensionAttributes()->getStore()->getId();
    +        /** Check if the current user is allowed to perform actions with the cart */
    +        $cart = $this->getCartForUser->execute($maskedCartId, $context->getUserId(), $storeId);
     
             try {
                 $this->cartItemRepository->deleteById($cartId, $itemId);
    @@ -95,7 +97,6 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value
                 throw new GraphQlInputException(__($e->getMessage()), $e);
             }
     
    -        $cart = $this->getCartForUser->execute($maskedCartId, $context->getUserId(), $storeId);
             return [
                 'cart' => [
                     'model' => $cart,
    
  • app/code/Magento/Quote/i18n/en_US.csv+3 0 modified
    @@ -68,3 +68,6 @@ Carts,Carts
     "Invalid state change requested","Invalid state change requested"
     "Validated Country Code","Validated Country Code"
     "Validated Vat Number","Validated Vat Number"
    +"Invalid Quote Item id %1","Invalid Quote Item id %1"
    +"Invalid quote address id %1","Invalid quote address id %1"
    +"Please check input parameters.","Please check input parameters."
    
  • app/code/Magento/Quote/Model/BillingAddressManagement.php+7 6 modified
    @@ -3,14 +3,15 @@
      * Copyright © Magento, Inc. All rights reserved.
      * See COPYING.txt for license details.
      */
    +declare(strict_types=1);
     
     namespace Magento\Quote\Model;
     
    +use Magento\Framework\App\ObjectManager;
     use Magento\Framework\Exception\InputException;
    -use Magento\Quote\Model\Quote\Address\BillingAddressPersister;
    -use Psr\Log\LoggerInterface as Logger;
     use Magento\Quote\Api\BillingAddressManagementInterface;
    -use Magento\Framework\App\ObjectManager;
    +use Magento\Quote\Api\Data\AddressInterface;
    +use Psr\Log\LoggerInterface as Logger;
     
     /**
      * Quote billing address write service object.
    @@ -25,14 +26,14 @@ class BillingAddressManagement implements BillingAddressManagementInterface
         protected $addressValidator;
     
         /**
    -     * Logger.
    +     * Logger object.
          *
          * @var Logger
          */
         protected $logger;
     
         /**
    -     * Quote repository.
    +     * Quote repository object.
          *
          * @var \Magento\Quote\Api\CartRepositoryInterface
          */
    @@ -72,7 +73,7 @@ public function __construct(
          * @inheritdoc
          * @SuppressWarnings(PHPMD.NPathComplexity)
          */
    -    public function assign($cartId, \Magento\Quote\Api\Data\AddressInterface $address, $useForShipping = false)
    +    public function assign($cartId, AddressInterface $address, $useForShipping = false)
         {
             /** @var \Magento\Quote\Model\Quote $quote */
             $quote = $this->quoteRepository->getActive($cartId);
    
  • app/code/Magento/Quote/Model/QuoteAddressValidator.php+56 28 modified
    @@ -3,8 +3,15 @@
      * Copyright © Magento, Inc. All rights reserved.
      * See COPYING.txt for license details.
      */
    +declare(strict_types=1);
    +
     namespace Magento\Quote\Model;
     
    +use Magento\Customer\Api\AddressRepositoryInterface;
    +use Magento\Customer\Api\CustomerRepositoryInterface;
    +use Magento\Customer\Model\Session;
    +use Magento\Framework\Exception\InputException;
    +use Magento\Framework\Exception\LocalizedException;
     use Magento\Framework\Exception\NoSuchEntityException;
     use Magento\Quote\Api\Data\AddressInterface;
     use Magento\Quote\Api\Data\CartInterface;
    @@ -17,35 +24,35 @@
     class QuoteAddressValidator
     {
         /**
    -     * Address factory.
    -     *
    -     * @var \Magento\Customer\Api\AddressRepositoryInterface
    +     * @var AddressRepositoryInterface
          */
    -    protected $addressRepository;
    +    protected AddressRepositoryInterface $addressRepository;
     
         /**
    -     * Customer repository.
    +     * Customer repository object.
          *
    -     * @var \Magento\Customer\Api\CustomerRepositoryInterface
    +     * @var CustomerRepositoryInterface
          */
    -    protected $customerRepository;
    +    protected CustomerRepositoryInterface $customerRepository;
     
         /**
    +     * @var Session
          * @deprecated 101.1.1 This class is not a part of HTML presentation layer and should not use sessions.
    +     * @see Session
          */
    -    protected $customerSession;
    +    protected Session $customerSession;
     
         /**
          * Constructs a quote shipping address validator service object.
          *
    -     * @param \Magento\Customer\Api\AddressRepositoryInterface $addressRepository
    -     * @param \Magento\Customer\Api\CustomerRepositoryInterface $customerRepository Customer repository.
    -     * @param \Magento\Customer\Model\Session $customerSession
    +     * @param AddressRepositoryInterface $addressRepository
    +     * @param CustomerRepositoryInterface $customerRepository Customer repository.
    +     * @param Session $customerSession
          */
         public function __construct(
    -        \Magento\Customer\Api\AddressRepositoryInterface $addressRepository,
    -        \Magento\Customer\Api\CustomerRepositoryInterface $customerRepository,
    -        \Magento\Customer\Model\Session $customerSession
    +        AddressRepositoryInterface $addressRepository,
    +        CustomerRepositoryInterface $customerRepository,
    +        Session $customerSession
         ) {
             $this->addressRepository = $addressRepository;
             $this->customerRepository = $customerRepository;
    @@ -56,18 +63,18 @@ public function __construct(
          * Validate address.
          *
          * @param AddressInterface $address
    -     * @param int|null $customerId Cart belongs to
    +     * @param int|null $customerId
          * @return void
    -     * @throws \Magento\Framework\Exception\InputException The specified address belongs to another customer.
    -     * @throws \Magento\Framework\Exception\NoSuchEntityException The specified customer ID or address ID is not valid.
    +     * @throws LocalizedException The specified customer ID or address ID is not valid.
    +     * @throws NoSuchEntityException The specified customer ID or address ID is not valid.
          */
         private function doValidate(AddressInterface $address, ?int $customerId): void
         {
             //validate customer id
             if ($customerId) {
                 $customer = $this->customerRepository->getById($customerId);
                 if (!$customer->getId()) {
    -                throw new \Magento\Framework\Exception\NoSuchEntityException(
    +                throw new NoSuchEntityException(
                         __('Invalid customer id %1', $customerId)
                     );
                 }
    @@ -76,15 +83,15 @@ private function doValidate(AddressInterface $address, ?int $customerId): void
             if ($address->getCustomerAddressId()) {
                 //Existing address cannot belong to a guest
                 if (!$customerId) {
    -                throw new \Magento\Framework\Exception\NoSuchEntityException(
    +                throw new NoSuchEntityException(
                         __('Invalid customer address id %1', $address->getCustomerAddressId())
                     );
                 }
                 //Validating address ID
                 try {
                     $this->addressRepository->getById($address->getCustomerAddressId());
                 } catch (NoSuchEntityException $e) {
    -                throw new \Magento\Framework\Exception\NoSuchEntityException(
    +                throw new NoSuchEntityException(
                         __('Invalid address id %1', $address->getId())
                     );
                 }
    @@ -94,7 +101,7 @@ private function doValidate(AddressInterface $address, ?int $customerId): void
                     return $address->getId();
                 }, $this->customerRepository->getById($customerId)->getAddresses());
                 if (!in_array($address->getCustomerAddressId(), $applicableAddressIds)) {
    -                throw new \Magento\Framework\Exception\NoSuchEntityException(
    +                throw new NoSuchEntityException(
                         __('Invalid customer address id %1', $address->getCustomerAddressId())
                     );
                 }
    @@ -104,12 +111,12 @@ private function doValidate(AddressInterface $address, ?int $customerId): void
         /**
          * Validates the fields in a specified address data object.
          *
    -     * @param \Magento\Quote\Api\Data\AddressInterface $addressData The address data object.
    +     * @param AddressInterface $addressData The address data object.
          * @return bool
    -     * @throws \Magento\Framework\Exception\InputException The specified address belongs to another customer.
    -     * @throws \Magento\Framework\Exception\NoSuchEntityException The specified customer ID or address ID is not valid.
    +     * @throws InputException The specified address belongs to another customer.
    +     * @throws NoSuchEntityException|LocalizedException The specified customer ID or address ID is not valid.
          */
    -    public function validate(AddressInterface $addressData)
    +    public function validate(AddressInterface $addressData): bool
         {
             $this->doValidate($addressData, $addressData->getCustomerId());
     
    @@ -122,11 +129,32 @@ public function validate(AddressInterface $addressData)
          * @param CartInterface $cart
          * @param AddressInterface $address
          * @return void
    -     * @throws \Magento\Framework\Exception\InputException The specified address belongs to another customer.
    -     * @throws \Magento\Framework\Exception\NoSuchEntityException The specified customer ID or address ID is not valid.
    +     * @throws InputException The specified address belongs to another customer.
    +     * @throws NoSuchEntityException|LocalizedException The specified customer ID or address ID is not valid.
          */
         public function validateForCart(CartInterface $cart, AddressInterface $address): void
         {
    -        $this->doValidate($address, $cart->getCustomerIsGuest() ? null : $cart->getCustomer()->getId());
    +        $this->doValidate($address, $cart->getCustomerIsGuest() ? null : (int) $cart->getCustomer()->getId());
    +    }
    +
    +    /**
    +     * Validate address id to be used for cart.
    +     *
    +     * @param CartInterface $cart
    +     * @param AddressInterface $address
    +     * @return void
    +     * @throws NoSuchEntityException The specified customer ID or address ID is not valid.
    +     */
    +    public function validateWithExistingAddress(CartInterface $cart, AddressInterface $address): void
    +    {
    +        // check if address belongs to quote.
    +        if ($address->getId() !== null) {
    +            $old = $cart->getAddressById($address->getId());
    +            if (empty($old)) {
    +                throw new NoSuchEntityException(
    +                    __('Invalid quote address id %1', $address->getId())
    +                );
    +            }
    +        }
         }
     }
    
  • app/code/Magento/Quote/Model/Quote.php+10 18 modified
    @@ -5,6 +5,7 @@
      */
     namespace Magento\Quote\Model;
     
    +use Magento\Catalog\Model\Product\Attribute\Source\Status as ProductStatus;
     use Magento\Customer\Api\Data\CustomerInterface;
     use Magento\Customer\Api\Data\GroupInterface;
     use Magento\Directory\Model\AllowedCountries;
    @@ -112,7 +113,7 @@ class Quote extends AbstractExtensibleModel implements \Magento\Quote\Api\Data\C
         /**
          * Checkout login method key
          */
    -    const CHECKOUT_METHOD_LOGIN_IN = 'login_in';
    +    public const CHECKOUT_METHOD_LOGIN_IN = 'login_in';
     
         /**
          * @var string
    @@ -172,15 +173,11 @@ class Quote extends AbstractExtensibleModel implements \Magento\Quote\Api\Data\C
         protected $_preventSaving = false;
     
         /**
    -     * Catalog product
    -     *
          * @var \Magento\Catalog\Helper\Product
          */
         protected $_catalogProduct;
     
         /**
    -     * Quote validator
    -     *
          * @var \Magento\Quote\Model\QuoteValidator
          */
         protected $quoteValidator;
    @@ -213,8 +210,6 @@ class Quote extends AbstractExtensibleModel implements \Magento\Quote\Api\Data\C
         protected $_customerFactory;
     
         /**
    -     * Group repository
    -     *
          * @var \Magento\Customer\Api\GroupRepositoryInterface
          */
         protected $groupRepository;
    @@ -260,22 +255,16 @@ class Quote extends AbstractExtensibleModel implements \Magento\Quote\Api\Data\C
         protected $_objectCopyService;
     
         /**
    -     * Address repository
    -     *
          * @var \Magento\Customer\Api\AddressRepositoryInterface
          */
         protected $addressRepository;
     
         /**
    -     * Search criteria builder
    -     *
          * @var \Magento\Framework\Api\SearchCriteriaBuilder
          */
         protected $searchCriteriaBuilder;
     
         /**
    -     * Filter builder
    -     *
          * @var \Magento\Framework\Api\FilterBuilder
          */
         protected $filterBuilder;
    @@ -874,7 +863,8 @@ public function beforeSave()
          * Loading quote data by customer
          *
          * @param \Magento\Customer\Model\Customer|int $customer
    -     * @deprecated 101.0.0
    +     * @deprecated 101.0.0 Deprecated to handle external usages of customer methods
    +     * @see https://jira.corp.magento.com/browse/MAGETWO-19935
          * @return $this
          */
         public function loadByCustomer($customer)
    @@ -1356,7 +1346,7 @@ public function setBillingAddress(\Magento\Quote\Api\Data\AddressInterface $addr
         {
             $old = $this->getAddressesCollection()->getItemById($address->getId())
                 ?? $this->getBillingAddress();
    -        if (!empty($old)) {
    +        if ($old) {
                 $old->addData($address->getData());
             } else {
                 $this->addAddress($address->setAddressType(Address::TYPE_BILLING));
    @@ -1378,7 +1368,7 @@ public function setShippingAddress(\Magento\Quote\Api\Data\AddressInterface $add
             } else {
                 $old = $this->getAddressesCollection()->getItemById($address->getId())
                     ?? $this->getShippingAddress();
    -            if (!empty($old)) {
    +            if ($old) {
                     $old->addData($address->getData());
                 } else {
                     $this->addAddress($address->setAddressType(Address::TYPE_SHIPPING));
    @@ -1427,12 +1417,14 @@ public function getItemsCollection($useCache = true)
         public function getAllItems()
         {
             $items = [];
    +        /** @var \Magento\Quote\Model\Quote\Item $item */
             foreach ($this->getItemsCollection() as $item) {
    -            /** @var \Magento\Quote\Model\Quote\Item $item */
    -            if (!$item->isDeleted()) {
    +            $product = $item->getProduct();
    +            if (!$item->isDeleted() && ($product && (int)$product->getStatus() !== ProductStatus::STATUS_DISABLED)) {
                     $items[] = $item;
                 }
             }
    +
             return $items;
         }
     
    
  • app/code/Magento/Quote/Model/ShippingMethodManagement.php+27 2 modified
    @@ -3,6 +3,8 @@
      * Copyright © Magento, Inc. All rights reserved.
      * See COPYING.txt for license details.
      */
    +declare(strict_types=1);
    +
     namespace Magento\Quote\Model;
     
     use Magento\Customer\Api\AddressRepositoryInterface;
    @@ -25,6 +27,7 @@
     use Magento\Quote\Model\Quote\Address\Rate;
     use Magento\Quote\Model\Quote\TotalsCollector;
     use Magento\Quote\Model\ResourceModel\Quote\Address as QuoteAddressResource;
    +use Magento\Customer\Model\Data\Address as CustomerAddress;
     
     /**
      * Shipping method read service
    @@ -38,7 +41,7 @@ class ShippingMethodManagement implements
         ShipmentEstimationInterface
     {
         /**
    -     * Quote repository.
    +     * Quote repository model
          *
          * @var CartRepositoryInterface
          */
    @@ -268,6 +271,8 @@ public function estimateByExtendedAddress($cartId, AddressInterface $address)
     
         /**
          * @inheritDoc
    +     * @throws InputException
    +     * @throws NoSuchEntityException
          */
         public function estimateByAddressId($cartId, $addressId)
         {
    @@ -278,7 +283,7 @@ public function estimateByAddressId($cartId, $addressId)
             if ($quote->isVirtual() || 0 == $quote->getItemsCount()) {
                 return [];
             }
    -        $address = $this->addressRepository->getById($addressId);
    +        $address = $this->getAddress($addressId, $quote);
     
             return $this->getShippingMethods($quote, $address);
         }
    @@ -384,4 +389,24 @@ private function getDataObjectProcessor()
             }
             return $this->dataProcessor;
         }
    +
    +    /**
    +     * Gets the address if exists for customer
    +     *
    +     * @param int $addressId
    +     * @param Quote $quote
    +     * @return CustomerAddress
    +     * @throws InputException The shipping address is incorrect.
    +     */
    +    private function getAddress(int $addressId, Quote $quote): CustomerAddress
    +    {
    +        $addresses = $quote->getCustomer()->getAddresses();
    +        foreach ($addresses as $address) {
    +            if ($addressId === (int)$address->getId()) {
    +                return $address;
    +            }
    +        }
    +
    +        throw new InputException(__('The shipping address is missing. Set the address and try again.'));
    +    }
     }
    
  • app/code/Magento/Quote/Plugin/QuoteAddress.php+67 0 added
    @@ -0,0 +1,67 @@
    +<?php
    +/**
    + * Copyright © Magento, Inc. All rights reserved.
    + * See COPYING.txt for license details.
    + */
    +declare(strict_types=1);
    +
    +namespace Magento\Quote\Plugin;
    +
    +use Magento\Framework\Exception\NoSuchEntityException;
    +use Magento\Quote\Model\Quote;
    +use Magento\Quote\Api\Data\AddressInterface;
    +use Magento\Quote\Model\QuoteAddressValidator;
    +
    +/**
    + * Quote address plugin
    + */
    +class QuoteAddress
    +{
    +    /**
    +     * @var QuoteAddressValidator
    +     */
    +    protected QuoteAddressValidator $addressValidator;
    +
    +    /**
    +     * @param QuoteAddressValidator $addressValidator
    +     */
    +    public function __construct(
    +        QuoteAddressValidator $addressValidator
    +    ) {
    +        $this->addressValidator = $addressValidator;
    +    }
    +
    +    /**
    +     * Validate address before setting billing address
    +     *
    +     * @param Quote $subject
    +     * @param AddressInterface|null $address
    +     * @return array
    +     * @throws NoSuchEntityException
    +     */
    +    public function beforeSetBillingAddress(Quote $subject, AddressInterface $address = null): array
    +    {
    +        if ($address !== null) {
    +            $this->addressValidator->validateWithExistingAddress($subject, $address);
    +        }
    +
    +        return [$address];
    +    }
    +
    +    /**
    +     * Validate address before setting shipping address
    +     *
    +     * @param Quote $subject
    +     * @param AddressInterface|null $address
    +     * @return array
    +     * @throws NoSuchEntityException
    +     */
    +    public function beforeSetShippingAddress(Quote $subject, AddressInterface $address = null): array
    +    {
    +        if ($address !== null) {
    +            $this->addressValidator->validateWithExistingAddress($subject, $address);
    +        }
    +
    +        return [$address];
    +    }
    +}
    
  • app/code/Magento/Quote/Plugin/ValidateQuoteOrigOrder.php+65 0 added
    @@ -0,0 +1,65 @@
    +<?php
    +/**
    + * Copyright © Magento, Inc. All rights reserved.
    + * See COPYING.txt for license details.
    + */
    +
    +declare(strict_types=1);
    +
    +namespace Magento\Quote\Plugin;
    +
    +use Magento\Framework\Exception\NoSuchEntityException;
    +use Magento\Framework\Webapi\Rest\Request as RestRequest;
    +use Magento\Quote\Api\CartRepositoryInterface;
    +use Magento\Quote\Api\Data\CartInterface;
    +use Magento\Sales\Api\OrderRepositoryInterface;
    +
    +/**
    + * Validate order id from request param
    + */
    +class ValidateQuoteOrigOrder
    +{
    +    /**
    +     * @var OrderRepositoryInterface
    +     */
    +    private $orderRepository;
    +
    +    /**
    +     * @var RestRequest $request
    +     */
    +    private $request;
    +
    +    /**
    +     * @param RestRequest $request
    +     * @param OrderRepositoryInterface $orderRepository
    +     */
    +    public function __construct(RestRequest $request, OrderRepositoryInterface $orderRepository)
    +    {
    +        $this->request = $request;
    +        $this->orderRepository = $orderRepository;
    +    }
    +
    +    /**
    +     * Validate the user authorization to order
    +     *
    +     * @param CartRepositoryInterface $cartRepository
    +     * @param CartInterface $quote
    +     * @return void
    +     * @throws NoSuchEntityException
    +     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
    +     */
    +    public function beforeSave(
    +        CartRepositoryInterface $cartRepository,
    +        CartInterface $quote
    +    ): void {
    +        $params = $this->request->getBodyParams();
    +        if (!empty($params) && isset($params['quote']['orig_order_id'])) {
    +            $orderId = $params['quote']['orig_order_id'];
    +            $order = $this->orderRepository->get($orderId);
    +            $orderCustomer = (int)$order->getCustomerId();
    +            if ($quote->getCustomerId() !== $orderCustomer) {
    +                throw new NoSuchEntityException(__('Please check input parameters.'));
    +            }
    +        }
    +    }
    +}
    
  • app/code/Magento/Quote/Test/Unit/Model/QuoteTest.php+11 2 modified
    @@ -1034,6 +1034,9 @@ public function testAddProductItemPreparation(): void
             $itemMock->expects($this->any())
                 ->method('representProduct')
                 ->willReturn(true);
    +        $itemMock->expects($this->any())
    +            ->method('getProduct')
    +            ->willReturn($this->productMock);
     
             $iterator = new \ArrayIterator([$itemMock]);
             $collectionMock->expects($this->any())
    @@ -1402,20 +1405,26 @@ public function testGetItemsCollection(): void
         public function testGetAllItems(): void
         {
             $itemOneMock = $this->getMockBuilder(\Magento\Quote\Model\ResourceModel\Quote\Item::class)
    -            ->addMethods(['isDeleted'])
    +            ->addMethods(['isDeleted', 'getProduct'])
                 ->disableOriginalConstructor()
                 ->getMock();
             $itemOneMock->expects($this->once())
                 ->method('isDeleted')
                 ->willReturn(false);
    +        $itemOneMock->expects($this->once())
    +            ->method('getProduct')
    +            ->willReturn($this->productMock);
     
             $itemTwoMock = $this->getMockBuilder(\Magento\Quote\Model\ResourceModel\Quote\Item::class)
    -            ->addMethods(['isDeleted'])
    +            ->addMethods(['isDeleted', 'getProduct'])
                 ->disableOriginalConstructor()
                 ->getMock();
             $itemTwoMock->expects($this->once())
                 ->method('isDeleted')
                 ->willReturn(true);
    +        $itemTwoMock->expects($this->once())
    +            ->method('getProduct')
    +            ->willReturn($this->productMock);
     
             $items = [$itemOneMock, $itemTwoMock];
             $itemResult = [$itemOneMock];
    
  • app/code/Magento/Quote/Test/Unit/Model/ShippingMethodManagementTest.php+120 94 modified
    @@ -24,12 +24,13 @@
     use Magento\Quote\Model\QuoteRepository;
     use Magento\Quote\Model\ResourceModel\Quote\Address as QuoteAddressResource;
     use Magento\Quote\Model\ShippingMethodManagement;
    -use Magento\Store\Model\Store;
     use PHPUnit\Framework\MockObject\MockObject;
     use PHPUnit\Framework\TestCase;
     use Magento\Quote\Api\Data\CartExtensionInterface;
     use Magento\Sales\Model\Order\ShippingAssignmentBuilder;
     use Magento\Quote\Api\Data\ShippingInterface;
    +use Magento\Customer\Api\Data\CustomerInterface;
    +use Magento\Customer\Model\Session;
     
     /**
      * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
    @@ -41,16 +42,6 @@ class ShippingMethodManagementTest extends TestCase
          */
         protected $model;
     
    -    /**
    -     * @var MockObject
    -     */
    -    protected $shippingMethodMock;
    -
    -    /**
    -     * @var MockObject
    -     */
    -    protected $methodDataFactoryMock;
    -
         /**
          * @var ShippingMethodConverter|MockObject
          */
    @@ -96,11 +87,6 @@ class ShippingMethodManagementTest extends TestCase
          */
         private $totalsCollector;
     
    -    /**
    -     * @var Store|MockObject
    -     */
    -    private $storeMock;
    -
         /**
          * @var QuoteAddressResource|MockObject
          */
    @@ -121,13 +107,20 @@ class ShippingMethodManagementTest extends TestCase
          */
         private $shippingAssignmentBuilder;
     
    +    /**
    +     * @var Session
    +     */
    +    private $customerSession;
    +
         protected function setUp(): void
         {
             $this->objectManager = new ObjectManager($this);
             $this->quoteRepository = $this->getMockForAbstractClass(CartRepositoryInterface::class);
             $this->addressRepository = $this->getMockForAbstractClass(AddressRepositoryInterface::class);
    +        $this->customerSession = $this->createMock(Session::class);
     
    -        $this->methodDataFactoryMock = $this->getMockBuilder(ShippingMethodInterfaceFactory::class)
    +        /** @var MockObject $methodDataFactoryMock */
    +        $methodDataFactoryMock = $this->getMockBuilder(ShippingMethodInterfaceFactory::class)
                 ->disableOriginalConstructor()
                 ->onlyMethods(['create'])
                 ->getMock();
    @@ -141,7 +134,6 @@ protected function setUp(): void
             $this->dataProcessor = $this->createMock($className);
     
             $this->quoteAddressResource = $this->createMock(QuoteAddressResource::class);
    -        $this->storeMock = $this->createMock(Store::class);
             $this->quote = $this->getMockBuilder(Quote::class)
                 ->disableOriginalConstructor()
                 ->setMethods([
    @@ -153,7 +145,8 @@ protected function setUp(): void
                     'collectTotals',
                     'save',
                     '__wakeup',
    -                'getExtensionAttributes'
    +                'getExtensionAttributes',
    +                'getCustomer'
                 ])
                 ->getMock();
     
    @@ -190,25 +183,17 @@ protected function setUp(): void
                 ShippingMethodManagement::class,
                 [
                     'quoteRepository' => $this->quoteRepository,
    -                'methodDataFactory' => $this->methodDataFactoryMock,
    +                'methodDataFactory' => $methodDataFactoryMock,
                     'converter' => $this->converter,
                     'totalsCollector' => $this->totalsCollector,
                     'addressRepository' => $this->addressRepository,
                     'quoteAddressResource' => $this->quoteAddressResource,
    +                'customerSession' => $this->customerSession,
                 ]
             );
     
    -        $this->objectManager->setBackwardCompatibleProperty(
    -            $this->model,
    -            'addressFactory',
    -            $this->addressFactory
    -        );
    -
    -        $this->objectManager->setBackwardCompatibleProperty(
    -            $this->model,
    -            'dataProcessor',
    -            $this->dataProcessor
    -        );
    +        $this->objectManager->setBackwardCompatibleProperty($this->model, 'addressFactory', $this->addressFactory);
    +        $this->objectManager->setBackwardCompatibleProperty($this->model, 'dataProcessor', $this->dataProcessor);
     
             $this->extensionAttributesMock = $this->getMockBuilder(CartExtensionInterface::class)
                 ->setMethods(['getShippingAssignments'])
    @@ -266,11 +251,12 @@ public function testGetMethod()
                 ->with('one_two')
                 ->willReturn($shippingRateMock);
     
    -        $this->shippingMethodMock = $this->getMockForAbstractClass(ShippingMethodInterface::class);
    +        /** @var MockObject $shippingMethodMock */
    +        $shippingMethodMock = $this->getMockForAbstractClass(ShippingMethodInterface::class);
             $this->converter->expects($this->once())
                 ->method('modelToDataObject')
                 ->with($shippingRateMock, $currencyCode)
    -            ->willReturn($this->shippingMethodMock);
    +            ->willReturn($shippingMethodMock);
             $this->model->get($cartId);
         }
     
    @@ -578,32 +564,32 @@ public function testEstimateByExtendedAddress()
                 ->method('create')
                 ->willReturn($address);
     
    -        $this->quoteRepository->expects(static::once())
    +        $this->quoteRepository->expects(self::once())
                 ->method('getActive')
                 ->with($cartId)
                 ->willReturn($this->quote);
     
    -        $this->quote->expects(static::once())
    +        $this->quote->expects(self::once())
                 ->method('isVirtual')
                 ->willReturn(false);
    -        $this->quote->expects(static::once())
    +        $this->quote->expects(self::once())
                 ->method('getItemsCount')
                 ->willReturn(1);
     
    -        $this->quote->expects(static::once())
    +        $this->quote->expects(self::once())
                 ->method('getShippingAddress')
                 ->willReturn($this->shippingAddress);
     
    -        $this->dataProcessor->expects(static::any())
    +        $this->dataProcessor->expects(self::any())
                 ->method('buildOutputDataArray')
                 ->willReturn($addressData);
     
    -        $this->shippingAddress->expects(static::once())
    +        $this->shippingAddress->expects(self::once())
                 ->method('setCollectShippingRates')
                 ->with(true)
                 ->willReturnSelf();
     
    -        $this->totalsCollector->expects(static::once())
    +        $this->totalsCollector->expects(self::once())
                 ->method('collectAddressTotals')
                 ->with($this->quote, $this->shippingAddress)
                 ->willReturnSelf();
    @@ -615,104 +601,144 @@ public function testEstimateByExtendedAddress()
             $methodObject = $this->getMockForAbstractClass(ShippingMethodInterface::class);
             $expectedRates = [$methodObject];
     
    -        $this->shippingAddress->expects(static::once())
    +        $this->shippingAddress->expects(self::once())
                 ->method('getGroupedAllShippingRates')
                 ->willReturn([[$rate]]);
     
    -        $this->quote->expects(static::once())
    +        $this->quote->expects(self::once())
                 ->method('getQuoteCurrencyCode')
                 ->willReturn($currencyCode);
     
    -        $this->converter->expects(static::once())
    +        $this->converter->expects(self::once())
                 ->method('modelToDataObject')
                 ->with($rate, $currencyCode)
                 ->willReturn($methodObject);
     
             $carriersRates = $this->model->estimateByExtendedAddress($cartId, $address);
    -        static::assertEquals($expectedRates, $carriersRates);
    +        self::assertEquals($expectedRates, $carriersRates);
         }
     
         /**
    +     * @dataProvider getAddressDataProvider
    +     *
          * @covers \Magento\Quote\Model\ShippingMethodManagement::estimateByAddressId
    +     * @param int $cartId
    +     * @param int $addressId
    +     * @param int $randomAddressId
    +     * @param bool $throwsException
          */
    -    public function testEstimateByAddressId()
    +    public function testEstimateByAddressId($cartId, $addressId, $randomAddressId, $throwsException)
         {
    -        $cartId = 1;
    -
             $addressData = [
                 'region' => 'California',
                 'region_id' => 23,
                 'country_id' => 1,
                 'postcode' => 90200,
             ];
             $currencyCode = 'UAH';
    +        $customerId = 1;
     
    -        /**
    -         * @var \Magento\Customer\Api\Data\AddressInterface|MockObject $address
    -         */
    -        $address = $this->getMockBuilder(\Magento\Customer\Api\Data\AddressInterface::class)
    +        $rate = $this->getMockBuilder(Rate::class)
                 ->disableOriginalConstructor()
    +            ->setMethods([])
                 ->getMock();
    +        $methodObject = $this->getMockForAbstractClass(ShippingMethodInterface::class);
     
    -        $this->addressRepository->expects($this->any())
    -            ->method('getById')
    -            ->willReturn($address);
    -
    -        $this->addressFactory->expects($this->any())
    -            ->method('create')
    -            ->willReturn($address);
    -
    -        $this->quoteRepository->expects(static::once())
    +        $this->quoteRepository->expects(self::once())
                 ->method('getActive')
                 ->with($cartId)
                 ->willReturn($this->quote);
     
    -        $this->quote->expects(static::once())
    +        $this->quote->expects(self::once())
                 ->method('isVirtual')
                 ->willReturn(false);
    -        $this->quote->expects(static::once())
    +
    +        $this->quote->expects(self::once())
                 ->method('getItemsCount')
                 ->willReturn(1);
     
    -        $this->quote->expects(static::once())
    -            ->method('getShippingAddress')
    -            ->willReturn($this->shippingAddress);
    -
    -        $this->dataProcessor->expects(static::any())
    -            ->method('buildOutputDataArray')
    -            ->willReturn($addressData);
    -
    -        $this->shippingAddress->expects(static::once())
    -            ->method('setCollectShippingRates')
    -            ->with(true)
    -            ->willReturnSelf();
    +        $this->setCustomerSession($addressId, $customerId);
    +        if ($throwsException) {
    +            $this->expectException('Magento\Framework\Exception\InputException');
    +            $this->expectExceptionMessage('The shipping address is missing. Set the address and try again.');
    +            $this->model->estimateByAddressId($cartId, $randomAddressId);
    +        } else {
    +            $this->quote->expects(self::once())
    +                ->method('getShippingAddress')
    +                ->willReturn($this->shippingAddress);
    +
    +            $this->dataProcessor->expects(self::any())
    +                ->method('buildOutputDataArray')
    +                ->willReturn($addressData);
    +
    +            $this->shippingAddress->expects(self::once())
    +                ->method('setCollectShippingRates')
    +                ->with(true)
    +                ->willReturnSelf();
    +
    +            $this->totalsCollector->expects(self::once())
    +                ->method('collectAddressTotals')
    +                ->with($this->quote, $this->shippingAddress)
    +                ->willReturnSelf();
    +
    +            $expectedRates = [$methodObject];
    +            $this->shippingAddress->expects(self::once())
    +                ->method('getGroupedAllShippingRates')
    +                ->willReturn([[$rate]]);
    +
    +            $this->quote->expects(self::once())
    +                ->method('getQuoteCurrencyCode')
    +                ->willReturn($currencyCode);
    +
    +            $this->converter->expects(self::once())
    +                ->method('modelToDataObject')
    +                ->with($rate, $currencyCode)
    +                ->willReturn($methodObject);
    +
    +            $carriersRates = $this->model->estimateByAddressId($cartId, $addressId);
    +            self::assertEquals($expectedRates, $carriersRates);
    +        }
    +    }
     
    -        $this->totalsCollector->expects(static::once())
    -            ->method('collectAddressTotals')
    -            ->with($this->quote, $this->shippingAddress)
    -            ->willReturnSelf();
    +    /**
    +     * @return array
    +     */
    +    public function getAddressDataProvider()
    +    {
    +        return [
    +            [1, 1, 5, true],
    +            [1, 1, 1, false],
    +        ];
    +    }
     
    -        $rate = $this->getMockBuilder(Rate::class)
    +    private function setCustomerSession($addressId, $customerId)
    +    {
    +        /**
    +         * @var \Magento\Customer\Model\Data\Address|MockObject $address
    +         */
    +        $address = $this->getMockBuilder(\Magento\Customer\Model\Data\Address::class)
    +            ->setMethods(['getId'])
                 ->disableOriginalConstructor()
    -            ->setMethods([])
                 ->getMock();
    -        $methodObject = $this->getMockForAbstractClass(ShippingMethodInterface::class);
    -        $expectedRates = [$methodObject];
    +        $address->expects($this->atLeastOnce())
    +            ->method('getId')
    +            ->willReturn($addressId);
     
    -        $this->shippingAddress->expects(static::once())
    -            ->method('getGroupedAllShippingRates')
    -            ->willReturn([[$rate]]);
    +        $this->addressRepository->expects($this->any())
    +            ->method('getById')
    +            ->willReturn($address);
     
    -        $this->quote->expects(static::once())
    -            ->method('getQuoteCurrencyCode')
    -            ->willReturn($currencyCode);
    +        $this->addressFactory->expects($this->any())
    +            ->method('create')
    +            ->willReturn($address);
     
    -        $this->converter->expects(static::once())
    -            ->method('modelToDataObject')
    -            ->with($rate, $currencyCode)
    -            ->willReturn($methodObject);
    +        $customerAddresses = [$address];
    +        $customerMock = $this->getMockForAbstractClass(CustomerInterface::class);
    +        $customerMock->method('getAddresses')->willReturn($customerAddresses);
     
    -        $carriersRates = $this->model->estimateByAddressId($cartId, $address);
    -        static::assertEquals($expectedRates, $carriersRates);
    +        $this->quote->method('getCustomer')->willReturn($customerMock);
    +        $this->customerSession->method('getCustomerId')->willReturn($customerId);
    +        $this->customerSession->expects(self::any())->method('isLoggedIn')->willReturn(true);
    +        $this->customerSession->expects(self::any())->method('getCustomerData')->willReturn($customerMock);
         }
     }
    
  • app/code/Magento/RelatedProductGraphQl/composer.json+10 8 modified
    @@ -2,19 +2,20 @@
         "name": "magento/module-related-product-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/module-catalog": "*",
    -        "magento/module-catalog-graph-ql": "*",
    -        "magento/framework": "*"
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-catalog-graph-ql": "100.4.*",
    +        "magento/framework": "103.0.*"
         },
         "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 @@
             }
         }
     }
    +
    
  • app/code/Magento/ReleaseNotification/composer.json+12 10 modified
    @@ -1,21 +1,22 @@
     {
         "name": "magento/module-release-notification",
         "description": "N/A",
    -    "require": {
    -        "php": "~7.4.0||~8.1.0",
    -        "magento/module-user": "*",
    -        "magento/module-backend": "*",
    -        "magento/module-ui": "*",
    -        "magento/framework": "*"
    -    },
    -    "suggest": {
    -        "magento/module-config": "*"
    -    },
         "type": "magento2-module",
         "license": [
             "OSL-3.0",
             "AFL-3.0"
         ],
    +    "version": "100.4.2",
    +    "require": {
    +        "php": "~7.4.0||~8.1.0",
    +        "magento/module-user": "101.2.*",
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-ui": "101.2.*",
    +        "magento/framework": "103.0.*"
    +    },
    +    "suggest": {
    +        "magento/module-config": "101.2.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -25,3 +26,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/RemoteStorage/composer.json+19 17 modified
    @@ -1,29 +1,30 @@
     {
         "name": "magento/module-remote-storage",
         "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-backend": "*",
    -        "magento/module-sitemap": "*",
    -        "magento/module-cms": "*",
    -        "magento/module-downloadable": "*",
    -        "magento/module-catalog": "*",
    -        "magento/module-media-storage": "*",
    -        "magento/module-media-gallery-metadata": "*",
    -        "magento/module-media-gallery-synchronization": "*",
    -        "magento/module-import-export": "*",
    -        "magento/module-catalog-import-export": "*",
    -        "magento/module-downloadable-import-export": "*",
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-sitemap": "100.4.*",
    +        "magento/module-cms": "104.0.*",
    +        "magento/module-downloadable": "100.4.*",
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-media-storage": "100.4.*",
    +        "magento/module-media-gallery-metadata": "100.4.*",
    +        "magento/module-media-gallery-synchronization": "100.4.*",
    +        "magento/module-import-export": "101.0.*",
    +        "magento/module-catalog-import-export": "101.1.*",
    +        "magento/module-downloadable-import-export": "100.4.*",
             "predis/predis": "*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -33,3 +34,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Reports/composer.json+25 23 modified
    @@ -1,35 +1,36 @@
     {
         "name": "magento/module-reports",
         "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-inventory": "*",
    -        "magento/module-cms": "*",
    -        "magento/module-config": "*",
    -        "magento/module-customer": "*",
    -        "magento/module-downloadable": "*",
    -        "magento/module-eav": "*",
    -        "magento/module-quote": "*",
    -        "magento/module-review": "*",
    -        "magento/module-sales": "*",
    -        "magento/module-sales-rule": "*",
    -        "magento/module-store": "*",
    -        "magento/module-tax": "*",
    -        "magento/module-widget": "*",
    -        "magento/module-wishlist": "*",
    -        "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-cms": "104.0.*",
    +        "magento/module-config": "101.2.*",
    +        "magento/module-customer": "103.0.*",
    +        "magento/module-downloadable": "100.4.*",
    +        "magento/module-eav": "102.1.*",
    +        "magento/module-quote": "101.2.*",
    +        "magento/module-review": "100.4.*",
    +        "magento/module-sales": "103.0.*",
    +        "magento/module-sales-rule": "101.2.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/module-tax": "100.4.*",
    +        "magento/module-widget": "101.2.*",
    +        "magento/module-wishlist": "101.2.*",
    +        "magento/module-directory": "100.4.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -39,3 +40,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/RequireJs/composer.json+8 6 modified
    @@ -1,18 +1,19 @@
     {
         "name": "magento/module-require-js",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "100.4.1",
         "require": {
             "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*"
    +        "magento/framework": "103.0.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -22,3 +23,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/ReviewAnalytics/composer.json+8 6 modified
    @@ -1,17 +1,18 @@
     {
         "name": "magento/module-review-analytics",
         "description": "N/A",
    -    "require": {
    -        "php": "~7.4.0||~8.1.0",
    -        "magento/framework": "*",
    -        "magento/module-review": "*",
    -        "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-review": "100.4.*",
    +        "magento/module-analytics": "100.4.*"
    +    },
         "autoload": {
             "files": [
                 "registration.php"
    @@ -21,3 +22,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Review/composer.json+18 16 modified
    @@ -1,30 +1,31 @@
     {
         "name": "magento/module-review",
         "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-customer": "*",
    -        "magento/module-eav": "*",
    -        "magento/module-newsletter": "*",
    -        "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-customer": "103.0.*",
    +        "magento/module-eav": "102.1.*",
    +        "magento/module-newsletter": "100.4.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/module-theme": "101.1.*",
    +        "magento/module-ui": "101.2.*"
         },
         "suggest": {
    -        "magento/module-cookie": "*",
    -        "magento/module-review-sample-data": "*"
    +        "magento/module-cookie": "100.4.*",
    +        "magento/module-review-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 @@
             }
         }
     }
    +
    
  • app/code/Magento/ReviewGraphQl/composer.json+12 10 modified
    @@ -2,21 +2,22 @@
         "name": "magento/module-review-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/module-catalog": "*",
    -        "magento/module-review": "*",
    -        "magento/module-store": "*",
    -        "magento/framework": "*"
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-review": "100.4.*",
    +        "magento/module-store": "101.1.*",
    +        "magento/framework": "103.0.*"
         },
         "suggest": {
    -        "magento/module-graph-ql": "*",
    -        "magento/module-graph-ql-cache": "*"
    +        "magento/module-graph-ql": "100.4.*",
    +        "magento/module-graph-ql-cache": "100.4.*"
         },
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -26,3 +27,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Robots/composer.json+10 8 modified
    @@ -1,22 +1,23 @@
     {
         "name": "magento/module-robots",
         "description": "N/A",
    +    "type": "magento2-module",
    +    "license": [
    +        "OSL-3.0",
    +        "AFL-3.0"
    +    ],
         "config": {
             "sort-packages": true
         },
    +    "version": "101.1.1",
         "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-theme": "*"
    +        "magento/module-theme": "101.1.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -26,3 +27,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Rss/composer.json+11 9 modified
    @@ -1,21 +1,22 @@
     {
         "name": "magento/module-rss",
         "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-backend": "*",
    -        "magento/module-customer": "*",
    -        "magento/module-store": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-customer": "103.0.*",
    +        "magento/module-store": "101.1.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -25,3 +26,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Rule/composer.json+12 10 modified
    @@ -1,23 +1,24 @@
     {
         "name": "magento/module-rule",
         "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",
             "lib-libxml": "*",
    -        "magento/framework": "*",
    -        "magento/module-backend": "*",
    -        "magento/module-catalog": "*",
    -        "magento/module-eav": "*",
    -        "magento/module-store": "*"
    +        "magento/framework": "103.0.*",
    +        "magento/module-backend": "102.0.*",
    +        "magento/module-catalog": "104.0.*",
    +        "magento/module-eav": "102.1.*",
    +        "magento/module-store": "101.1.*"
         },
    -    "type": "magento2-module",
    -    "license": [
    -        "OSL-3.0",
    -        "AFL-3.0"
    -    ],
         "autoload": {
             "files": [
                 "registration.php"
    @@ -27,3 +28,4 @@
             }
         }
     }
    +
    
  • app/code/Magento/Sales/Controller/Adminhtml/Order/AddComment.php+4 5 modified
    @@ -20,12 +20,12 @@ class AddComment extends \Magento\Sales\Controller\Adminhtml\Order implements Ht
          *
          * @see _isAllowed()
          */
    -    const ADMIN_RESOURCE = 'Magento_Sales::comment';
    +    public const ADMIN_RESOURCE = 'Magento_Sales::comment';
     
         /**
          * ACL resource needed to send comment email notification
          */
    -    const ADMIN_SALES_EMAIL_RESOURCE = 'Magento_Sales::emails';
    +    public const ADMIN_SALES_EMAIL_RESOURCE = 'Magento_Sales::emails';
     
         /**
          * Add order comment action
    @@ -52,13 +52,12 @@ public function execute()
                         $notify = false;
                     }
     
    -                $history = $order->addStatusHistoryComment($data['comment'], $data['status']);
    +                $comment = trim(strip_tags($data['comment']));
    +                $history = $order->addStatusHistoryComment($comment, $data['status']);
                     $history->setIsVisibleOnFront($visible);
                     $history->setIsCustomerNotified($notify);
                     $history->save();
     
    -                $comment = trim(strip_tags($data['comment']));
    -
                     $order->save();
                     /** @var OrderCommentSender $orderCommentSender */
                     $orderCommentSender = $this->_objectManager
    

Vulnerability mechanics

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

References

9

News mentions

0

No linked articles in our index yet.