Skip to content

Commit

Permalink
Improve GitLab projects name verification (pypi#16262)
Browse files Browse the repository at this point in the history
* Improve GitLab projects name verification

* Update translations

* Replace ConsecutiveSpecialCharacters with a regex

* Update Gitlab wrong name detection to reduce dependency to Gitlab internal code.

* Update translations

* Fix bad merge

---------

Co-authored-by: William Woodruff <william@trailofbits.com>
Co-authored-by: Dustin Ingram <di@users.noreply.github.com>
  • Loading branch information
3 people authored Dec 2, 2024
1 parent d6d91d5 commit 1054c4d
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 9 deletions.
39 changes: 39 additions & 0 deletions tests/unit/oidc/forms/test_gitlab.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,45 @@ def test_validate_basic_invalid_fields(self, monkeypatch, data):
# We're testing only the basic validation here.
assert not form.validate()

@pytest.mark.parametrize(
"project_name",
["invalid.git", "invalid.atom", "invalid--project"],
)
def test_reserved_project_names(self, project_name):

data = MultiDict(
{
"namespace": "some",
"workflow_filepath": "subfolder/some-workflow.yml",
"project": project_name,
}
)

form = gitlab.GitLabPublisherForm(data)
assert not form.validate()

@pytest.mark.parametrize(
"namespace",
[
"invalid.git",
"invalid.atom",
"consecutive--special-characters",
"must-end-with-non-special-characters-",
],
)
def test_reserved_organization_names(self, namespace):

data = MultiDict(
{
"namespace": namespace,
"workflow_filepath": "subfolder/some-workflow.yml",
"project": "valid-project",
}
)

form = gitlab.GitLabPublisherForm(data)
assert not form.validate()

@pytest.mark.parametrize(
"workflow_filepath",
[
Expand Down
18 changes: 11 additions & 7 deletions warehouse/locale/messages.pot
Original file line number Diff line number Diff line change
Expand Up @@ -374,7 +374,7 @@ msgid "Select project"
msgstr ""

#: warehouse/manage/forms.py:495 warehouse/oidc/forms/_core.py:23
#: warehouse/oidc/forms/gitlab.py:43
#: warehouse/oidc/forms/gitlab.py:57
msgid "Specify project name"
msgstr ""

Expand Down Expand Up @@ -625,7 +625,7 @@ msgid "Expired invitation for '${username}' deleted."
msgstr ""

#: warehouse/oidc/forms/_core.py:25 warehouse/oidc/forms/_core.py:35
#: warehouse/oidc/forms/gitlab.py:45
#: warehouse/oidc/forms/gitlab.py:60 warehouse/oidc/forms/gitlab.py:64
msgid "Invalid project name"
msgstr ""

Expand Down Expand Up @@ -745,26 +745,30 @@ msgid "Workflow filename must be a filename only, without directories"
msgstr ""

#: warehouse/oidc/forms/gitlab.py:32
msgid "Name ends with .git or .atom"
msgstr ""

#: warehouse/oidc/forms/gitlab.py:41
msgid "Specify GitLab namespace (username or group/subgroup)"
msgstr ""

#: warehouse/oidc/forms/gitlab.py:36
#: warehouse/oidc/forms/gitlab.py:46 warehouse/oidc/forms/gitlab.py:50
msgid "Invalid GitLab username or group/subgroup name."
msgstr ""

#: warehouse/oidc/forms/gitlab.py:53
#: warehouse/oidc/forms/gitlab.py:72
msgid "Specify top-level pipeline file path"
msgstr ""

#: warehouse/oidc/forms/gitlab.py:62
#: warehouse/oidc/forms/gitlab.py:81
msgid "Invalid environment name"
msgstr ""

#: warehouse/oidc/forms/gitlab.py:77
#: warehouse/oidc/forms/gitlab.py:96
msgid "Top-level pipeline file path must end with .yml or .yaml"
msgstr ""

#: warehouse/oidc/forms/gitlab.py:81
#: warehouse/oidc/forms/gitlab.py:100
msgid "Top-level pipeline file path cannot start or end with /"
msgstr ""

Expand Down
23 changes: 21 additions & 2 deletions warehouse/oidc/forms/gitlab.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,26 @@
# limitations under the License.

import re
import typing

import wtforms

from warehouse.i18n import localize as _
from warehouse.oidc.forms._core import PendingPublisherMixin

# https://docs.gitlab.com/ee/user/reserved_names.html#limitations-on-project-and-group-names
_VALID_GITLAB_PROJECT = re.compile(r"^[a-zA-Z0-9][a-zA-Z0-9-_.]*$")
_VALID_GITLAB_NAMESPACE = re.compile(r"^[a-zA-Z0-9][a-zA-Z0-9-_./]*$")
_VALID_GITLAB_PROJECT = re.compile(r"^[a-zA-Z0-9][a-zA-Z0-9_.-]*[a-zA-Z0-9]$")
_VALID_GITLAB_NAMESPACE = re.compile(r"^[a-zA-Z0-9][a-zA-Z0-9-_./+]*[a-zA-Z0-9]$")
_VALID_GITLAB_ENVIRONMENT = re.compile(r"^[a-zA-Z0-9\-_/${} ]+$")

_CONSECUTIVE_SPECIAL_CHARACTERS = re.compile(r"(?!.*[._-]{2})")


def ends_with_atom_or_git(form: wtforms.Form, field: wtforms.Field) -> None:
field_value = typing.cast(str, field.data).lower()
if field_value.endswith(".atom") or field_value.endswith(".git"):
raise wtforms.validators.ValidationError(_("Name ends with .git or .atom"))


class GitLabPublisherBase(wtforms.Form):
__params__ = ["namespace", "project", "workflow_filepath", "environment"]
Expand All @@ -31,19 +40,29 @@ class GitLabPublisherBase(wtforms.Form):
wtforms.validators.InputRequired(
message=_("Specify GitLab namespace (username or group/subgroup)"),
),
ends_with_atom_or_git,
wtforms.validators.Regexp(
_VALID_GITLAB_NAMESPACE,
message=_("Invalid GitLab username or group/subgroup name."),
),
wtforms.validators.Regexp(
_CONSECUTIVE_SPECIAL_CHARACTERS,
message=_("Invalid GitLab username or group/subgroup name."),
),
]
)

project = wtforms.StringField(
validators=[
wtforms.validators.InputRequired(message=_("Specify project name")),
ends_with_atom_or_git,
wtforms.validators.Regexp(
_VALID_GITLAB_PROJECT, message=_("Invalid project name")
),
wtforms.validators.Regexp(
_CONSECUTIVE_SPECIAL_CHARACTERS,
message=_("Invalid project name"),
),
]
)

Expand Down

0 comments on commit 1054c4d

Please sign in to comment.