from unittest.mock import MagicMock, patch import pytest from prowler.config.config import ( default_fixer_config_file_path, load_and_validate_config_file, ) from prowler.providers.common.models import Connection from prowler.providers.github.exceptions.exceptions import ( GithubEnvironmentVariableError, GithubInvalidCredentialsError, GithubInvalidProviderIdError, GithubInvalidTokenError, GithubSetUpIdentityError, GithubSetUpSessionError, ) from prowler.providers.github.github_provider import GithubProvider from prowler.providers.github.models import ( GithubAppIdentityInfo, GithubIdentityInfo, GithubSession, ) from tests.providers.github.github_fixtures import ( ACCOUNT_ID, ACCOUNT_NAME, ACCOUNT_URL, APP_ID, APP_KEY, APP_NAME, OAUTH_TOKEN, PAT_TOKEN, ) class TestGitHubProvider: def test_github_provider_PAT(self): personal_access_token = PAT_TOKEN oauth_app_token = None github_app_id = None github_app_key = None fixer_config = load_and_validate_config_file( "github", default_fixer_config_file_path ) with ( patch( "prowler.providers.github.github_provider.GithubProvider.setup_session", return_value=GithubSession(token=PAT_TOKEN, id="", key=""), ), patch( "prowler.providers.github.github_provider.GithubProvider.setup_identity", return_value=GithubIdentityInfo( account_id=ACCOUNT_ID, account_name=ACCOUNT_NAME, account_url=ACCOUNT_URL, ), ), ): provider = GithubProvider( personal_access_token, oauth_app_token, github_app_id, github_app_key, ) assert provider._type == "github" assert provider.session == GithubSession(token=PAT_TOKEN, id="", key="") assert provider.identity == GithubIdentityInfo( account_name=ACCOUNT_NAME, account_id=ACCOUNT_ID, account_url=ACCOUNT_URL, ) assert provider._audit_config == { "inactive_not_archived_days_threshold": 180, } assert provider._fixer_config == fixer_config def test_github_provider_OAuth(self): personal_access_token = None oauth_app_token = OAUTH_TOKEN github_app_id = None github_app_key = None fixer_config = load_and_validate_config_file( "github", default_fixer_config_file_path ) with ( patch( "prowler.providers.github.github_provider.GithubProvider.setup_session", return_value=GithubSession(token=OAUTH_TOKEN, id="", key=""), ), patch( "prowler.providers.github.github_provider.GithubProvider.setup_identity", return_value=GithubIdentityInfo( account_id=ACCOUNT_ID, account_name=ACCOUNT_NAME, account_url=ACCOUNT_URL, ), ), ): provider = GithubProvider( personal_access_token, oauth_app_token, github_app_id, github_app_key, ) assert provider._type == "github" assert provider.session == GithubSession(token=OAUTH_TOKEN, id="", key="") assert provider.identity == GithubIdentityInfo( account_name=ACCOUNT_NAME, account_id=ACCOUNT_ID, account_url=ACCOUNT_URL, ) assert provider._audit_config == { "inactive_not_archived_days_threshold": 180, } assert provider._fixer_config == fixer_config def test_github_provider_App(self): personal_access_token = None oauth_app_token = None github_app_id = APP_ID github_app_key = APP_KEY fixer_config = load_and_validate_config_file( "github", default_fixer_config_file_path ) with ( patch( "prowler.providers.github.github_provider.GithubProvider.setup_session", return_value=GithubSession(token="", id=APP_ID, key=APP_KEY), ), patch( "prowler.providers.github.github_provider.GithubProvider.setup_identity", return_value=GithubAppIdentityInfo( app_id=APP_ID, app_name=APP_NAME, installations=["test-org"], ), ), ): provider = GithubProvider( personal_access_token, oauth_app_token, github_app_id, github_app_key, ) assert provider._type == "github" assert provider.session == GithubSession(token="", id=APP_ID, key=APP_KEY) assert provider.identity == GithubAppIdentityInfo( app_id=APP_ID, app_name=APP_NAME, installations=["test-org"] ) assert provider._audit_config == { "inactive_not_archived_days_threshold": 180, } assert provider._fixer_config == fixer_config def test_test_connection_with_personal_access_token_success(self): """Test successful connection with personal access token.""" with ( patch( "prowler.providers.github.github_provider.GithubProvider.setup_session", return_value=GithubSession(token=PAT_TOKEN, id="", key=""), ), patch( "prowler.providers.github.github_provider.GithubProvider.setup_identity", return_value=GithubIdentityInfo( account_id=ACCOUNT_ID, account_name=ACCOUNT_NAME, account_url=ACCOUNT_URL, ), ), ): connection = GithubProvider.test_connection(personal_access_token=PAT_TOKEN) assert isinstance(connection, Connection) assert connection.is_connected is True assert connection.error is None def test_test_connection_with_oauth_app_token_success(self): """Test successful connection with OAuth app token.""" with ( patch( "prowler.providers.github.github_provider.GithubProvider.setup_session", return_value=GithubSession(token=OAUTH_TOKEN, id="", key=""), ), patch( "prowler.providers.github.github_provider.GithubProvider.setup_identity", return_value=GithubIdentityInfo( account_id=ACCOUNT_ID, account_name=ACCOUNT_NAME, account_url=ACCOUNT_URL, ), ), ): connection = GithubProvider.test_connection(oauth_app_token=OAUTH_TOKEN) assert isinstance(connection, Connection) assert connection.is_connected is True assert connection.error is None def test_test_connection_with_github_app_success(self): """Test successful connection with GitHub App credentials.""" with ( patch( "prowler.providers.github.github_provider.GithubProvider.setup_session", return_value=GithubSession(token="", id=APP_ID, key=APP_KEY), ), patch( "prowler.providers.github.github_provider.GithubProvider.setup_identity", return_value=GithubAppIdentityInfo( app_id=APP_ID, app_name=APP_NAME, installations=["test-org"] ), ), ): connection = GithubProvider.test_connection( github_app_id=APP_ID, github_app_key=APP_KEY ) assert isinstance(connection, Connection) assert connection.is_connected is True assert connection.error is None def test_test_connection_with_invalid_token_raises_exception(self): """Test connection with invalid token raises exception.""" with ( patch( "prowler.providers.github.github_provider.GithubProvider.setup_session", return_value=GithubSession(token="invalid-token", id="", key=""), ), patch( "prowler.providers.github.github_provider.GithubProvider.setup_identity", side_effect=GithubInvalidTokenError( original_exception=Exception("Invalid token") ), ), ): with pytest.raises(GithubInvalidTokenError): GithubProvider.test_connection(personal_access_token="invalid-token") def test_test_connection_with_invalid_token_no_raise(self): """Test connection with invalid token without raising exception.""" with ( patch( "prowler.providers.github.github_provider.GithubProvider.setup_session", return_value=GithubSession(token="invalid-token", id="", key=""), ), patch( "prowler.providers.github.github_provider.GithubProvider.setup_identity", side_effect=GithubInvalidTokenError( original_exception=Exception("Invalid token") ), ), ): connection = GithubProvider.test_connection( personal_access_token="invalid-token", raise_on_exception=False ) assert isinstance(connection, Connection) assert connection.is_connected is False assert isinstance(connection.error, GithubInvalidTokenError) def test_test_connection_with_invalid_app_credentials_raises_exception(self): """Test connection with invalid GitHub App credentials raises exception.""" with ( patch( "prowler.providers.github.github_provider.GithubProvider.setup_session", return_value=GithubSession(token="", id=APP_ID, key="invalid-key"), ), patch( "prowler.providers.github.github_provider.GithubProvider.setup_identity", side_effect=GithubInvalidCredentialsError( original_exception=Exception("Invalid credentials") ), ), ): with pytest.raises(GithubInvalidCredentialsError): GithubProvider.test_connection( github_app_id=APP_ID, github_app_key="invalid-key" ) def test_test_connection_with_invalid_app_credentials_no_raise(self): """Test connection with invalid GitHub App credentials without raising exception.""" with ( patch( "prowler.providers.github.github_provider.GithubProvider.setup_session", return_value=GithubSession(token="", id=APP_ID, key="invalid-key"), ), patch( "prowler.providers.github.github_provider.GithubProvider.setup_identity", side_effect=GithubInvalidCredentialsError( original_exception=Exception("Invalid credentials") ), ), ): connection = GithubProvider.test_connection( github_app_id=APP_ID, github_app_key="invalid-key", raise_on_exception=False, ) assert isinstance(connection, Connection) assert connection.is_connected is False assert isinstance(connection.error, GithubInvalidCredentialsError) def test_test_connection_setup_session_error_raises_exception(self): """Test connection when setup_session raises an exception.""" with ( patch( "prowler.providers.github.github_provider.GithubProvider.setup_session", side_effect=GithubSetUpSessionError( original_exception=Exception("Setup error") ), ), patch("prowler.providers.github.github_provider.logger") as mock_logger, ): with pytest.raises(GithubSetUpSessionError): GithubProvider.test_connection(personal_access_token=PAT_TOKEN) mock_logger.critical.assert_called_once() def test_test_connection_setup_session_error_no_raise(self): """Test connection when setup_session raises an exception without raising.""" with ( patch( "prowler.providers.github.github_provider.GithubProvider.setup_session", side_effect=GithubSetUpSessionError( original_exception=Exception("Setup error") ), ), patch("prowler.providers.github.github_provider.logger") as mock_logger, ): connection = GithubProvider.test_connection( personal_access_token=PAT_TOKEN, raise_on_exception=False ) assert isinstance(connection, Connection) assert connection.is_connected is False assert isinstance(connection.error, GithubSetUpSessionError) mock_logger.critical.assert_called_once() def test_test_connection_environment_variable_error_raises_exception(self): """Test connection when environment variable error occurs.""" with ( patch( "prowler.providers.github.github_provider.GithubProvider.setup_session", side_effect=GithubEnvironmentVariableError( file="test_file.py", message="Env error" ), ), patch("prowler.providers.github.github_provider.logger") as mock_logger, ): with pytest.raises(GithubEnvironmentVariableError): GithubProvider.test_connection(personal_access_token=PAT_TOKEN) mock_logger.critical.assert_called_once() def test_test_connection_environment_variable_error_no_raise(self): """Test connection when environment variable error occurs without raising.""" with ( patch( "prowler.providers.github.github_provider.GithubProvider.setup_session", side_effect=GithubEnvironmentVariableError( file="test_file.py", message="Env error" ), ), patch("prowler.providers.github.github_provider.logger") as mock_logger, ): connection = GithubProvider.test_connection( personal_access_token=PAT_TOKEN, raise_on_exception=False ) assert isinstance(connection, Connection) assert connection.is_connected is False assert isinstance(connection.error, GithubEnvironmentVariableError) mock_logger.critical.assert_called_once() def test_test_connection_setup_identity_error_raises_exception(self): """Test connection when setup_identity raises an exception.""" with ( patch( "prowler.providers.github.github_provider.GithubProvider.setup_session", return_value=GithubSession(token=PAT_TOKEN, id="", key=""), ), patch( "prowler.providers.github.github_provider.GithubProvider.setup_identity", side_effect=GithubSetUpIdentityError( original_exception=Exception("Identity error") ), ), ): with pytest.raises(GithubSetUpIdentityError): GithubProvider.test_connection(personal_access_token=PAT_TOKEN) def test_test_connection_setup_identity_error_no_raise(self): """Test connection when setup_identity raises an exception without raising.""" with ( patch( "prowler.providers.github.github_provider.GithubProvider.setup_session", return_value=GithubSession(token=PAT_TOKEN, id="", key=""), ), patch( "prowler.providers.github.github_provider.GithubProvider.setup_identity", side_effect=GithubSetUpIdentityError( original_exception=Exception("Identity error") ), ), ): connection = GithubProvider.test_connection( personal_access_token=PAT_TOKEN, raise_on_exception=False ) assert isinstance(connection, Connection) assert connection.is_connected is False assert isinstance(connection.error, GithubSetUpIdentityError) def test_test_connection_generic_exception_raises_exception(self): """Test connection when a generic exception occurs.""" with ( patch( "prowler.providers.github.github_provider.GithubProvider.setup_session", side_effect=Exception("Generic error"), ), patch("prowler.providers.github.github_provider.logger") as mock_logger, ): with pytest.raises(Exception) as exc_info: GithubProvider.test_connection(personal_access_token=PAT_TOKEN) assert str(exc_info.value) == "Generic error" mock_logger.critical.assert_called_once() def test_test_connection_generic_exception_no_raise(self): """Test connection when a generic exception occurs without raising.""" with ( patch( "prowler.providers.github.github_provider.GithubProvider.setup_session", side_effect=Exception("Generic error"), ), patch("prowler.providers.github.github_provider.logger") as mock_logger, ): connection = GithubProvider.test_connection( personal_access_token=PAT_TOKEN, raise_on_exception=False ) assert isinstance(connection, Connection) assert connection.is_connected is False assert isinstance(connection.error, Exception) assert str(connection.error) == "Generic error" mock_logger.critical.assert_called_once() def test_test_connection_with_provider_id(self): """Test connection with provider_id parameter (should validate provider ID).""" with ( patch( "prowler.providers.github.github_provider.GithubProvider.setup_session", return_value=GithubSession(token=PAT_TOKEN, id="", key=""), ), patch( "prowler.providers.github.github_provider.GithubProvider.setup_identity", return_value=GithubIdentityInfo( account_id=ACCOUNT_ID, account_name=ACCOUNT_NAME, account_url=ACCOUNT_URL, ), ), patch( "prowler.providers.github.github_provider.GithubProvider.validate_provider_id", return_value=None, ), ): connection = GithubProvider.test_connection( personal_access_token=PAT_TOKEN, provider_id="test-org" ) assert isinstance(connection, Connection) assert connection.is_connected is True assert connection.error is None def test_test_connection_with_invalid_provider_id_raises_exception(self): """Test connection with invalid provider_id raises exception.""" with ( patch( "prowler.providers.github.github_provider.GithubProvider.setup_session", return_value=GithubSession(token=PAT_TOKEN, id="", key=""), ), patch( "prowler.providers.github.github_provider.GithubProvider.setup_identity", return_value=GithubIdentityInfo( account_id=ACCOUNT_ID, account_name=ACCOUNT_NAME, account_url=ACCOUNT_URL, ), ), patch( "prowler.providers.github.github_provider.GithubProvider.validate_provider_id", side_effect=GithubInvalidProviderIdError( file="test_file.py", message="Invalid provider ID" ), ), ): with pytest.raises(GithubInvalidProviderIdError): GithubProvider.test_connection( personal_access_token=PAT_TOKEN, provider_id="invalid-org" ) def test_test_connection_with_invalid_provider_id_no_raise(self): """Test connection with invalid provider_id without raising exception.""" with ( patch( "prowler.providers.github.github_provider.GithubProvider.setup_session", return_value=GithubSession(token=PAT_TOKEN, id="", key=""), ), patch( "prowler.providers.github.github_provider.GithubProvider.setup_identity", return_value=GithubIdentityInfo( account_id=ACCOUNT_ID, account_name=ACCOUNT_NAME, account_url=ACCOUNT_URL, ), ), patch( "prowler.providers.github.github_provider.GithubProvider.validate_provider_id", side_effect=GithubInvalidProviderIdError( file="test_file.py", message="Invalid provider ID" ), ), ): connection = GithubProvider.test_connection( personal_access_token=PAT_TOKEN, provider_id="invalid-org", raise_on_exception=False, ) assert isinstance(connection, Connection) assert connection.is_connected is False assert isinstance(connection.error, GithubInvalidProviderIdError) def test_validate_provider_id_with_valid_user(self): """Test validate_provider_id with valid user (matches authenticated user).""" mock_session = GithubSession(token=PAT_TOKEN, id="", key="") with ( patch("prowler.providers.github.github_provider.Auth.Token"), patch("prowler.providers.github.github_provider.Github") as mock_github, patch("prowler.providers.github.github_provider.GithubRetry"), ): # Mock the GitHub client and user mock_user = MagicMock() mock_user.login = "test-user" mock_github_instance = MagicMock() mock_github_instance.get_user.return_value = mock_user mock_github.return_value = mock_github_instance # Should not raise an exception GithubProvider.validate_provider_id(mock_session, "test-user") def test_validate_provider_id_with_valid_organization(self): """Test validate_provider_id with valid organization.""" mock_session = GithubSession(token=PAT_TOKEN, id="", key="") with ( patch("prowler.providers.github.github_provider.Auth.Token"), patch("prowler.providers.github.github_provider.Github") as mock_github, patch("prowler.providers.github.github_provider.GithubRetry"), ): # Mock the GitHub client and user mock_user = MagicMock() mock_user.login = "test-user" mock_github_instance = MagicMock() mock_github_instance.get_user.return_value = mock_user mock_github_instance.get_organization.return_value = ( MagicMock() ) # Organization exists mock_github.return_value = mock_github_instance # Should not raise an exception GithubProvider.validate_provider_id(mock_session, "test-org") def test_validate_provider_id_with_invalid_provider_id(self): """Test validate_provider_id with invalid provider ID.""" mock_session = GithubSession(token=PAT_TOKEN, id="", key="") with ( patch("prowler.providers.github.github_provider.Auth.Token"), patch("prowler.providers.github.github_provider.Github") as mock_github, patch("prowler.providers.github.github_provider.GithubRetry"), ): # Mock the GitHub client and user mock_user = MagicMock() mock_user.login = "test-user" mock_github_instance = MagicMock() mock_github_instance.get_user.return_value = mock_user mock_github_instance.get_organization.side_effect = Exception("Not found") mock_github_instance.get_user.side_effect = [ mock_user, Exception("Not found"), ] mock_github.return_value = mock_github_instance with pytest.raises(GithubInvalidProviderIdError): GithubProvider.validate_provider_id(mock_session, "invalid-provider") def test_validate_provider_id_with_github_app(self): """Test validate_provider_id with GitHub App credentials.""" mock_session = GithubSession(token="", id=APP_ID, key=APP_KEY) with ( patch("prowler.providers.github.github_provider.Auth.AppAuth"), patch( "prowler.providers.github.github_provider.GithubIntegration" ) as mock_integration, patch("prowler.providers.github.github_provider.GithubRetry"), ): # Mock the GitHub integration and installations mock_installation = MagicMock() mock_installation.raw_data = {"account": {"login": "test-org"}} mock_integration_instance = MagicMock() mock_integration_instance.get_installations.return_value = [ mock_installation ] mock_integration.return_value = mock_integration_instance # Should not raise an exception GithubProvider.validate_provider_id(mock_session, "test-org") def test_validate_provider_id_with_github_app_invalid_org(self): """Test validate_provider_id with GitHub App credentials and invalid organization.""" mock_session = GithubSession(token="", id=APP_ID, key=APP_KEY) with ( patch("prowler.providers.github.github_provider.Auth.AppAuth"), patch( "prowler.providers.github.github_provider.GithubIntegration" ) as mock_integration, patch("prowler.providers.github.github_provider.GithubRetry"), ): # Mock the GitHub integration and installations mock_installation = MagicMock() mock_installation.raw_data = {"account": {"login": "other-org"}} mock_integration_instance = MagicMock() mock_integration_instance.get_installations.return_value = [ mock_installation ] mock_integration.return_value = mock_integration_instance with pytest.raises(GithubInvalidProviderIdError): GithubProvider.validate_provider_id(mock_session, "invalid-org") class Test_GithubProvider_Scoping: def setup_method(self): """Setup mock session and identity for testing""" self.mock_session = GithubSession( token=PAT_TOKEN, key="", id=0, ) self.mock_identity = GithubIdentityInfo( account_id=ACCOUNT_ID, account_name=ACCOUNT_NAME, account_url=ACCOUNT_URL, ) def test_provider_scoping_integration(self): """Test that provider correctly handles scoping parameters and integrates with services""" repositories = ["owner1/repo1", "owner2/repo2"] organizations = ["org1", "org2"] with ( patch( "prowler.providers.github.github_provider.GithubProvider.setup_session", return_value=self.mock_session, ), patch( "prowler.providers.github.github_provider.GithubProvider.setup_identity", return_value=self.mock_identity, ), ): # Test with both scoping parameters provider = GithubProvider( personal_access_token=PAT_TOKEN, repositories=repositories, organizations=organizations, ) assert provider.repositories == repositories assert provider.organizations == organizations assert provider.type == "github" assert provider.auth_method == "Personal Access Token" # Test with no scoping (backward compatibility) provider_no_scoping = GithubProvider( personal_access_token=PAT_TOKEN, ) assert provider_no_scoping.repositories == [] assert provider_no_scoping.organizations == [] # Test with None values (should convert to empty lists) provider_none = GithubProvider( personal_access_token=PAT_TOKEN, repositories=None, organizations=None, ) assert provider_none.repositories == [] assert provider_none.organizations == []