diff --git a/CHANGELOG-WIP.md b/CHANGELOG-WIP.md index 19a3a847a7..cc9508c442 100644 --- a/CHANGELOG-WIP.md +++ b/CHANGELOG-WIP.md @@ -1,16 +1,27 @@ # WIP Release notes for Commerce 5.6 +### Development - Cart controller actions that accept an explicit cart number are now rate limited to mitigate enumeration attacks. - Cart numbers are now generated using a cryptographically secure random number generator. -- Fixed a bug where Variant with empty SKUs didn't show a validation error when saving a product after it was duplicated. ([#4197](https://github.com/craftcms/commerce/issues/4197)) - Shipping rule categories are now eager loaded on shipping rules automatically. ([#4220](https://github.com/craftcms/commerce/issues/4220)) -- Added `craft\commerce\services\ShippingRuleCategories::getShippingRuleCategoriesByRuleIds()`. -- Added `variantUiLabelFormat` and `productUiLabelFormat` settings to product types, for customizing how products and variants are labeled throughout the control panel. ([#4178](https://github.com/craftcms/commerce/pull/4178)) -- Added `relatedToProducts` and `relatedToVariants` GraphQL query arguments, enabling queries for elements related to specific products or variants. ([#4202](https://github.com/craftcms/commerce/discussions/4202)) + +### Extensibility - Added `craft\commerce\elements\db\ProductQuery::$savable`. - Added `craft\commerce\elements\db\ProductQuery::savable()`. - Added `craft\commerce\elements\db\VariantQuery::$savable`. -- Added `craft\commerce\elements\db\VariantQuery::savable()`. - Added `craft\commerce\elements\db\VariantQuery::editable()`. +- Added `craft\commerce\elements\db\VariantQuery::savable()`. +- Added `craft\commerce\helpers\ProductQuery::cleanseQueryCriteria()`. +- Added `craft\commerce\services\ShippingRuleCategories::getShippingRuleCategoriesByRuleIds()`. +- Added `craft\commerce\services\ShippingRuleCategories::getShippingRuleCategoriesByRuleIds()`. +- Added `relatedToProducts` and `relatedToVariants` GraphQL query arguments, enabling queries for elements related to specific products or variants. ([#4202](https://github.com/craftcms/commerce/discussions/4202)) +- Added `variantUiLabelFormat` and `productUiLabelFormat` settings to product types, for customizing how products and variants are labeled throughout the control panel. ([#4178](https://github.com/craftcms/commerce/pull/4178)) - `craft\commerce\elements\db\ProductQuery::$editable` is now nullable. - `craft\commerce\elements\db\VariantQuery::$editable` is now nullable. + +### System +- Craft Commerce now requires Craft CMS 5.9.9 or later. +- Fixed a bug where Variant with empty SKUs didn't show a validation error when saving a product after it was duplicated. ([#4197](https://github.com/craftcms/commerce/issues/4197)) +- Fixed a SQL error that could occur when querying for unfulfilled orders on PostgreSQL. ([#4228](https://github.com/craftcms/commerce/issues/4228)) +- Fixed an error that could occur when resaving variants. ([#4226](https://github.com/craftcms/commerce/issues/4226)) +- Fixed [high-severity](https://github.com/craftcms/cms/security/policy#severity--remediation) SQL injection vulnerabilities in the control panel. diff --git a/CHANGELOG.md b/CHANGELOG.md index 1b227b66ee..91cea37fbf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ - Fixed an error that could occur when editing inventory locations. ([#4233](https://github.com/craftcms/commerce/issues/4233)) - Fixed a SQL error that could occur when querying for unfulfilled orders on PostgreSQL. ([#4228](https://github.com/craftcms/commerce/issues/4228)) - Fixed an error that could occur when resaving variants. ([#4226](https://github.com/craftcms/commerce/issues/4226)) +- Fixed [high-severity](https://github.com/craftcms/cms/security/policy#severity--remediation) SQL injection vulnerabilities in the control panel. +- Added `craft\commerce\helpers\ProductQuery::cleanseQueryCriteria()`. ## 5.5.3 - 2026-02-09 diff --git a/composer.json b/composer.json index 58d3c12e6d..1675852421 100644 --- a/composer.json +++ b/composer.json @@ -22,7 +22,7 @@ "prefer-stable": true, "require": { "php": "^8.2", - "craftcms/cms": "^5.9.0", + "craftcms/cms": "^5.9.9", "dompdf/dompdf": "^2.0.2", "ibericode/vat": "^1.2.2", "iio/libmergepdf": "^4.0", diff --git a/composer.lock b/composer.lock index 6c9e38fe2d..5d7460400e 100644 --- a/composer.lock +++ b/composer.lock @@ -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": "5722c0136166cfedca244fa3fc58d187", + "content-hash": "8e3d9e9078db15f2d323c4001d063d0f", "packages": [ { "name": "bacon/bacon-qr-code", @@ -62,16 +62,16 @@ }, { "name": "brick/math", - "version": "0.14.6", + "version": "0.14.8", "source": { "type": "git", "url": "https://github.com/brick/math.git", - "reference": "32498d5e1897e7642c0b961ace2df6d7dc9a3bc3" + "reference": "63422359a44b7f06cae63c3b429b59e8efcc0629" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/brick/math/zipball/32498d5e1897e7642c0b961ace2df6d7dc9a3bc3", - "reference": "32498d5e1897e7642c0b961ace2df6d7dc9a3bc3", + "url": "https://api.github.com/repos/brick/math/zipball/63422359a44b7f06cae63c3b429b59e8efcc0629", + "reference": "63422359a44b7f06cae63c3b429b59e8efcc0629", "shasum": "" }, "require": { @@ -110,7 +110,7 @@ ], "support": { "issues": "https://github.com/brick/math/issues", - "source": "https://github.com/brick/math/tree/0.14.6" + "source": "https://github.com/brick/math/tree/0.14.8" }, "funding": [ { @@ -118,7 +118,7 @@ "type": "github" } ], - "time": "2026-02-05T07:59:58+00:00" + "time": "2026-02-10T14:33:43+00:00" }, { "name": "carbonphp/carbon-doctrine-types", @@ -475,16 +475,16 @@ }, { "name": "craftcms/cms", - "version": "5.9.6", + "version": "5.9.10", "source": { "type": "git", "url": "https://github.com/craftcms/cms.git", - "reference": "558372701d7870da4a3cda88592a21d962de0cb1" + "reference": "2f5149d4d64a5dafae9db99357823655422ba77d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/craftcms/cms/zipball/558372701d7870da4a3cda88592a21d962de0cb1", - "reference": "558372701d7870da4a3cda88592a21d962de0cb1", + "url": "https://api.github.com/repos/craftcms/cms/zipball/2f5149d4d64a5dafae9db99357823655422ba77d", + "reference": "2f5149d4d64a5dafae9db99357823655422ba77d", "shasum": "" }, "require": { @@ -601,7 +601,7 @@ "rss": "https://github.com/craftcms/cms/releases.atom", "source": "https://github.com/craftcms/cms" }, - "time": "2026-02-03T16:48:37+00:00" + "time": "2026-02-13T00:01:18+00:00" }, { "name": "craftcms/plugin-installer", @@ -880,29 +880,29 @@ }, { "name": "doctrine/deprecations", - "version": "1.1.5", + "version": "1.1.6", "source": { "type": "git", "url": "https://github.com/doctrine/deprecations.git", - "reference": "459c2f5dd3d6a4633d3b5f46ee2b1c40f57d3f38" + "reference": "d4fe3e6fd9bb9e72557a19674f44d8ac7db4c6ca" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/deprecations/zipball/459c2f5dd3d6a4633d3b5f46ee2b1c40f57d3f38", - "reference": "459c2f5dd3d6a4633d3b5f46ee2b1c40f57d3f38", + "url": "https://api.github.com/repos/doctrine/deprecations/zipball/d4fe3e6fd9bb9e72557a19674f44d8ac7db4c6ca", + "reference": "d4fe3e6fd9bb9e72557a19674f44d8ac7db4c6ca", "shasum": "" }, "require": { "php": "^7.1 || ^8.0" }, "conflict": { - "phpunit/phpunit": "<=7.5 || >=13" + "phpunit/phpunit": "<=7.5 || >=14" }, "require-dev": { - "doctrine/coding-standard": "^9 || ^12 || ^13", - "phpstan/phpstan": "1.4.10 || 2.1.11", + "doctrine/coding-standard": "^9 || ^12 || ^14", + "phpstan/phpstan": "1.4.10 || 2.1.30", "phpstan/phpstan-phpunit": "^1.0 || ^2", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.6 || ^10.5 || ^11.5 || ^12", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.6 || ^10.5 || ^11.5 || ^12.4 || ^13.0", "psr/log": "^1 || ^2 || ^3" }, "suggest": { @@ -922,9 +922,9 @@ "homepage": "https://www.doctrine-project.org/", "support": { "issues": "https://github.com/doctrine/deprecations/issues", - "source": "https://github.com/doctrine/deprecations/tree/1.1.5" + "source": "https://github.com/doctrine/deprecations/tree/1.1.6" }, - "time": "2025-04-07T20:06:18+00:00" + "time": "2026-02-07T07:09:04+00:00" }, { "name": "doctrine/inflector", @@ -7590,16 +7590,16 @@ }, { "name": "webmozart/assert", - "version": "2.1.2", + "version": "2.1.3", "source": { "type": "git", "url": "https://github.com/webmozarts/assert.git", - "reference": "ce6a2f100c404b2d32a1dd1270f9b59ad4f57649" + "reference": "6976757ba8dd70bf8cbaea0914ad84d8b51a9f46" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webmozarts/assert/zipball/ce6a2f100c404b2d32a1dd1270f9b59ad4f57649", - "reference": "ce6a2f100c404b2d32a1dd1270f9b59ad4f57649", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/6976757ba8dd70bf8cbaea0914ad84d8b51a9f46", + "reference": "6976757ba8dd70bf8cbaea0914ad84d8b51a9f46", "shasum": "" }, "require": { @@ -7646,9 +7646,9 @@ ], "support": { "issues": "https://github.com/webmozarts/assert/issues", - "source": "https://github.com/webmozarts/assert/tree/2.1.2" + "source": "https://github.com/webmozarts/assert/tree/2.1.3" }, - "time": "2026-01-13T14:02:24+00:00" + "time": "2026-02-13T21:01:40+00:00" }, { "name": "webonyx/graphql-php", @@ -8419,22 +8419,22 @@ }, { "name": "codeception/lib-asserts", - "version": "3.1.0", + "version": "3.2.0", "source": { "type": "git", "url": "https://github.com/Codeception/lib-asserts.git", - "reference": "8e161f38a71cdf3dc638c5427df21c0f01f12d13" + "reference": "f161e5d3a9e5ae573ca01cfb3b5601ff5303df03" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Codeception/lib-asserts/zipball/8e161f38a71cdf3dc638c5427df21c0f01f12d13", - "reference": "8e161f38a71cdf3dc638c5427df21c0f01f12d13", + "url": "https://api.github.com/repos/Codeception/lib-asserts/zipball/f161e5d3a9e5ae573ca01cfb3b5601ff5303df03", + "reference": "f161e5d3a9e5ae573ca01cfb3b5601ff5303df03", "shasum": "" }, "require": { "ext-dom": "*", "php": "^8.2 || ^8.3 || ^8.4 || ^8.5", - "phpunit/phpunit": "^11.5 || ^12.0" + "phpunit/phpunit": "^11.5 || ^12.0 || ^13.0" }, "type": "library", "autoload": { @@ -8467,9 +8467,9 @@ ], "support": { "issues": "https://github.com/Codeception/lib-asserts/issues", - "source": "https://github.com/Codeception/lib-asserts/tree/3.1.0" + "source": "https://github.com/Codeception/lib-asserts/tree/3.2.0" }, - "time": "2025-12-22T08:25:07+00:00" + "time": "2026-02-06T15:19:32+00:00" }, { "name": "codeception/lib-innerbrowser", @@ -8532,23 +8532,23 @@ }, { "name": "codeception/lib-web", - "version": "2.0.1", + "version": "2.1.0", "source": { "type": "git", "url": "https://github.com/Codeception/lib-web.git", - "reference": "bbec12e789c3b810ec8cb86e5f46b5bfd673c441" + "reference": "a030a3a22fc8e856b5957086794ed5403c7992d9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Codeception/lib-web/zipball/bbec12e789c3b810ec8cb86e5f46b5bfd673c441", - "reference": "bbec12e789c3b810ec8cb86e5f46b5bfd673c441", + "url": "https://api.github.com/repos/Codeception/lib-web/zipball/a030a3a22fc8e856b5957086794ed5403c7992d9", + "reference": "a030a3a22fc8e856b5957086794ed5403c7992d9", "shasum": "" }, "require": { "ext-mbstring": "*", "guzzlehttp/psr7": "^2.0", "php": "^8.2", - "phpunit/phpunit": "^11.5 | ^12", + "phpunit/phpunit": "^11.5 | ^12 | ^13", "symfony/css-selector": ">=4.4.24 <9.0" }, "conflict": { @@ -8579,9 +8579,9 @@ ], "support": { "issues": "https://github.com/Codeception/lib-web/issues", - "source": "https://github.com/Codeception/lib-web/tree/2.0.1" + "source": "https://github.com/Codeception/lib-web/tree/2.1.0" }, - "time": "2025-11-27T21:09:09+00:00" + "time": "2026-02-06T15:22:13+00:00" }, { "name": "codeception/lib-xml", @@ -8925,27 +8925,27 @@ }, { "name": "codeception/stub", - "version": "4.2.1", + "version": "4.3.0", "source": { "type": "git", "url": "https://github.com/Codeception/Stub.git", - "reference": "0c573cd5c62a828dadadc41bc56f8434860bb7bb" + "reference": "6305b97eaf6ea9bdaed29a5bd4d6f2948f577d8f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Codeception/Stub/zipball/0c573cd5c62a828dadadc41bc56f8434860bb7bb", - "reference": "0c573cd5c62a828dadadc41bc56f8434860bb7bb", + "url": "https://api.github.com/repos/Codeception/Stub/zipball/6305b97eaf6ea9bdaed29a5bd4d6f2948f577d8f", + "reference": "6305b97eaf6ea9bdaed29a5bd4d6f2948f577d8f", "shasum": "" }, "require": { "php": "^8.1", - "phpunit/phpunit": "^8.4 | ^9.0 | ^10.0 | ^11 | ^12" + "phpunit/phpunit": "^8.4 | ^9.0 | ^10.0 | ^11 | ^12 | ^13" }, "conflict": { "codeception/codeception": "<5.0.6" }, "require-dev": { - "consolidation/robo": "^3.0" + "consolidation/robo": "^4.0" }, "type": "library", "autoload": { @@ -8960,9 +8960,9 @@ "description": "Flexible Stub wrapper for PHPUnit's Mock Builder", "support": { "issues": "https://github.com/Codeception/Stub/issues", - "source": "https://github.com/Codeception/Stub/tree/4.2.1" + "source": "https://github.com/Codeception/Stub/tree/4.3.0" }, - "time": "2025-12-05T13:37:14+00:00" + "time": "2026-02-06T15:19:04+00:00" }, { "name": "composer/ca-bundle", @@ -9531,16 +9531,16 @@ }, { "name": "justinrainbow/json-schema", - "version": "6.6.4", + "version": "v6.7.2", "source": { "type": "git", "url": "https://github.com/jsonrainbow/json-schema.git", - "reference": "2eeb75d21cf73211335888e7f5e6fd7440723ec7" + "reference": "6fea66c7204683af437864e7c4e7abf383d14bc0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/jsonrainbow/json-schema/zipball/2eeb75d21cf73211335888e7f5e6fd7440723ec7", - "reference": "2eeb75d21cf73211335888e7f5e6fd7440723ec7", + "url": "https://api.github.com/repos/jsonrainbow/json-schema/zipball/6fea66c7204683af437864e7c4e7abf383d14bc0", + "reference": "6fea66c7204683af437864e7c4e7abf383d14bc0", "shasum": "" }, "require": { @@ -9600,9 +9600,9 @@ ], "support": { "issues": "https://github.com/jsonrainbow/json-schema/issues", - "source": "https://github.com/jsonrainbow/json-schema/tree/6.6.4" + "source": "https://github.com/jsonrainbow/json-schema/tree/v6.7.2" }, - "time": "2025-12-19T15:01:32+00:00" + "time": "2026-02-15T15:06:22+00:00" }, { "name": "league/factory-muffin", @@ -10847,16 +10847,16 @@ }, { "name": "phpunit/phpunit", - "version": "11.5.51", + "version": "11.5.53", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "ad14159f92910b0f0e3928c13e9b2077529de091" + "reference": "a997a653a82845f1240d73ee73a8a4e97e4b0607" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/ad14159f92910b0f0e3928c13e9b2077529de091", - "reference": "ad14159f92910b0f0e3928c13e9b2077529de091", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/a997a653a82845f1240d73ee73a8a4e97e4b0607", + "reference": "a997a653a82845f1240d73ee73a8a4e97e4b0607", "shasum": "" }, "require": { @@ -10929,7 +10929,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/11.5.51" + "source": "https://github.com/sebastianbergmann/phpunit/tree/11.5.53" }, "funding": [ { @@ -10953,20 +10953,20 @@ "type": "tidelift" } ], - "time": "2026-02-05T07:59:30+00:00" + "time": "2026-02-10T12:28:25+00:00" }, { "name": "psy/psysh", - "version": "v0.12.19", + "version": "v0.12.20", "source": { "type": "git", "url": "https://github.com/bobthecow/psysh.git", - "reference": "a4f766e5c5b6773d8399711019bb7d90875a50ee" + "reference": "19678eb6b952a03b8a1d96ecee9edba518bb0373" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/bobthecow/psysh/zipball/a4f766e5c5b6773d8399711019bb7d90875a50ee", - "reference": "a4f766e5c5b6773d8399711019bb7d90875a50ee", + "url": "https://api.github.com/repos/bobthecow/psysh/zipball/19678eb6b952a03b8a1d96ecee9edba518bb0373", + "reference": "19678eb6b952a03b8a1d96ecee9edba518bb0373", "shasum": "" }, "require": { @@ -11030,9 +11030,9 @@ ], "support": { "issues": "https://github.com/bobthecow/psysh/issues", - "source": "https://github.com/bobthecow/psysh/tree/v0.12.19" + "source": "https://github.com/bobthecow/psysh/tree/v0.12.20" }, - "time": "2026-01-30T17:33:13+00:00" + "time": "2026-02-11T15:05:28+00:00" }, { "name": "rector/rector", @@ -12642,5 +12642,5 @@ "php": "^8.2" }, "platform-dev": {}, - "plugin-api-version": "2.9.0" + "plugin-api-version": "2.6.0" } diff --git a/src/controllers/CartController.php b/src/controllers/CartController.php index 61c3640a21..cc86b1da4c 100644 --- a/src/controllers/CartController.php +++ b/src/controllers/CartController.php @@ -96,9 +96,7 @@ public function behaviors(): array 'limit' => 1, 'window' => 1, // Only apply rate limiting when a cart number is explicitly passed - 'active' => function(Context $context, $rateLimitId) { - return $context->request->getBodyParam('number') || $context->request->getQueryParam('number'); - }, + 'active' => fn(Context $context, $rateLimitId) => $context->request->getBodyParam('number') || $context->request->getQueryParam('number'), 'identifier' => fn(Context $context, $rateLimitId) => sprintf( '%s:%s', $rateLimitId, diff --git a/src/elements/db/ProductQuery.php b/src/elements/db/ProductQuery.php index d38e099c63..168c9c7767 100644 --- a/src/elements/db/ProductQuery.php +++ b/src/elements/db/ProductQuery.php @@ -976,7 +976,10 @@ private function _applyHasVariantParam(): void $variantQuery = $this->hasVariant; } elseif (is_array($this->hasVariant)) { $query = Variant::find(); - $variantQuery = Craft::configure($query, $this->hasVariant); + + $criteria = ProductQueryHelper::cleanseQueryCriteria($this->hasVariant); + + $variantQuery = Craft::configure($query, $criteria); } else { throw new QueryAbortedException('Invalid param used. ProductQuery::hasVariant param only expects a variant query or variant query config.'); } diff --git a/src/elements/db/VariantQuery.php b/src/elements/db/VariantQuery.php index 4f5a2ebe1a..9ecd9fa699 100755 --- a/src/elements/db/VariantQuery.php +++ b/src/elements/db/VariantQuery.php @@ -831,7 +831,10 @@ private function _applyHasProductParam(): void $productQuery = $this->hasProduct; } elseif (is_array($this->hasProduct)) { $productQuery = Product::find(); - $productQuery = Craft::configure($productQuery, $this->hasProduct); + + $criteria = ProductQueryHelper::cleanseQueryCriteria($this->hasProduct); + + $productQuery = Craft::configure($productQuery, $criteria); } else { return; } diff --git a/src/helpers/ProductQuery.php b/src/helpers/ProductQuery.php index 3bb80ab870..d557e28e95 100644 --- a/src/helpers/ProductQuery.php +++ b/src/helpers/ProductQuery.php @@ -7,9 +7,13 @@ namespace craft\commerce\helpers; +use Craft; use craft\base\Element; use craft\commerce\elements\Product; +use craft\controllers\ElementIndexesController; +use craft\controllers\ElementSearchController; use craft\helpers\Db; +use craft\helpers\ElementHelper; use DateTime; /** @@ -80,4 +84,20 @@ public static function statusCondition(string $status, string $tablePrefix = '') default => false, }; } + + /** + * @param array $criteria + * @return array + * @since 5.6.0 + */ + public static function cleanseQueryCriteria(array $criteria): array + { + // Figure out if creating the query has come from a request where params are passed to a controller action + $controller = Craft::$app->controller; + if ($controller instanceof ElementIndexesController || $controller instanceof ElementSearchController) { + $criteria = ElementHelper::cleanseQueryCriteria($criteria); + } + + return $criteria; + } }