diff --git a/prowler/CHANGELOG.md b/prowler/CHANGELOG.md index e05172e495..ce1114f587 100644 --- a/prowler/CHANGELOG.md +++ b/prowler/CHANGELOG.md @@ -17,7 +17,14 @@ All notable changes to the **Prowler SDK** are documented in this file. --- -## [v5.10.2] (Prowler UNRELEASED) +## [v5.10.3] (Prowler UNRELEASED) + +### Fixed +- GitHub App authentication for GitHub provider [(#8529)](https://github.com/prowler-cloud/prowler/pull/8529) + +--- + +## [v5.10.2] (Prowler v5.10.2) ### Fixed - Order requirements by ID in Prowler ThreatScore AWS compliance framework [(#8495)](https://github.com/prowler-cloud/prowler/pull/8495) diff --git a/prowler/providers/github/github_provider.py b/prowler/providers/github/github_provider.py index 4153b679da..5b86e9c76a 100644 --- a/prowler/providers/github/github_provider.py +++ b/prowler/providers/github/github_provider.py @@ -365,8 +365,16 @@ class GithubProvider(Provider): elif session.id != 0 and session.key: auth = Auth.AppAuth(session.id, session.key) gi = GithubIntegration(auth=auth, retry=retry_config) + installations = [] + for installation in gi.get_installations(): + installations.append( + installation.raw_data.get("account", {}).get("login") + ) try: - identity = GithubAppIdentityInfo(app_id=gi.get_app().id) + identity = GithubAppIdentityInfo( + app_id=gi.get_app().id, + installations=installations, + ) return identity except Exception as error: diff --git a/prowler/providers/github/models.py b/prowler/providers/github/models.py index b133dc9a69..bc1c382bc2 100644 --- a/prowler/providers/github/models.py +++ b/prowler/providers/github/models.py @@ -18,6 +18,7 @@ class GithubIdentityInfo(BaseModel): class GithubAppIdentityInfo(BaseModel): app_id: str + installations: list[str] class GithubOutputOptions(ProviderOutputOptions): diff --git a/prowler/providers/github/services/repository/repository_service.py b/prowler/providers/github/services/repository/repository_service.py index 674383a641..501bd72601 100644 --- a/prowler/providers/github/services/repository/repository_service.py +++ b/prowler/providers/github/services/repository/repository_service.py @@ -7,6 +7,7 @@ from pydantic.v1 import BaseModel from prowler.lib.logger import logger from prowler.providers.github.lib.service.service import GithubService +from prowler.providers.github.models import GithubAppIdentityInfo class Repository(GithubService): @@ -102,12 +103,23 @@ class Repository(GithubService): def _list_repositories(self): """ List repositories based on provider scoping configuration. + If the provider is a GitHub App, it will list repositories in the organizations that the GitHub App is installed in. + If the provider is a user, it will list repositories where the user is a member or owner. + If input repositories are provided, it will list repositories that match the input repositories. + If input organizations are provided, it will list repositories in the organizations that match the input organizations. """ logger.info("Repository - Listing Repositories...") repos = {} try: for client in self.clients: - if self.provider.repositories or self.provider.organizations: + if ( + self.provider.repositories + or self.provider.organizations + or ( + isinstance(self.provider.identity, GithubAppIdentityInfo) + and self.provider.identity.installations + ) + ): if self.provider.repositories: logger.info( f"Filtering for specific repositories: {self.provider.repositories}" @@ -141,6 +153,24 @@ class Repository(GithubService): self._handle_github_api_error( error, "processing organization", org_name ) + if ( + isinstance(self.provider.identity, GithubAppIdentityInfo) + and self.provider.identity.installations + ): + logger.info( + f"Filtering for repositories in the organizations or accounts that the GitHub App is installed in: {', '.join(self.provider.identity.installations)}" + ) + for org_name in self.provider.identity.installations: + try: + repos_list, _ = self._get_repositories_from_owner( + client, org_name + ) + for repo in repos_list: + self._process_repository(repo, repos) + except Exception as error: + self._handle_github_api_error( + error, "processing organization", org_name + ) else: logger.info( "No repository or organization specified, discovering accessible repositories via GraphQL API..." diff --git a/tests/lib/outputs/finding_test.py b/tests/lib/outputs/finding_test.py index 5b22fa675d..f9d4aaf7de 100644 --- a/tests/lib/outputs/finding_test.py +++ b/tests/lib/outputs/finding_test.py @@ -585,7 +585,9 @@ class TestFinding: provider = MagicMock() provider.type = "github" # GitHub App identity only has app_id, not account_name/account_id - provider.identity = GithubAppIdentityInfo(app_id=APP_ID) + provider.identity = GithubAppIdentityInfo( + app_id=APP_ID, installations=["test-org"] + ) provider.auth_method = "GitHub App Token" # Mock check result diff --git a/tests/lib/outputs/html/html_test.py b/tests/lib/outputs/html/html_test.py index e3a834e452..e85567b20a 100644 --- a/tests/lib/outputs/html/html_test.py +++ b/tests/lib/outputs/html/html_test.py @@ -673,7 +673,7 @@ class TestHTML: provider = set_mocked_github_provider( auth_method="GitHub App Token", - identity=GithubAppIdentityInfo(app_id=APP_ID), + identity=GithubAppIdentityInfo(app_id=APP_ID, installations=["test-org"]), ) summary = output.get_assessment_summary(provider) diff --git a/tests/providers/github/github_provider_test.py b/tests/providers/github/github_provider_test.py index a3c74bca86..199964a743 100644 --- a/tests/providers/github/github_provider_test.py +++ b/tests/providers/github/github_provider_test.py @@ -135,6 +135,7 @@ class TestGitHubProvider: "prowler.providers.github.github_provider.GithubProvider.setup_identity", return_value=GithubAppIdentityInfo( app_id=APP_ID, + installations=["test-org"], ), ), ): @@ -147,7 +148,9 @@ class TestGitHubProvider: assert provider._type == "github" assert provider.session == GithubSession(token="", id=APP_ID, key=APP_KEY) - assert provider.identity == GithubAppIdentityInfo(app_id=APP_ID) + assert provider.identity == GithubAppIdentityInfo( + app_id=APP_ID, installations=["test-org"] + ) assert provider._audit_config == { "inactive_not_archived_days_threshold": 180, } @@ -206,7 +209,9 @@ class TestGitHubProvider: ), patch( "prowler.providers.github.github_provider.GithubProvider.setup_identity", - return_value=GithubAppIdentityInfo(app_id=APP_ID), + return_value=GithubAppIdentityInfo( + app_id=APP_ID, installations=["test-org"] + ), ), ): connection = GithubProvider.test_connection(