From b7631d1b91e264345fb27e8ec765f1029e08b0c8 Mon Sep 17 00:00:00 2001 From: Tessa Walsh Date: Wed, 26 Jun 2024 15:04:54 -0400 Subject: [PATCH] Add slug validation and test (#1891) Fixes #1890 Adds validation for org slugs, ensuring that they contain only ASCII alphanumeric characters and dashes (`-`). If an invalid slug is provided, an HTTPException is returned with status code 400 and detail `invalid_slug`. --- backend/btrixcloud/orgs.py | 9 ++++++--- backend/btrixcloud/utils.py | 10 ++++++++++ backend/test/test_org.py | 14 ++++++++++++++ 3 files changed, 30 insertions(+), 3 deletions(-) diff --git a/backend/btrixcloud/orgs.py b/backend/btrixcloud/orgs.py index 9d2100cd..01823271 100644 --- a/backend/btrixcloud/orgs.py +++ b/backend/btrixcloud/orgs.py @@ -41,7 +41,7 @@ from .models import ( PaginatedResponse, ) from .pagination import DEFAULT_PAGE_SIZE, paginated_format -from .utils import slug_from_name +from .utils import slug_from_name, validate_slug if TYPE_CHECKING: from .invites import InviteOps @@ -775,8 +775,10 @@ def init_orgs_api(app, mdb, user_manager, invites, user_dep): id_ = uuid4() - slug = new_org.slug - if not slug: + if new_org.slug: + validate_slug(new_org.slug) + slug = new_org.slug + else: slug = slug_from_name(new_org.name) org = Organization( @@ -803,6 +805,7 @@ def init_orgs_api(app, mdb, user_manager, invites, user_dep): ): org.name = rename.name if rename.slug: + validate_slug(rename.slug) org.slug = rename.slug else: org.slug = slug_from_name(rename.name) diff --git a/backend/btrixcloud/utils.py b/backend/btrixcloud/utils.py index 357507d9..a6d7b5c5 100644 --- a/backend/btrixcloud/utils.py +++ b/backend/btrixcloud/utils.py @@ -8,6 +8,7 @@ import json import signal import os import sys +import re from datetime import datetime from typing import Optional, Dict, Union, List @@ -123,6 +124,15 @@ def slug_from_name(name: str) -> str: return slugify(name.replace("'", "")) +def validate_slug(slug: str) -> None: + """Validate org slug, raise HTTPException if invalid + + Slugs must contain alphanumeric characters and dashes (-) only. + """ + if re.match(r"^[\w-]+$", slug) is None: + raise HTTPException(status_code=400, detail="invalid_slug") + + def stream_dict_list_as_csv(data: List[Dict[str, Union[str, int]]], filename: str): """Stream list of dictionaries as CSV with attachment filename header""" if not data: diff --git a/backend/test/test_org.py b/backend/test/test_org.py index 6939d520..e5327865 100644 --- a/backend/test/test_org.py +++ b/backend/test/test_org.py @@ -72,6 +72,20 @@ def test_rename_org(admin_auth_headers, default_org_id): assert data["slug"] == UPDATED_SLUG +def test_rename_org_invalid_slug(admin_auth_headers, default_org_id): + UPDATED_NAME = "updated org name" + UPDATED_SLUG = "not a valid slug" + rename_data = {"name": UPDATED_NAME, "slug": UPDATED_SLUG} + r = requests.post( + f"{API_PREFIX}/orgs/{default_org_id}/rename", + headers=admin_auth_headers, + json=rename_data, + ) + + assert r.status_code == 400 + assert r.json()["detail"] == "invalid_slug" + + def test_create_org(admin_auth_headers): NEW_ORG_NAME = "New Org" r = requests.post(