Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions src/git_commit_guard/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,8 @@ def _resolve_github_username(rev, email):
except urllib.error.HTTPError as e:
if e.code == HTTPStatus.NOT_FOUND:
commits_api_404 = True
elif e.code in (HTTPStatus.UNAUTHORIZED, HTTPStatus.FORBIDDEN):
raise
except (urllib.error.URLError, TimeoutError):
pass
if username is None:
Expand Down Expand Up @@ -454,6 +456,24 @@ def check_signature(rev, result):
"git operation timed out — cannot verify signature",
check=Check.SIGNATURE,
)
except urllib.error.HTTPError as e:
if e.code == HTTPStatus.UNAUTHORIZED:
result.error(
"GitHub API rejected token (HTTP 401) — "
"GITHUB_TOKEN may be invalid or expired",
check=Check.SIGNATURE,
)
elif e.code == HTTPStatus.FORBIDDEN:
result.error(
"GitHub API forbidden (HTTP 403) — GITHUB_TOKEN may lack "
"'repo' scope, or you are unauthenticated and rate-limited",
check=Check.SIGNATURE,
)
else:
result.error(
f"GitHub API error (HTTP {e.code}) — cannot verify signature",
check=Check.SIGNATURE,
)
except (urllib.error.URLError, TimeoutError):
result.error(
"GitHub API unreachable — cannot verify signature",
Expand Down
78 changes: 78 additions & 0 deletions tests/test_git_commit_guard.py
Original file line number Diff line number Diff line change
Expand Up @@ -929,6 +929,84 @@ def test_commits_api_non_404_http_error_falls_through(self):
check_signature("abc123", r)
assert r.ok

def test_commits_api_401_surfaces_token_message(self):
r = Result()
with (
patch(
"git_commit_guard._get_author_email", return_value="user@example.com"
),
patch(
"git_commit_guard._get_github_remote_info",
return_value=("owner", "repo"),
),
patch(
"git_commit_guard._fetch_github_commit_author",
side_effect=urllib.error.HTTPError(
url="", code=401, msg="Unauthorized", hdrs=None, fp=None
),
),
):
check_signature("abc123", r)
assert not r.ok
assert any("rejected token (HTTP 401)" in msg for _, _, msg in r.errors)

def test_commits_api_403_surfaces_token_message(self):
r = Result()
with (
patch(
"git_commit_guard._get_author_email", return_value="user@example.com"
),
patch(
"git_commit_guard._get_github_remote_info",
return_value=("owner", "repo"),
),
patch(
"git_commit_guard._fetch_github_commit_author",
side_effect=urllib.error.HTTPError(
url="", code=403, msg="Forbidden", hdrs=None, fp=None
),
),
):
check_signature("abc123", r)
assert not r.ok
assert any("forbidden (HTTP 403)" in msg for _, _, msg in r.errors)

def test_search_api_403_surfaces_rate_limit_message(self):
r = Result()
with (
patch(
"git_commit_guard._get_author_email", return_value="corp@example.com"
),
patch("git_commit_guard._get_github_remote_info", return_value=None),
patch(
"git_commit_guard._fetch_github_username",
side_effect=urllib.error.HTTPError(
url="", code=403, msg="Forbidden", hdrs=None, fp=None
),
),
):
check_signature("abc123", r)
assert not r.ok
assert any("forbidden (HTTP 403)" in msg for _, _, msg in r.errors)

def test_other_http_error_uses_generic_http_message(self):
r = Result()
with (
patch(
"git_commit_guard._get_author_email", return_value="corp@example.com"
),
patch("git_commit_guard._get_github_remote_info", return_value=None),
patch(
"git_commit_guard._fetch_github_username",
side_effect=urllib.error.HTTPError(
url="", code=500, msg="Server Error", hdrs=None, fp=None
),
),
):
check_signature("abc123", r)
assert not r.ok
assert any("GitHub API error (HTTP 500)" in msg for _, _, msg in r.errors)

def test_url_error_fails(self):
r = Result()
with patch(
Expand Down
Loading