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

Ignore case when verifying GitLab/GitHub URLs #16899

Merged
merged 13 commits into from
Nov 14, 2024
16 changes: 16 additions & 0 deletions tests/unit/oidc/models/test_activestate.py
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,22 @@ def test_activestate_publisher_sub(
check(expected, actual, signed_claims)
assert str(e.value) == error_msg

@pytest.mark.parametrize(
("url", "expected"),
[
("https://platform.activestate.com/repository_name/project_name", True),
("https://platform.activestate.com/repository_name/PrOjECt_NaMe", False),
],
)
def test_activestate_publisher_verify_url(self, url, expected):
publisher = ActiveStatePublisher(
organization="repository_name",
activestate_project_name="project_name",
actor_id=ACTOR_ID,
actor=ACTOR,
)
assert publisher.verify_url(url) == expected


class TestPendingActiveStatePublisher:
def test_reify_does_not_exist_yet(self, db_request):
Expand Down
7 changes: 6 additions & 1 deletion tests/unit/oidc/models/test_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,9 +136,14 @@ def test_supports_attestations(self):
"https://github.com",
False,
),
( # Default verification is case-sensitive
"https://publisher.com/owner/project",
"https://publisher.com/owner/PrOjeCt",
False,
),
],
)
def test_verify_url(self, monkeypatch, url, publisher_url, expected):
def test_verify_url(self, url, publisher_url, expected):
class ConcretePublisher(_core.OIDCPublisher):
@property
def publisher_base_url(self):
Expand Down
2 changes: 2 additions & 0 deletions tests/unit/oidc/models/test_github.py
Original file line number Diff line number Diff line change
Expand Up @@ -662,6 +662,8 @@ def test_github_publisher_duplicates_cant_be_created(self, db_request):
("https://repository_owner.github.io/repository_name/../malicious", False),
("https://repository_owner.github.io/", False),
("https://repository_owner.github.io/unrelated_name/", False),
("https://github.com/RePosItory_OwNeR/rePository_Name.git", True),
("https://repository_owner.github.io/RePoSiToRy_NaMe/subpage", True),
],
)
def test_github_publisher_verify_url(self, url, expected):
Expand Down
1 change: 1 addition & 0 deletions tests/unit/oidc/models/test_gitlab.py
Original file line number Diff line number Diff line change
Expand Up @@ -608,6 +608,7 @@ def test_gitlab_publisher_duplicates_cant_be_created(self, db_request):
("https://gitlab.com/repository_owner/repository_name.git", True),
("https://gitlab.com/repository_owner/repository_name.git/", True),
("https://gitlab.com/repository_owner/repository_name.git/issues", False),
("https://gitlab.com/repository_OwNeR/RePoSiToRy_Name.git/", True),
],
)
def test_gitlab_publisher_verify_url(self, url, expected):
Expand Down
16 changes: 15 additions & 1 deletion warehouse/oidc/models/_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,12 @@ class OIDCPublisherMixin:
# even have an "equivalent" column.
__lookup_strategies__: list = []

# URL Verification Case Sensitivity
# Determines whether the user URLs should be verified in a case-sensitive
# manner. By default, the URLs are treated as case-sensitive. This behavior
# can be overridden in individual Publisher definitions.
url_verification_is_case_sensitive: bool = True
DarkaMaul marked this conversation as resolved.
Show resolved Hide resolved

@classmethod
def lookup_by_claims(cls, session, signed_claims: SignedClaims):
for lookup in cls.__lookup_strategies__:
Expand Down Expand Up @@ -362,7 +368,15 @@ def verify_url(self, url: str) -> bool:
if self.publisher_base_url is None:
# Currently this only applies to the Google provider
return False
publisher_uri = rfc3986.api.uri_reference(self.publisher_base_url).normalize()
# Several OIDC Publishers have case-insensitive owner/repo slugs.
# In such cases, we want to perform a case-insensitive comparison
# during the URL verification.
if not self.url_verification_is_case_sensitive:
url = url.lower()
publisher_base_url = self.publisher_base_url.lower()
else:
publisher_base_url = self.publisher_base_url
publisher_uri = rfc3986.api.uri_reference(publisher_base_url).normalize()
user_uri = rfc3986.api.uri_reference(url).normalize()
if publisher_uri.path is None:
# Currently no Trusted Publishers with a `publisher_base_url` have an empty
Expand Down
12 changes: 10 additions & 2 deletions warehouse/oidc/models/github.py
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,9 @@ class GitHubPublisherMixin:
"ref_protected",
}

# GitHub uses case-insensitive owner/repo slugs
url_verification_is_case_sensitive: bool = False

@staticmethod
def __lookup_all__(klass, signed_claims: SignedClaims) -> Query | None:
# This lookup requires the environment claim to be present;
Expand Down Expand Up @@ -356,13 +359,18 @@ def verify_url(self, url: str):
The suffix `.git` in repo URLs is ignored, since `github.com/org/repo.git`
always redirects to `github.com/org/repo`. This does not apply to subpaths,
like `github.com/org/repo.git/issues`, which do not redirect to the correct URL.

GitHub uses case-insensitive owner/repo slugs - so we perform a case-insensitive
comparison.
"""
url = url.lower()
DarkaMaul marked this conversation as resolved.
Show resolved Hide resolved
url_for_generic_check = url.removesuffix("/").removesuffix(".git")

if super().verify_url(url_for_generic_check):
return True

docs_url = f"https://{self.repository_owner}.github.io/{self.repository_name}"
docs_url = (
f"https://{self.repository_owner}.github.io/{self.repository_name}".lower()
)
docs_uri = rfc3986.api.uri_reference(docs_url).normalize()
user_uri = rfc3986.api.uri_reference(url).normalize()

Expand Down
3 changes: 3 additions & 0 deletions warehouse/oidc/models/gitlab.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,9 @@ class GitLabPublisherMixin:
"groups_direct",
}

# GitLab uses case-insensitive owner/repo slugs
url_verification_is_case_sensitive: bool = False

@staticmethod
def __lookup_all__(klass, signed_claims: SignedClaims) -> Query | None:
# This lookup requires the environment claim to be present;
Expand Down