CVE-2022-39275: Incorrect Authorization (CWE-863)
value: Saleor is a headless, GraphQL commerce platform. In affected versions some GraphQL mutations were not properly checking the ID type input which allowed to access database objects that the authenticated user may not be allowed to access. This vulnerability can be used to expose the following information: Estimating database row counts from tables with a sequential primary key or Exposing staff user and customer email addresses and full name through the `assignNavigation()` mutation. This issue has been patched in main and backported to multiple releases (3.7.17, 3.6.18, 3.5.23, 3.4.24, 3.3.26, 3.2.14, 3.1.24). Users are advised to upgrade. There are no known workarounds for this issue.
CWE description: The product performs an authorization check when an actor attempts to access a resource or perform an action, but it does not correctly perform the check. This allows attackers to bypass intended access restrictions.
CVSS v3 base score: 4.3
Published: 2022-10-06T18:16Z
Repository: https://github.com/saleor/saleor
Language: Python
Files changed: saleor/graphql/account/mutations/account.py, saleor/graphql/account/mutations/base.py, saleor/graphql/menu/mutations.py, saleor/graphql/product/bulk_mutations/products.py
Commit date: 2022-10-03 14:04:47 +0200
Commit message: Merge pull request from GHSA-xhq8-8c5v-w8ff

* Fix improper ID type validation

* Fix failing test cases

- `ProductVariantBulkCreate` mutation was not able to determine whether Product type ID was passed
- `ShippingPriceTranslate` mutation uses a QuerySet with ID type ShippingMethodType
Diff: diff --git a/saleor/graphql/account/mutations/account.py b/saleor/graphql/account/mutations/account.py
index f57a9346c0..6b72efe2f8 100644
--- a/saleor/graphql/account/mutations/account.py
+++ b/saleor/graphql/account/mutations/account.py
@@ -386,11 +386,11 @@ class AccountSetDefaultAddress(BaseMutation):
         error_type_field = "account_errors"
         permissions = (AuthorizationFilters.AUTHENTICATED_USER,)
 
     @classmethod
     def perform_mutation(cls, _root, info, **data):
-        address = cls.get_node_or_error(info, data.get("id"), Address)
+        address = cls.get_node_or_error(info, data.get("id"), only_type=Address)
         user = info.context.user
 
         if not user.addresses.filter(pk=address.pk).exists():
             raise ValidationError(
                 {
diff --git a/saleor/graphql/account/mutations/base.py b/saleor/graphql/account/mutations/base.py
index 3309f81dad..49f1ae7a34 100644
--- a/saleor/graphql/account/mutations/base.py
+++ b/saleor/graphql/account/mutations/base.py
@@ -367,11 +367,11 @@ class BaseAddressDelete(ModelDeleteMutation):
     def perform_mutation(cls, _root, info, **data):
         if not cls.check_permissions(info.context):
             raise PermissionDenied()
 
         node_id = data.get("id")
-        instance = cls.get_node_or_error(info, node_id, Address)
+        instance = cls.get_node_or_error(info, node_id, only_type=Address)
         if instance:
             cls.clean_instance(info, instance)
 
         db_id = instance.id
 
diff --git a/saleor/graphql/menu/mutations.py b/saleor/graphql/menu/mutations.py
index 029f6072a4..6b58dece60 100644
--- a/saleor/graphql/menu/mutations.py
+++ b/saleor/graphql/menu/mutations.py
@@ -512,11 +512,11 @@ class AssignNavigation(BaseMutation):
 
     @classmethod
     def perform_mutation(cls, _root, info, navigation_type, menu=None):
         site = load_site(info.context)
         if menu is not None:
-            menu = cls.get_node_or_error(info, menu, field="menu")
+            menu = cls.get_node_or_error(info, menu, field="menu", only_type=Menu)
 
         if navigation_type == NavigationType.MAIN:
             site.settings.top_menu = menu
             site.settings.save(update_fields=["top_menu"])
         elif navigation_type == NavigationType.SECONDARY:
diff --git a/saleor/graphql/product/bulk_mutations/products.py b/saleor/graphql/product/bulk_mutations/products.py
index f05f7393c3..d5533c6af3 100644
--- a/saleor/graphql/product/bulk_mutations/products.py
+++ b/saleor/graphql/product/bulk_mutations/products.py
@@ -545,11 +545,11 @@ class ProductVariantBulkCreate(BaseMutation):
         create_stocks(variant, stocks, warehouses)
 
     @classmethod
     @traced_atomic_transaction()
     def perform_mutation(cls, _root, info, **data):
-        product = cls.get_node_or_error(info, data["product_id"], models.Product)
+        product = cls.get_node_or_error(info, data["product_id"], only_type="Product")
         errors = defaultdict(list)
 
         cleaned_inputs = cls.clean_variants(info, data["variants"], product, errors)
         instances = cls.create_variants(info, cleaned_inputs, product, errors)
         if errors:

Vulnerable code:         address = cls.get_node_or_error(info, data.get("id"), Address)
        instance = cls.get_node_or_error(info, node_id, Address)
            menu = cls.get_node_or_error(info, menu, field="menu")
        product = cls.get_node_or_error(info, data["product_id"], models.Product)
Fixed code:         address = cls.get_node_or_error(info, data.get("id"), only_type=Address)
        instance = cls.get_node_or_error(info, node_id, only_type=Address)
            menu = cls.get_node_or_error(info, menu, field="menu", only_type=Menu)
        product = cls.get_node_or_error(info, data["product_id"], only_type="Product")