feat(repository): add new check repository_secret_scanning_enabled (#7759)

Co-authored-by: Sergio Garcia <hello@mistercloudsec.com>
This commit is contained in:
Andoni Alonso
2025-05-22 11:23:42 +02:00
committed by GitHub
parent c44ea3943e
commit 06ff3db8af
6 changed files with 201 additions and 1 deletions
+2 -1
View File
@@ -2,7 +2,7 @@
All notable changes to the **Prowler SDK** are documented in this file.
## [5.8.0] (Prowler v5.8.0)
## [5.8.0] (Prowler v5.8.0)
### Added
- Add CIS 1.11 compliance framework for Kubernetes. [(#7790)](https://github.com/prowler-cloud/prowler/pull/7790)
@@ -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_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)
### Fixed
@@ -0,0 +1,30 @@
{
"Provider": "github",
"CheckID": "repository_secret_scanning_enabled",
"CheckTitle": "Check if secret scanning is enabled to detect sensitive data in the repository",
"CheckType": [],
"ServiceName": "repository",
"SubServiceName": "",
"ResourceIdTemplate": "github:user-id:repository/repository-name",
"Severity": "high",
"ResourceType": "GitHubRepository",
"Description": "Ensure that scanners are in place to detect and prevent sensitive data, such as confidential ID numbers, passwords, and other sensitive information, from being committed in the source code. This check verifies that secret scanning is enabled to identify and prevent sensitive data from being included in the repository.",
"Risk": "If secret scanning is not enabled, sensitive data may be inadvertently committed to the repository, increasing the risk of data breaches and exploitation by attackers.",
"RelatedUrl": "https://docs.github.com/en/code-security/secret-scanning/about-secret-scanning",
"Remediation": {
"Code": {
"CLI": "",
"NativeIaC": "",
"Other": "",
"Terraform": ""
},
"Recommendation": {
"Text": "Enable secret scanning in the repository settings to automatically detect and prevent sensitive data from being committed to the codebase.",
"Url": "https://docs.github.com/en/code-security/secret-scanning/about-secret-scanning"
}
},
"Categories": [],
"DependsOn": [],
"RelatedTo": [],
"Notes": ""
}
@@ -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_secret_scanning_enabled(Check):
"""Check if secret scanning is enabled to detect sensitive data in the repository
This class verifies whether each repository has secret scanning enabled.
"""
def execute(self) -> List[CheckReportGithub]:
"""Execute the Github Repository Secret Scanning check
Iterates over all repositories and checks if secret scanning is enabled.
Returns:
List[CheckReportGithub]: A list of reports for each repository
"""
findings = []
for repo in repository_client.repositories.values():
if repo.secret_scanning_enabled is not None:
report = CheckReportGithub(
metadata=self.metadata(), resource=repo, repository=repo.name
)
if getattr(repo, "secret_scanning_enabled", None):
report.status = "PASS"
report.status_extended = f"Repository {repo.name} has secret scanning enabled to detect sensitive data."
else:
report.status = "FAIL"
report.status_extended = f"Repository {repo.name} does not have secret scanning enabled to detect sensitive data."
findings.append(report)
return findings
@@ -117,6 +117,21 @@ class Repository(GithubService):
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
secret_scanning_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"
)
except Exception as error:
logger.error(
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
secret_scanning_enabled = None
repos[repo.id] = Repo(
id=repo.id,
name=repo.name,
@@ -135,6 +150,7 @@ class Repository(GithubService):
default_branch_protection=branch_protection,
codeowners_exists=codeowners_exists,
require_code_owner_reviews=require_code_owner_reviews,
secret_scanning_enabled=secret_scanning_enabled,
delete_branch_on_merge=delete_branch_on_merge,
)
@@ -164,5 +180,6 @@ class Repo(BaseModel):
approval_count: Optional[int]
codeowners_exists: Optional[bool]
require_code_owner_reviews: Optional[bool]
secret_scanning_enabled: Optional[bool]
delete_branch_on_merge: Optional[bool]
conversation_resolution: Optional[bool]
@@ -0,0 +1,112 @@
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_secret_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_secret_scanning_enabled.repository_secret_scanning_enabled.repository_client",
new=repository_client,
),
):
from prowler.providers.github.services.repository.repository_secret_scanning_enabled.repository_secret_scanning_enabled import (
repository_secret_scanning_enabled,
)
check = repository_secret_scanning_enabled()
result = check.execute()
assert len(result) == 0
def test_one_repository_no_secret_scanning(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=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_secret_scanning_enabled.repository_secret_scanning_enabled.repository_client",
new=repository_client,
),
):
from prowler.providers.github.services.repository.repository_secret_scanning_enabled.repository_secret_scanning_enabled import (
repository_secret_scanning_enabled,
)
check = repository_secret_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 secret scanning enabled to detect sensitive data."
)
def test_one_repository_with_secret_scanning(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,
),
}
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_secret_scanning_enabled.repository_secret_scanning_enabled.repository_client",
new=repository_client,
),
):
from prowler.providers.github.services.repository.repository_secret_scanning_enabled.repository_secret_scanning_enabled import (
repository_secret_scanning_enabled,
)
check = repository_secret_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 secret scanning enabled to detect sensitive data."
)
@@ -25,6 +25,7 @@ def mock_list_repositories(_):
approval_count=2,
codeowners_exists=True,
require_code_owner_reviews=True,
secret_scanning_enabled=True,
enforce_admins=True,
delete_branch_on_merge=True,
conversation_resolution=True,
@@ -64,6 +65,7 @@ class Test_Repository_Service:
assert repository_service.repositories[1].approval_count == 2
assert repository_service.repositories[1].codeowners_exists is True
assert repository_service.repositories[1].require_code_owner_reviews is True
assert repository_service.repositories[1].secret_scanning_enabled is True
class Test_Repository_FileExists: