From 322e81b4b6b64db62835b59cd15eea9a73c61a49 Mon Sep 17 00:00:00 2001 From: jmeridth Date: Wed, 18 Mar 2026 18:50:51 -0500 Subject: [PATCH 1/3] fix: cast gh_app_id to string for JWT encoding compatibility Relates to https://github.com/github-community-projects/evergreen/issues/508 ## What Cast gh_app_id to str() when passing it to login_as_app_installation, which internally calls jwt.encode expecting the iss claim to be a string. Updated tests to pass integer app IDs and assert the string conversion occurs. ## Why Since v2.0.0, GitHub App authentication fails with "TypeError: Issuer (iss) must be a string" because newer versions of PyJWT enforce that the iss claim is a string, but gh_app_id was being passed as an integer. ## Notes - Tests now use assert_called_once_with instead of assert_called_once to verify the exact arguments, preventing this class of regression - Test inputs changed from strings to integers to mirror real-world usage where env vars are parsed as ints Signed-off-by: jmeridth --- auth.py | 2 +- test_auth.py | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/auth.py b/auth.py index 70851596..2337128f 100644 --- a/auth.py +++ b/auth.py @@ -33,7 +33,7 @@ def auth_to_github( else: gh = github3.github.GitHub() gh.login_as_app_installation( - gh_app_private_key_bytes, gh_app_id, gh_app_installation_id + gh_app_private_key_bytes, str(gh_app_id), gh_app_installation_id ) github_connection = gh elif ghe and token: diff --git a/test_auth.py b/test_auth.py index 9788df01..40fabaa9 100644 --- a/test_auth.py +++ b/test_auth.py @@ -28,6 +28,7 @@ def test_auth_to_github_with_github_app(self, mock_login): mock_login.return_value = MagicMock() result = auth_to_github("", 12345, 678910, b"hello", "", False) + mock_login.assert_called_once_with(b"hello", "12345", 678910) self.assertIsInstance(result, github3.github.GitHub, False) def test_auth_to_github_with_token(self): @@ -65,9 +66,9 @@ def test_auth_to_github_with_ghe_and_ghe_app(self, mock_ghe): mock = mock_ghe.return_value mock.login_as_app_installation = MagicMock(return_value=True) result = auth_to_github( - "", "123", "123", b"123", "https://github.example.com", True + "", 123, 456, b"123", "https://github.example.com", True ) - mock.login_as_app_installation.assert_called_once() + mock.login_as_app_installation.assert_called_once_with(b"123", "123", 456) self.assertEqual(result, mock) @patch("github3.apps.create_jwt_headers", MagicMock(return_value="gh_token")) From 013faea1dd5cf9d1ae5b8e1bee6c14bc3b298893 Mon Sep 17 00:00:00 2001 From: jmeridth Date: Wed, 18 Mar 2026 20:04:23 -0500 Subject: [PATCH 2/3] fix: harden-runner action, bump to 2.16.0 Signed-off-by: jmeridth --- .github/workflows/codeql.yml | 2 +- .github/workflows/contributor_report.yaml | 2 +- .github/workflows/copilot-setup-steps.yml | 2 +- .github/workflows/dependency-review.yml | 2 +- .github/workflows/docker-image.yml | 2 +- .github/workflows/linter.yaml | 2 +- .github/workflows/mark-ready-when-ready.yml | 2 +- .github/workflows/python-package.yml | 2 +- .github/workflows/scorecard.yml | 2 +- .github/workflows/stale.yaml | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 0380a035..0cf1a71a 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -41,7 +41,7 @@ jobs: steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.15.1 + uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0 with: egress-policy: audit diff --git a/.github/workflows/contributor_report.yaml b/.github/workflows/contributor_report.yaml index eb60a357..c6cc0e03 100644 --- a/.github/workflows/contributor_report.yaml +++ b/.github/workflows/contributor_report.yaml @@ -16,7 +16,7 @@ jobs: steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.15.1 + uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0 with: egress-policy: audit diff --git a/.github/workflows/copilot-setup-steps.yml b/.github/workflows/copilot-setup-steps.yml index 6bf3e987..3fe2c36c 100644 --- a/.github/workflows/copilot-setup-steps.yml +++ b/.github/workflows/copilot-setup-steps.yml @@ -26,7 +26,7 @@ jobs: # If you do not check out your code, Copilot will do this for you. steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.15.1 + uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0 with: egress-policy: audit diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml index c40f8241..4f200f01 100644 --- a/.github/workflows/dependency-review.yml +++ b/.github/workflows/dependency-review.yml @@ -17,7 +17,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.15.1 + uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0 with: egress-policy: audit diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index a0da914c..599b7ee9 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -15,7 +15,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.15.1 + uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0 with: egress-policy: audit diff --git a/.github/workflows/linter.yaml b/.github/workflows/linter.yaml index e966adee..eb19bc06 100644 --- a/.github/workflows/linter.yaml +++ b/.github/workflows/linter.yaml @@ -22,7 +22,7 @@ jobs: statuses: write steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.15.1 + uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0 with: egress-policy: audit diff --git a/.github/workflows/mark-ready-when-ready.yml b/.github/workflows/mark-ready-when-ready.yml index 5deb1810..3bb7cdf1 100644 --- a/.github/workflows/mark-ready-when-ready.yml +++ b/.github/workflows/mark-ready-when-ready.yml @@ -25,7 +25,7 @@ jobs: github.event.pull_request.draft == true steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.15.1 + uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0 with: egress-policy: audit diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 72ffe554..94626788 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -26,7 +26,7 @@ jobs: steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.15.1 + uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0 with: egress-policy: audit diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index c554a6be..5165b56e 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -27,7 +27,7 @@ jobs: steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.15.1 + uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0 with: egress-policy: audit diff --git a/.github/workflows/stale.yaml b/.github/workflows/stale.yaml index 17c96beb..41257d7b 100644 --- a/.github/workflows/stale.yaml +++ b/.github/workflows/stale.yaml @@ -14,7 +14,7 @@ jobs: pull-requests: read steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.15.1 + uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0 with: egress-policy: audit From abfb8e6292803e0531ca3c980b7dd41f2583e548 Mon Sep 17 00:00:00 2001 From: jmeridth Date: Thu, 19 Mar 2026 07:21:45 -0500 Subject: [PATCH 3/3] fix: correct parameter order and type annotations in get_github_app_installation_token ## What Fixed swapped parameter order in test_get_github_app_installation_token where gh_app_private_key_bytes and gh_app_id were in the wrong positions. Updated type annotations for gh_app_id and gh_app_installation_id from str to int | None to match the actual types returned by get_int_env_var and passed by callers. ## Why The test passed despite the swapped arguments because create_jwt_headers was mocked, so the incorrect types were never exercised. The str type annotations were inconsistent with auth_to_github (which already used int | None) and with config.py where these values originate as int | None from get_int_env_var. ## Notes - Added str() cast at the create_jwt_headers call site, matching the existing pattern in auth_to_github line 36 Signed-off-by: jmeridth --- auth.py | 12 +++++++----- test_auth.py | 2 +- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/auth.py b/auth.py index 2337128f..e0f4619d 100644 --- a/auth.py +++ b/auth.py @@ -51,9 +51,9 @@ def auth_to_github( def get_github_app_installation_token( ghe: str, - gh_app_id: str, + gh_app_id: int | None, gh_app_private_key_bytes: bytes, - gh_app_installation_id: str, + gh_app_installation_id: int | None, ) -> str | None: """ Get a GitHub App Installation token. @@ -61,14 +61,16 @@ def get_github_app_installation_token( Args: ghe (str): the GitHub Enterprise endpoint - gh_app_id (str): the GitHub App ID + gh_app_id (int | None): the GitHub App ID gh_app_private_key_bytes (bytes): the GitHub App Private Key - gh_app_installation_id (str): the GitHub App Installation ID + gh_app_installation_id (int | None): the GitHub App Installation ID Returns: str: the GitHub App token """ - jwt_headers = github3.apps.create_jwt_headers(gh_app_private_key_bytes, gh_app_id) + jwt_headers = github3.apps.create_jwt_headers( + gh_app_private_key_bytes, str(gh_app_id) + ) api_endpoint = f"{ghe}/api/v3" if ghe else "https://api.github.com" url = f"{api_endpoint}/app/installations/{gh_app_installation_id}/access_tokens" diff --git a/test_auth.py b/test_auth.py index 40fabaa9..5c10b19e 100644 --- a/test_auth.py +++ b/test_auth.py @@ -85,7 +85,7 @@ def test_get_github_app_installation_token(self, mock_post): mock_ghe = "" result = get_github_app_installation_token( - mock_ghe, b"gh_private_token", "gh_app_id", "gh_installation_id" + mock_ghe, "gh_app_id", b"gh_private_token", "gh_installation_id" ) self.assertEqual(result, dummy_token)