diff --git a/products/warehouse_sources/backend/temporal/data_imports/sources/gitlab/gitlab.py b/products/warehouse_sources/backend/temporal/data_imports/sources/gitlab/gitlab.py index 2ce7eb355f90..9a7b0493ca61 100644 --- a/products/warehouse_sources/backend/temporal/data_imports/sources/gitlab/gitlab.py +++ b/products/warehouse_sources/backend/temporal/data_imports/sources/gitlab/gitlab.py @@ -190,11 +190,13 @@ def validate_credentials( if not project or not project.strip(): return False, "Missing project id or path" - # GitLab addresses a project by its numeric id or a group/project path. A pasted URL or a bare - # group name otherwise URL-encodes into a nonsense path, 404s, and gets reported as "not found - # or not accessible with this token" — which points the user at the token rather than the format. + # GitLab addresses a project by its numeric id or a group/project path. A pasted URL, a bare + # group name, or a stray leading, trailing, or doubled slash otherwise URL-encodes into a + # nonsense path, 404s, and gets reported as "not found or not accessible with this token", which + # points the user at the token rather than the format. project_ref = project.strip() - if "://" in project_ref or ("/" not in project_ref and not project_ref.isdigit()): + segments = project_ref.split("/") + if not project_ref.isdigit() and ("://" in project_ref or len(segments) < 2 or not all(segments)): return ( False, "Enter the project as group/project (for example, mygroup/myproject) or its numeric project ID, not a full URL.", diff --git a/products/warehouse_sources/backend/temporal/data_imports/sources/gitlab/tests/test_gitlab.py b/products/warehouse_sources/backend/temporal/data_imports/sources/gitlab/tests/test_gitlab.py index 6e96a8a7c474..2be030a12b23 100644 --- a/products/warehouse_sources/backend/temporal/data_imports/sources/gitlab/tests/test_gitlab.py +++ b/products/warehouse_sources/backend/temporal/data_imports/sources/gitlab/tests/test_gitlab.py @@ -286,6 +286,9 @@ def test_missing_inputs_short_circuit(self, host, token, project, expected_msg): "https://gitlab.com/mygroup/", # pasted URL "mygroup", # bare group, no project and not a numeric id "some-dashboard", # bare name + "/myproject", # leading slash: encodes to an empty group segment + "mygroup/", # trailing slash: encodes to an empty project segment + "mygroup//myproject", # doubled slash: encodes to an empty middle segment ], ) def test_malformed_project_gets_format_guidance(self, project):