diff --git a/prowler/CHANGELOG.md b/prowler/CHANGELOG.md index 1e0a128541..73373c7414 100644 --- a/prowler/CHANGELOG.md +++ b/prowler/CHANGELOG.md @@ -14,6 +14,7 @@ All notable changes to the **Prowler SDK** are documented in this file. - Add a level for Prowler ThreatScore in the accordion in Dashboard. [(#7739)](https://github.com/prowler-cloud/prowler/pull/7739) - Add CIS 4.0 compliance framework for GCP. [(7785)](https://github.com/prowler-cloud/prowler/pull/7785) - Add `repository_has_codeowners_file` check for GitHub provider. [(#7752)](https://github.com/prowler-cloud/prowler/pull/7752) +- Add `repository_dependency_scanning_enabled` check for GitHub provider. [(#7771)](https://github.com/prowler-cloud/prowler/pull/7771) - Add `repository_secret_scanning_enabled` check for GitHub provider. [(#7759)](https://github.com/prowler-cloud/prowler/pull/7759) - Add `repository_default_branch_requires_codeowners_review` check for GitHub provider. [(#7753)](https://github.com/prowler-cloud/prowler/pull/7753) diff --git a/prowler/providers/github/services/repository/repository_dependency_scanning_enabled/__init__.py b/prowler/providers/github/services/repository/repository_dependency_scanning_enabled/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/prowler/providers/github/services/repository/repository_dependency_scanning_enabled/repository_dependency_scanning_enabled.metadata.json b/prowler/providers/github/services/repository/repository_dependency_scanning_enabled/repository_dependency_scanning_enabled.metadata.json new file mode 100644 index 0000000000..c245667ed1 --- /dev/null +++ b/prowler/providers/github/services/repository/repository_dependency_scanning_enabled/repository_dependency_scanning_enabled.metadata.json @@ -0,0 +1,30 @@ +{ + "Provider": "github", + "CheckID": "repository_dependency_scanning_enabled", + "CheckTitle": "Check if package vulnerability scanning is enabled for dependencies in the repository", + "CheckType": [], + "ServiceName": "repository", + "SubServiceName": "", + "ResourceIdTemplate": "github:user-id:repository/repository-name", + "Severity": "high", + "ResourceType": "GitHubRepository", + "Description": "Implement scanning tools to detect, prevent, and monitor known open-source vulnerabilities in packages used within the organization's projects. This check verifies that dependency/package vulnerability scanning (e.g., Dependabot alerts) is enabled for the repository.", + "Risk": "If package vulnerability scanning is not enabled, known vulnerabilities in dependencies may go undetected, increasing the risk of exploitation and security breaches.", + "RelatedUrl": "https://docs.github.com/en/code-security/dependabot/dependabot-alerts/about-dependabot-alerts", + "Remediation": { + "Code": { + "CLI": "", + "NativeIaC": "", + "Other": "", + "Terraform": "" + }, + "Recommendation": { + "Text": "Enable Dependabot alerts or another package vulnerability scanner in the repository settings to automatically detect and alert on vulnerable dependencies.", + "Url": "https://docs.github.com/en/code-security/dependabot/dependabot-alerts/about-dependabot-alerts" + } + }, + "Categories": [], + "DependsOn": [], + "RelatedTo": [], + "Notes": "" +} diff --git a/prowler/providers/github/services/repository/repository_dependency_scanning_enabled/repository_dependency_scanning_enabled.py b/prowler/providers/github/services/repository/repository_dependency_scanning_enabled/repository_dependency_scanning_enabled.py new file mode 100644 index 0000000000..f06c58135f --- /dev/null +++ b/prowler/providers/github/services/repository/repository_dependency_scanning_enabled/repository_dependency_scanning_enabled.py @@ -0,0 +1,38 @@ +from typing import List + +from prowler.lib.check.models import Check, CheckReportGithub +from prowler.providers.github.services.repository.repository_client import ( + repository_client, +) + + +class repository_dependency_scanning_enabled(Check): + """Check if package vulnerability scanning (Dependabot alerts) is enabled for dependencies in the repository + + This class verifies whether each repository has package vulnerability scanning enabled. + """ + + def execute(self) -> List[CheckReportGithub]: + """Execute the Github Repository Package Vulnerabilities Scanner check + + Iterates over all repositories and checks if package vulnerability scanning is enabled. + + Returns: + List[CheckReportGithub]: A list of reports for each repository + """ + findings = [] + for repo in repository_client.repositories.values(): + if repo.dependabot_alerts_enabled is not None: + report = CheckReportGithub( + metadata=self.metadata(), resource=repo, repository=repo.name + ) + if repo.dependabot_alerts_enabled: + report.status = "PASS" + report.status_extended = f"Repository {repo.name} has package vulnerability scanning (Dependabot alerts) enabled." + else: + report.status = "FAIL" + report.status_extended = f"Repository {repo.name} does not have package vulnerability scanning (Dependabot alerts) enabled." + + findings.append(report) + + return findings diff --git a/prowler/providers/github/services/repository/repository_service.py b/prowler/providers/github/services/repository/repository_service.py index f3a77de04e..01e7713b56 100644 --- a/prowler/providers/github/services/repository/repository_service.py +++ b/prowler/providers/github/services/repository/repository_service.py @@ -118,6 +118,7 @@ class Repository(GithubService): ) secret_scanning_enabled = False + dependabot_alerts_enabled = False try: if ( repo.security_and_analysis @@ -127,11 +128,30 @@ class Repository(GithubService): repo.security_and_analysis.secret_scanning.status == "enabled" ) + try: + # Use get_dependabot_alerts to check if Dependabot alerts are enabled + repo.get_dependabot_alerts()[0] + # 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"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" + ) + dependabot_alerts_enabled = None except Exception as error: logger.error( f"{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, @@ -151,6 +171,7 @@ class Repository(GithubService): codeowners_exists=codeowners_exists, require_code_owner_reviews=require_code_owner_reviews, secret_scanning_enabled=secret_scanning_enabled, + dependabot_alerts_enabled=dependabot_alerts_enabled, delete_branch_on_merge=delete_branch_on_merge, ) @@ -181,5 +202,6 @@ class Repo(BaseModel): codeowners_exists: Optional[bool] require_code_owner_reviews: Optional[bool] secret_scanning_enabled: Optional[bool] + dependabot_alerts_enabled: Optional[bool] delete_branch_on_merge: Optional[bool] conversation_resolution: Optional[bool] diff --git a/tests/providers/github/services/repository/repository_dependency_scanning_enabled/repository_dependency_scanning_enabled_test.py b/tests/providers/github/services/repository/repository_dependency_scanning_enabled/repository_dependency_scanning_enabled_test.py new file mode 100644 index 0000000000..9930055be8 --- /dev/null +++ b/tests/providers/github/services/repository/repository_dependency_scanning_enabled/repository_dependency_scanning_enabled_test.py @@ -0,0 +1,114 @@ +from unittest import mock + +from prowler.providers.github.services.repository.repository_service import Repo +from tests.providers.github.github_fixtures import set_mocked_github_provider + + +class Test_repository_dependency_scanning_enabled: + def test_no_repositories(self): + repository_client = mock.MagicMock + repository_client.repositories = {} + + with ( + mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=set_mocked_github_provider(), + ), + mock.patch( + "prowler.providers.github.services.repository.repository_dependency_scanning_enabled.repository_dependency_scanning_enabled.repository_client", + new=repository_client, + ), + ): + from prowler.providers.github.services.repository.repository_dependency_scanning_enabled.repository_dependency_scanning_enabled import ( + repository_dependency_scanning_enabled, + ) + + check = repository_dependency_scanning_enabled() + result = check.execute() + assert len(result) == 0 + + def test_one_repository_no_dependabot(self): + repository_client = mock.MagicMock + repo_name = "repo1" + repository_client.repositories = { + 1: Repo( + id=1, + name=repo_name, + full_name="account-name/repo1", + default_branch="main", + private=False, + securitymd=True, + require_pull_request=False, + approval_count=0, + secret_scanning_enabled=True, + dependabot_alerts_enabled=False, + ), + } + + with ( + mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=set_mocked_github_provider(), + ), + mock.patch( + "prowler.providers.github.services.repository.repository_dependency_scanning_enabled.repository_dependency_scanning_enabled.repository_client", + new=repository_client, + ), + ): + from prowler.providers.github.services.repository.repository_dependency_scanning_enabled.repository_dependency_scanning_enabled import ( + repository_dependency_scanning_enabled, + ) + + check = repository_dependency_scanning_enabled() + result = check.execute() + assert len(result) == 1 + assert result[0].resource_id == 1 + assert result[0].resource_name == repo_name + assert result[0].status == "FAIL" + assert ( + result[0].status_extended + == f"Repository {repo_name} does not have package vulnerability scanning (Dependabot alerts) enabled." + ) + + def test_one_repository_with_dependabot(self): + repository_client = mock.MagicMock + repo_name = "repo2" + repository_client.repositories = { + 2: Repo( + id=2, + name=repo_name, + full_name="account-name/repo2", + default_branch="main", + private=False, + securitymd=True, + require_pull_request=False, + approval_count=0, + secret_scanning_enabled=True, + dependabot_alerts_enabled=True, + ), + } + + with ( + mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=set_mocked_github_provider(), + ), + mock.patch( + "prowler.providers.github.services.repository.repository_dependency_scanning_enabled.repository_dependency_scanning_enabled.repository_client", + new=repository_client, + ), + ): + from prowler.providers.github.services.repository.repository_dependency_scanning_enabled.repository_dependency_scanning_enabled import ( + repository_dependency_scanning_enabled, + ) + + check = repository_dependency_scanning_enabled() + result = check.execute() + assert len(result) == 1 + assert result[0].resource_id == 2 + assert result[0].resource_name == repo_name + assert result[0].status == "PASS" + assert ( + result[0].status_extended + == f"Repository {repo_name} has package vulnerability scanning (Dependabot alerts) enabled." + )