CVE-2024-2032 (Python, published 2024-06-06T19:15Z)
Description: value: A race condition vulnerability exists in zenml-io/zenml versions up to and including 0.55.3, which allows for the creation of multiple users with the same username when requests are sent in parallel. This issue was fixed in version 0.55.5. The vulnerability arises due to insufficient handling of concurrent user creation requests, leading to data inconsistencies and potential authentication problems. Specifically, concurrent processes may overwrite or corrupt user data, complicating user identification and posing security risks. This issue is particularly concerning for APIs that rely on usernames as input parameters, such as PUT /api/v1/users/test_race, where it could lead to further complications.
Severity: nan (CVSS v3 base score: )
Weakness: CWE-366 \u2014 Race Condition within a Thread
If two threads of execution use a resource simultaneously, there exists the possibility that resources may be used while invalid, in turn making the state of execution undefined.
Repository: https://github.com/zenml-io/zenml
Files changed:
src/zenml/zen_stores/migrations/versions/72675226b2de_unique_users.py
src/zenml/zen_stores/schemas/user_schemas.py
src/zenml/zen_stores/sql_zen_store.py
tests/integration/functional/zen_stores/test_zen_store.py
Commit message: Uniquely constrained users table (#2483)

* use db constrains for users

* lint a bit

* coderabbitai

* set rate limits for login api

* Revert "set rate limits for login api"

This reverts commit 78ce40e738e73ce7d9398c73559f3d29a502c24f.

* bring checks back

* Auto-update of Starter template

* Auto-update of NLP template

* review suggestions

* resolve branching

* Auto-update of Starter template

* Auto-update of NLP template

* lint

---------

Co-authored-by: GitHub Actions <actions@github.com>

Vulnerable code:
from sqlalchemy import TEXT, Column
            # Check if a service account with the given name already
            # exists
            service_accounts = session.execute(
                select(UserSchema).where(
                    UserSchema.name == service_account.name,
                    UserSchema.is_service_account.is_(True),  # type: ignore[attr-defined]
                )
            ).fetchall()
            if len(service_accounts) == 1:
                session.commit()
            else:
                raise EntityExistsError(
                    f"Unable to create service account with name "
                    f"'{service_account.name}': Found existing service "
                    "account with this name."
                )

            # Check if a user account with the given name already exists
            users = session.execute(
                select(UserSchema).where(
                    UserSchema.name == user.name,
                    UserSchema.is_service_account.is_(False),  # type: ignore[attr-defined]
                )
            ).fetchall()
            if len(users) == 1:
                session.commit()
            else:
                raise EntityExistsError(
                    f"Unable to create user with name '{user.name}': "
                    f"Found an existing user account with this name."
                )
        except EntityExistsError:
        except EntityExistsError:

Fixed code:
"""unique users [72675226b2de].

Revision ID: 72675226b2de
Revises: 0.55.4
Create Date: 2024-02-29 14:58:25.584731

"""

from alembic import op

# revision identifiers, used by Alembic.
revision = "72675226b2de"
down_revision = "0.55.4"
branch_labels = None
depends_on = None


def upgrade() -> None:
    """Upgrade database schema and/or data, creating a new revision."""
    with op.batch_alter_table("user", schema=None) as batch_op:
        batch_op.create_unique_constraint(
            "uq_user_name_is_service_account", ["name", "is_service_account"]
        )


def downgrade() -> None:
    """Downgrade database schema and/or data back to the previous revision."""
    with op.batch_alter_table("user", schema=None) as batch_op:
        batch_op.drop_constraint(
            "uq_user_name_is_service_account", type_="unique"
        )
from sqlalchemy import TEXT, Column, UniqueConstraint
    __table_args__ = (UniqueConstraint("name", "is_service_account"),)
            # Check if a service account with the given name already
            # exists
            err_msg = (
                f"Unable to create service account with name "
                f"'{service_account.name}': Found existing service "
                "account with this name."
            )
            try:
                self._get_account_schema(
                    service_account.name, session=session, service_account=True
                )
                raise EntityExistsError(err_msg)
            except KeyError:
                pass

            # on commit an IntegrityError may arise we let it bubble up
            session.commit()
            # Check if a user account with the given name already exists
            err_msg = (
                f"Unable to create user with name '{user.name}': "
                f"Found an existing user account with this name."
            )
            try:
                self._get_account_schema(
                    user.name,
                    session=session,
                    # Filter out service accounts
                    service_account=False,
                )
                raise EntityExistsError(err_msg)
            except KeyError:
                pass

            # on commit an IntegrityError may arise we let it bubble up
            session.commit()
from sqlalchemy.exc import IntegrityError
        except (EntityExistsError, IntegrityError):
        except (EntityExistsError, IntegrityError):