From d9805e63083eb77514fa558e46159e6e0c59ea45 Mon Sep 17 00:00:00 2001 From: Prowler Bot Date: Thu, 14 Aug 2025 10:56:30 +0200 Subject: [PATCH] fix(github): list all accessible repositories (#8523) Co-authored-by: Sergio Garcia --- .../github/getting-started-github.md | 51 ++- .../github/img/github-pat-permissions.png | Bin 0 -> 91448 bytes prowler/CHANGELOG.md | 1 + prowler/providers/github/github_provider.py | 6 + .../services/repository/repository_service.py | 381 +++++++++++------- .../repository/repository_service_test.py | 295 +++++--------- 6 files changed, 385 insertions(+), 349 deletions(-) create mode 100644 docs/tutorials/github/img/github-pat-permissions.png diff --git a/docs/tutorials/github/getting-started-github.md b/docs/tutorials/github/getting-started-github.md index 3515b2ca23..2d3580410d 100644 --- a/docs/tutorials/github/getting-started-github.md +++ b/docs/tutorials/github/getting-started-github.md @@ -24,7 +24,56 @@ Personal Access Tokens provide the simplest GitHub authentication method and sup - Scroll down the left sidebar - Click "Developer settings" -3. **Generate New Token** +3. **Generate Fine-Grained Token** + - Click "Personal access tokens" + - Select "Fine-grained tokens" + - Click "Generate new token" + +4. **Configure Token Settings** + - **Token name**: Give your token a descriptive name (e.g., "Prowler Security Scanner") + - **Expiration**: Set an appropriate expiration date (recommended: 90 days or less) + - **Repository access**: Choose "All repositories" or "Only select repositories" based on your needs + + ???+ note "Public repositories" + Even if you select 'Only select repositories', the token will have access to the public repositories that you own or are a member of. + +5. **Configure Token Permissions** + To enable Prowler functionality, configure the following permissions: + + - **Repository permissions:** + - **Contents**: Read-only access + - **Metadata**: Read-only access + - **Pull requests**: Read-only access + - **Security advisories**: Read-only access + - **Statuses**: Read-only access + + - **Organization permissions:** + - **Members**: Read-only access + + - **Account permissions:** + - **Email addresses**: Read-only access + +6. **Copy and Store the Token** + - Copy the generated token immediately (GitHub displays tokens only once) + - Store tokens securely using environment variables + +![GitHub Personal Access Token Permissions](./img/github-pat-permissions.png) + +#### **Option 2: Create a Classic Personal Access Token (Not Recommended)** + +???+ warning "Security Risk" + Classic tokens provide broad permissions that may exceed what Prowler actually needs. Use fine-grained tokens instead for better security. + +1. **Navigate to GitHub Settings** + - Open [GitHub](https://github.com) and sign in + - Click the profile picture in the top right corner + - Select "Settings" from the dropdown menu + +2. **Access Developer Settings** + - Scroll down the left sidebar + - Click "Developer settings" + +3. **Generate Classic Token** - Click "Personal access tokens" - Select "Tokens (classic)" - Click "Generate new token" diff --git a/docs/tutorials/github/img/github-pat-permissions.png b/docs/tutorials/github/img/github-pat-permissions.png new file mode 100644 index 0000000000000000000000000000000000000000..d666c70baaed3c155c57936888270ace1bd02eb4 GIT binary patch literal 91448 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 S#n%hqkEFQV list[str]: + """ + Use the GitHub GraphQL API to list all repositories that the authentication token has access to. + This works with high-granularity (fine-grained) PATs. + """ + graphql_url = "https://api.github.com/graphql" + token = self.provider.session.token + headers = { + "Authorization": f"bearer {token}", + "Content-Type": "application/json", + } + query = """ + { + viewer { + repositories(first: 100, affiliations: [OWNER, ORGANIZATION_MEMBER]) { + nodes { + nameWithOwner + } + } + } + } + """ + + try: + response = requests.post( + graphql_url, json={"query": query}, headers=headers + ) + response.raise_for_status() + data = response.json() + + if "errors" in data: + logger.error(f"Error in GraphQL query: {data['errors']}") + return [] + + repo_nodes = ( + data.get("data", {}) + .get("viewer", {}) + .get("repositories", {}) + .get("nodes", []) + ) + return [repo["nameWithOwner"] for repo in repo_nodes] + + except requests.exceptions.RequestException as error: + logger.error( + f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" + ) + return [] + def _list_repositories(self): """ List repositories based on provider scoping configuration. - - Scoping behavior: - - No scoping: Returns all accessible repositories for authenticated user - - Repository scoping: Returns only specified repositories - Example: --repository owner1/repo1 owner2/repo2 - - Organization scoping: Returns all repositories from specified organizations - Example: --organization org1 org2 - - Combined scoping: Returns specified repositories + all repos from organizations - Example: --repository owner1/repo1 --organization org2 - - Returns: - dict: Dictionary of repository ID to Repo objects - - Raises: - github.GithubException: When GitHub API access fails - github.RateLimitExceededException: When API rate limits are exceeded """ logger.info("Repository - Listing Repositories...") repos = {} @@ -109,11 +142,38 @@ class Repository(GithubService): error, "processing organization", org_name ) else: - for repo in client.get_user().get_repos(): - self._process_repository(repo, repos) + logger.info( + "No repository or organization specified, discovering accessible repositories via GraphQL API..." + ) + accessible_repo_names = self._get_accessible_repos_graphql() + + if not accessible_repo_names: + logger.warning( + "Could not find any accessible repositories with the provided token." + ) + + for repo_name in accessible_repo_names: + try: + repo = client.get_repo(repo_name) + logger.info( + f"Processing repository found via GraphQL: {repo.full_name}" + ) + self._process_repository(repo, repos) + except Exception as error: + if hasattr(self, "_handle_github_api_error"): + self._handle_github_api_error( + error, + "accessing repository discovered via GraphQL", + repo_name, + ) + else: + logger.error( + f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" + ) + except github.RateLimitExceededException as error: logger.error(f"GitHub API rate limit exceeded: {error}") - raise # Re-raise rate limit errors as they need special handling + raise except github.GithubException as error: logger.error(f"GitHub API error while listing repositories: {error}") except Exception as error: @@ -124,158 +184,167 @@ class Repository(GithubService): def _process_repository(self, repo, repos): """Process a single repository and extract all its information.""" - default_branch = repo.default_branch - securitymd_exists = self._file_exists(repo, "SECURITY.md") - # CODEOWNERS file can be in .github/, root, or docs/ - # https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners#codeowners-file-location - codeowners_paths = [ - ".github/CODEOWNERS", - "CODEOWNERS", - "docs/CODEOWNERS", - ] - codeowners_files = [self._file_exists(repo, path) for path in codeowners_paths] - if True in codeowners_files: - codeowners_exists = True - elif all(file is None for file in codeowners_files): - codeowners_exists = None - else: - codeowners_exists = False - delete_branch_on_merge = ( - repo.delete_branch_on_merge - if repo.delete_branch_on_merge is not None - else False - ) - - require_pr = False - approval_cnt = 0 - branch_protection = False - required_linear_history = False - allow_force_pushes = True - branch_deletion = True - require_code_owner_reviews = False - require_signed_commits = False - status_checks = False - enforce_admins = False - conversation_resolution = False try: - branch = repo.get_branch(default_branch) - if branch.protected: - protection = branch.get_protection() - if protection: - require_pr = protection.required_pull_request_reviews is not None - approval_cnt = ( - protection.required_pull_request_reviews.required_approving_review_count - if require_pr - else 0 - ) - required_linear_history = protection.required_linear_history - allow_force_pushes = protection.allow_force_pushes - branch_deletion = protection.allow_deletions - status_checks = protection.required_status_checks is not None - enforce_admins = protection.enforce_admins - conversation_resolution = ( - protection.required_conversation_resolution - ) - branch_protection = True - require_code_owner_reviews = ( - protection.required_pull_request_reviews.require_code_owner_reviews - if require_pr - else False - ) - require_signed_commits = branch.get_required_signatures() - except Exception as error: - # If the branch is not found, it is not protected - if "404" in str(error): - logger.warning( - f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" - ) - # Any other error, we cannot know if the branch is protected or not + default_branch = repo.default_branch + securitymd_exists = self._file_exists(repo, "SECURITY.md") + # CODEOWNERS file can be in .github/, root, or docs/ + # https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners#codeowners-file-location + codeowners_paths = [ + ".github/CODEOWNERS", + "CODEOWNERS", + "docs/CODEOWNERS", + ] + codeowners_files = [ + self._file_exists(repo, path) for path in codeowners_paths + ] + if True in codeowners_files: + codeowners_exists = True + elif all(file is None for file in codeowners_files): + codeowners_exists = None else: - require_pr = None - approval_cnt = None - branch_protection = None - required_linear_history = None - allow_force_pushes = None - branch_deletion = None - require_code_owner_reviews = None - require_signed_commits = None - status_checks = None - enforce_admins = None - conversation_resolution = None - logger.error( - f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" - ) + codeowners_exists = False + delete_branch_on_merge = ( + repo.delete_branch_on_merge + if repo.delete_branch_on_merge is not None + else False + ) - secret_scanning_enabled = False - dependabot_alerts_enabled = False - try: - if ( - repo.security_and_analysis - and repo.security_and_analysis.secret_scanning - ): - secret_scanning_enabled = ( - repo.security_and_analysis.secret_scanning.status == "enabled" - ) + require_pr = False + approval_cnt = 0 + branch_protection = False + required_linear_history = False + allow_force_pushes = True + branch_deletion = True + require_code_owner_reviews = False + require_signed_commits = False + status_checks = False + enforce_admins = False + conversation_resolution = False try: - # Use get_dependabot_alerts to check if Dependabot alerts are enabled - repo.get_dependabot_alerts().totalCount - # If the call succeeds, Dependabot is enabled (even if no alerts) - dependabot_alerts_enabled = True + branch = repo.get_branch(default_branch) + if branch.protected: + protection = branch.get_protection() + if protection: + require_pr = ( + protection.required_pull_request_reviews is not None + ) + approval_cnt = ( + protection.required_pull_request_reviews.required_approving_review_count + if require_pr + else 0 + ) + required_linear_history = protection.required_linear_history + allow_force_pushes = protection.allow_force_pushes + branch_deletion = protection.allow_deletions + status_checks = protection.required_status_checks is not None + enforce_admins = protection.enforce_admins + conversation_resolution = ( + protection.required_conversation_resolution + ) + branch_protection = True + require_code_owner_reviews = ( + protection.required_pull_request_reviews.require_code_owner_reviews + if require_pr + else False + ) + require_signed_commits = branch.get_required_signatures() except Exception as error: - error_str = str(error) - if ( - "403" in error_str - and "Dependabot alerts are disabled for this repository." - in error_str - ): - dependabot_alerts_enabled = False - else: - logger.error( - f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" + # If the branch is not found, it is not protected + if "404" in str(error): + logger.warning( + f"{repo.full_name}: {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" ) - dependabot_alerts_enabled = None + # Any other error, we cannot know if the branch is protected or not + else: + require_pr = None + approval_cnt = None + branch_protection = None + required_linear_history = None + allow_force_pushes = None + branch_deletion = None + require_code_owner_reviews = None + require_signed_commits = None + status_checks = None + enforce_admins = None + conversation_resolution = None + logger.error( + f"{repo.full_name}: {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" + ) + + secret_scanning_enabled = False + dependabot_alerts_enabled = False + try: + if ( + repo.security_and_analysis + and repo.security_and_analysis.secret_scanning + ): + secret_scanning_enabled = ( + repo.security_and_analysis.secret_scanning.status == "enabled" + ) + try: + # Use get_dependabot_alerts to check if Dependabot alerts are enabled + repo.get_dependabot_alerts().totalCount + # If the call succeeds, Dependabot is enabled (even if no alerts) + dependabot_alerts_enabled = True + except Exception as error: + error_str = str(error) + if ( + "403" in error_str + and "Dependabot alerts are disabled for this repository." + in error_str + ): + dependabot_alerts_enabled = False + else: + logger.error( + f"{repo.full_name}: {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" + ) + dependabot_alerts_enabled = None + except Exception as error: + logger.error( + f"{repo.full_name}: {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" + ) + secret_scanning_enabled = None + dependabot_alerts_enabled = None + repos[repo.id] = Repo( + id=repo.id, + name=repo.name, + owner=repo.owner.login, + full_name=repo.full_name, + default_branch=Branch( + name=default_branch, + protected=branch_protection, + default_branch=True, + require_pull_request=require_pr, + approval_count=approval_cnt, + required_linear_history=required_linear_history, + allow_force_pushes=allow_force_pushes, + branch_deletion=branch_deletion, + status_checks=status_checks, + enforce_admins=enforce_admins, + conversation_resolution=conversation_resolution, + require_code_owner_reviews=require_code_owner_reviews, + require_signed_commits=require_signed_commits, + ), + private=repo.private, + archived=repo.archived, + pushed_at=repo.pushed_at, + securitymd=securitymd_exists, + codeowners_exists=codeowners_exists, + secret_scanning_enabled=secret_scanning_enabled, + dependabot_alerts_enabled=dependabot_alerts_enabled, + delete_branch_on_merge=delete_branch_on_merge, + ) except Exception as error: logger.error( - f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" + f"{repo.full_name}: {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" ) - secret_scanning_enabled = None - dependabot_alerts_enabled = None - repos[repo.id] = Repo( - id=repo.id, - name=repo.name, - owner=repo.owner.login, - full_name=repo.full_name, - default_branch=Branch( - name=default_branch, - protected=branch_protection, - default_branch=True, - require_pull_request=require_pr, - approval_count=approval_cnt, - required_linear_history=required_linear_history, - allow_force_pushes=allow_force_pushes, - branch_deletion=branch_deletion, - status_checks=status_checks, - enforce_admins=enforce_admins, - conversation_resolution=conversation_resolution, - require_code_owner_reviews=require_code_owner_reviews, - require_signed_commits=require_signed_commits, - ), - private=repo.private, - archived=repo.archived, - pushed_at=repo.pushed_at, - securitymd=securitymd_exists, - codeowners_exists=codeowners_exists, - secret_scanning_enabled=secret_scanning_enabled, - dependabot_alerts_enabled=dependabot_alerts_enabled, - delete_branch_on_merge=delete_branch_on_merge, - ) class Branch(BaseModel): """Model for Github Branch""" name: str - protected: bool + protected: Optional[bool] default_branch: bool require_pull_request: Optional[bool] approval_count: Optional[int] diff --git a/tests/providers/github/services/repository/repository_service_test.py b/tests/providers/github/services/repository/repository_service_test.py index ada89ee072..20e5887d54 100644 --- a/tests/providers/github/services/repository/repository_service_test.py +++ b/tests/providers/github/services/repository/repository_service_test.py @@ -1,6 +1,7 @@ from datetime import datetime, timezone from unittest.mock import MagicMock, patch +import requests from github import GithubException, RateLimitExceededException from prowler.providers.github.services.repository.repository_service import ( @@ -40,6 +41,7 @@ def mock_list_repositories(_): archived=False, pushed_at=datetime.now(timezone.utc), delete_branch_on_merge=True, + dependabot_alerts_enabled=True, ), } @@ -110,6 +112,105 @@ class Test_Repository_FileExists: assert mock_logger.error.called +class Test_Repository_GraphQL: + def setup_method(self): + self.mock_repo1 = MagicMock() + self.mock_repo1.id = 1 + self.mock_repo1.name = "repo1" + self.mock_repo1.owner.login = "owner1" + self.mock_repo1.full_name = "owner1/repo1" + self.mock_repo1.default_branch = "main" + self.mock_repo1.private = False + self.mock_repo1.archived = False + self.mock_repo1.pushed_at = datetime.now(timezone.utc) + self.mock_repo1.delete_branch_on_merge = False + self.mock_repo1.security_and_analysis = None + self.mock_repo1.get_contents.side_effect = [None, None, None] + self.mock_repo1.get_branch.side_effect = Exception("404 Not Found") + self.mock_repo1.get_dependabot_alerts.side_effect = Exception("403 Forbidden") + + def test_no_scoping_uses_graphql(self): + """Test that no scoping triggers the GraphQL discovery method successfully""" + provider = set_mocked_github_provider() + provider.repositories = [] + provider.organizations = [] + + with patch.object(Repository, "__init__", lambda x, y: None): + repository_service = Repository(provider) + mock_client = MagicMock() + repository_service.clients = [mock_client] + repository_service.provider = provider + + with patch.object( + repository_service, + "_get_accessible_repos_graphql", + return_value=["owner1/repo1"], + ) as mock_graphql_call: + mock_client.get_repo.return_value = self.mock_repo1 + + repos = repository_service._list_repositories() + + assert len(repos) == 1 + assert 1 in repos + assert repos[1].name == "repo1" + + mock_graphql_call.assert_called_once() + mock_client.get_repo.assert_called_once_with("owner1/repo1") + + def test_graphql_call_api_error(self): + """Test that an error during the GraphQL call is handled gracefully""" + provider = set_mocked_github_provider() + provider.repositories = [] + provider.organizations = [] + + with patch.object(Repository, "__init__", lambda x, y: None): + repository_service = Repository(provider) + repository_service.clients = [MagicMock()] + repository_service.provider = provider + + with patch( + "requests.post", + side_effect=requests.exceptions.RequestException("API Error"), + ): + with patch( + "prowler.providers.github.services.repository.repository_service.logger" + ) as mock_logger: + + repos = repository_service._list_repositories() + + assert len(repos) == 0 + mock_logger.error.assert_called_once() + + log_output = str(mock_logger.error.call_args) + assert "RequestException" in log_output + assert "API Error" in log_output + + def test_graphql_returns_empty_list(self): + """Test the case where GraphQL returns no repositories""" + provider = set_mocked_github_provider() + provider.repositories = [] + provider.organizations = [] + + with patch.object(Repository, "__init__", lambda x, y: None): + repository_service = Repository(provider) + repository_service.clients = [MagicMock()] + repository_service.provider = provider + + with patch.object( + repository_service, "_get_accessible_repos_graphql", return_value=[] + ): + with patch( + "prowler.providers.github.services.repository.repository_service.logger" + ) as mock_logger: + + repos = repository_service._list_repositories() + + assert len(repos) == 0 + mock_logger.warning.assert_called_with( + "Could not find any accessible repositories with the provided token." + ) + + class Test_Repository_Scoping: def setup_method(self): self.mock_repo1 = MagicMock() @@ -123,7 +224,7 @@ class Test_Repository_Scoping: self.mock_repo1.pushed_at = datetime.now(timezone.utc) self.mock_repo1.delete_branch_on_merge = True self.mock_repo1.security_and_analysis = None - self.mock_repo1.get_contents.return_value = None + self.mock_repo1.get_contents.side_effect = [None, None, None] self.mock_repo1.get_branch.side_effect = Exception("404 Not Found") self.mock_repo1.get_dependabot_alerts.side_effect = Exception("404 Not Found") @@ -138,200 +239,10 @@ class Test_Repository_Scoping: self.mock_repo2.pushed_at = datetime.now(timezone.utc) self.mock_repo2.delete_branch_on_merge = True self.mock_repo2.security_and_analysis = None - self.mock_repo2.get_contents.return_value = None + self.mock_repo2.get_contents.side_effect = [None, None, None] self.mock_repo2.get_branch.side_effect = Exception("404 Not Found") self.mock_repo2.get_dependabot_alerts.side_effect = Exception("404 Not Found") - def test_no_repository_scoping(self): - """Test that all repositories are returned when no scoping is specified""" - provider = set_mocked_github_provider() - provider.repositories = [] - provider.organizations = [] - - mock_client = MagicMock() - mock_user = MagicMock() - mock_user.get_repos.return_value = [self.mock_repo1, self.mock_repo2] - mock_client.get_user.return_value = mock_user - - with patch( - "prowler.providers.github.services.repository.repository_service.GithubService.__init__" - ): - repository_service = Repository(provider) - repository_service.clients = [mock_client] - repository_service.provider = provider - - repos = repository_service._list_repositories() - - assert len(repos) == 2 - assert 1 in repos - assert 2 in repos - assert repos[1].name == "repo1" - assert repos[2].name == "repo2" - - def test_specific_repository_scoping(self): - """Test that only specified repositories are returned""" - provider = set_mocked_github_provider() - provider.repositories = ["owner1/repo1"] - provider.organizations = [] - - mock_client = MagicMock() - mock_client.get_repo.return_value = self.mock_repo1 - - with patch( - "prowler.providers.github.services.repository.repository_service.GithubService.__init__" - ): - repository_service = Repository(provider) - repository_service.clients = [mock_client] - repository_service.provider = provider - - repos = repository_service._list_repositories() - - assert len(repos) == 1 - assert 1 in repos - assert repos[1].name == "repo1" - assert repos[1].full_name == "owner1/repo1" - mock_client.get_repo.assert_called_once_with("owner1/repo1") - - def test_multiple_repository_scoping(self): - """Test that multiple specified repositories are returned""" - provider = set_mocked_github_provider() - provider.repositories = ["owner1/repo1", "owner2/repo2"] - provider.organizations = [] - - mock_client = MagicMock() - mock_client.get_repo.side_effect = [self.mock_repo1, self.mock_repo2] - - with patch( - "prowler.providers.github.services.repository.repository_service.GithubService.__init__" - ): - repository_service = Repository(provider) - repository_service.clients = [mock_client] - repository_service.provider = provider - - repos = repository_service._list_repositories() - - assert len(repos) == 2 - assert 1 in repos - assert 2 in repos - assert repos[1].name == "repo1" - assert repos[2].name == "repo2" - assert mock_client.get_repo.call_count == 2 - - def test_invalid_repository_format(self): - """Test that invalid repository formats are skipped with warning""" - provider = set_mocked_github_provider() - provider.repositories = ["invalid-repo-name", "owner/valid-repo"] - provider.organizations = [] - - mock_client = MagicMock() - mock_client.get_repo.return_value = self.mock_repo1 - - with patch( - "prowler.providers.github.services.repository.repository_service.GithubService.__init__" - ): - repository_service = Repository(provider) - repository_service.clients = [mock_client] - repository_service.provider = provider - - with patch( - "prowler.providers.github.services.repository.repository_service.logger" - ) as mock_logger: - repos = repository_service._list_repositories() - - # Should only have the valid repository - assert len(repos) == 1 - assert 1 in repos - # Should log warning for invalid format - assert mock_logger.warning.call_count >= 1 - # Check that at least one warning is about invalid format - warning_calls = [ - call[0][0] for call in mock_logger.warning.call_args_list - ] - assert any( - "should be in 'owner/repo-name' format" in call - for call in warning_calls - ) - - def test_repository_not_found(self): - """Test that inaccessible repositories are skipped with warning""" - provider = set_mocked_github_provider() - provider.repositories = ["owner/nonexistent-repo"] - provider.organizations = [] - - mock_client = MagicMock() - mock_client.get_repo.side_effect = Exception("404 Not Found") - - with patch( - "prowler.providers.github.services.repository.repository_service.GithubService.__init__" - ): - repository_service = Repository(provider) - repository_service.clients = [mock_client] - repository_service.provider = provider - - repos = repository_service._list_repositories() - - # Should be empty since repository wasn't found - assert len(repos) == 0 - - def test_organization_scoping(self): - """Test that repositories from specified organizations are returned""" - provider = set_mocked_github_provider() - provider.repositories = [] - provider.organizations = ["org1"] - - mock_client = MagicMock() - mock_org = MagicMock() - mock_org.get_repos.return_value = [self.mock_repo1] - mock_client.get_organization.return_value = mock_org - - with patch( - "prowler.providers.github.services.repository.repository_service.GithubService.__init__" - ): - repository_service = Repository(provider) - repository_service.clients = [mock_client] - repository_service.provider = provider - - repos = repository_service._list_repositories() - - assert len(repos) == 1 - assert 1 in repos - assert repos[1].name == "repo1" - mock_client.get_organization.assert_called_once_with("org1") - - def test_organization_as_user_fallback(self): - """Test that organization scoping falls back to user when organization not found""" - provider = set_mocked_github_provider() - provider.repositories = [] - provider.organizations = ["user1"] - - mock_client = MagicMock() - # Organization lookup fails - mock_client.get_organization.side_effect = GithubException( - 404, "Not Found", None - ) - # User lookup succeeds - mock_user = MagicMock() - mock_user.get_repos.return_value = [self.mock_repo1] - mock_client.get_user.return_value = mock_user - - # Create service without calling the parent constructor - repository_service = Repository.__new__(Repository) - repository_service.clients = [mock_client] - repository_service.provider = provider - - with patch( - "prowler.providers.github.services.repository.repository_service.logger" - ) as mock_logger: - repos = repository_service._list_repositories() - - assert len(repos) == 1 - assert 1 in repos - assert repos[1].name == "repo1" - mock_client.get_organization.assert_called_once_with("user1") - mock_client.get_user.assert_called_once_with("user1") - # Should log info about trying as user - mock_logger.info.assert_called() - def test_combined_repository_and_organization_scoping(self): """Test that both repository and organization scoping can be used together""" provider = set_mocked_github_provider()