Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve GitLab projects name verification #16262

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
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 @@ -116,6 +116,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
19 changes: 12 additions & 7 deletions warehouse/locale/messages.pot
Original file line number Diff line number Diff line change
Expand Up @@ -369,7 +369,7 @@ msgid "Select project"
msgstr ""

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

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

#: warehouse/oidc/forms/_core.py:25 warehouse/oidc/forms/gitlab.py:46
#: warehouse/oidc/forms/_core.py:25 warehouse/oidc/forms/gitlab.py:61
#: warehouse/oidc/forms/gitlab.py:65
msgid "Invalid project name"
msgstr ""

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

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

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

#: warehouse/oidc/forms/gitlab.py:37
#: warehouse/oidc/forms/gitlab.py:47 warehouse/oidc/forms/gitlab.py:51
msgid "Invalid GitLab username or group/subgroup name."
msgstr ""

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

#: warehouse/oidc/forms/gitlab.py:63
#: warehouse/oidc/forms/gitlab.py:82
msgid "Invalid environment name"
msgstr ""

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

#: warehouse/oidc/forms/gitlab.py:82
#: warehouse/oidc/forms/gitlab.py:101
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,6 +11,7 @@
# limitations under the License.

import re
import typing

import wtforms

Expand All @@ -19,10 +20,18 @@
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: forms.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(forms.Form):
__params__ = ["namespace", "project", "workflow_filepath", "environment"]
Expand All @@ -32,19 +41,29 @@ class GitLabPublisherBase(forms.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