feat: add new check

This commit is contained in:
HugoPBrito committed 2024-12-12 13:44:13 +01:00
1 parent 705f577412
commit e907abb929
7 files changed
+203 -5

No files matched your search

@@ -28,7 +28,10 @@ class repository_code_changes_multi_approval_requirement(Check):
report.status = "FAIL"
report.status_extended = f"Repository {repo.name} does not enforce at least 2 approvals for code changes."
if repo.default_branch_protection.approval_count >= 2:
if (
repo.default_branch_protection
and repo.default_branch_protection.approval_count >= 2
):
report.status = "PASS"
report.status_extended = f"Repository {repo.name} does enforce at least 2 approvals for code changes."
@@ -0,0 +1,30 @@
{
"Provider": "github",
"CheckID": "repository_merging_requires_linear_history",
"CheckTitle": "Check if repository default branch requires linear history",
"CheckType": [],
"ServiceName": "repository",
"SubServiceName": "",
"ResourceIdTemplate": "",
"Severity": "medium",
"ResourceType": "Other",
"Description": "Ensure that the repository default branch requires linear history.",
"Risk": "Allowing non-linear history can result in a cluttered and difficult-to-trace Git history, making it harder to identify specific changes, debug issues, and understand the sequence of development. This increases the risk of errors, inconsistencies, and bugs, especially in production environments.",
"RelatedUrl": "https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/managing-protected-branches/about-protected-branches#require-linear-history",
"Remediation": {
"Code": {
"CLI": "",
"NativeIaC": "",
"Other": "",
"Terraform": ""
},
"Recommendation": {
"Text": "Enforce a linear history by requiring rebase or squash merges for pull requests. This will create a clean, chronological commit history, making it easier to track changes, revert modifications, and troubleshoot any issues that arise.",
"Url": "https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/managing-protected-branches/managing-a-branch-protection-rule"
}
},
"Categories": [],
"DependsOn": [],
"RelatedTo": [],
"Notes": ""
}
@@ -0,0 +1,40 @@
from typing import List
from prowler.lib.check.models import Check, Check_Report_Github
from prowler.providers.github.services.repository.repository_client import (
repository_client,
)
class repository_merging_requires_linear_history(Check):
"""Check if a repository requires linear history on default branch
This class verifies whether each repository requires linear history on the default branch.
"""
def execute(self) -> List[Check_Report_Github]:
"""Execute the Github Repository Merging Requires Linear History check
Iterates over all repositories and checks if they require linear history on the default branch.
Returns:
List[Check_Report_Github]: A list of reports for each repository
"""
findings = []
for repo in repository_client.repositories.values():
report = Check_Report_Github(self.metadata())
report.resource_id = repo.id
report.resource_name = repo.name
report.status = "FAIL"
report.status_extended = f"Repository {repo.name} does not require linear history on default branch ({repo.default_branch})."
if (
repo.default_branch_protection
and repo.default_branch_protection.linear_history
):
report.status = "PASS"
report.status_extended = f"Repository {repo.name} does require linear history on default branch ({repo.default_branch})."
findings.append(report)
return findings
@@ -44,9 +44,13 @@ class Repository(GithubService):
if require_pr
else 0
)
required_linear_history = (
protection.required_linear_history
)
branch_protection = Protection(
require_pull_request=require_pr,
approval_count=approval_cnt,
linear_history=required_linear_history,
)
except Exception as e:
logger.warning(
@@ -73,8 +77,9 @@ class Repository(GithubService):
class Protection(BaseModel):
"""Model for Github Branch Protection"""
require_pull_request: Optional[bool] = False
approval_count: Optional[int] = 0
require_pull_request: bool = False
approval_count: int = 0
linear_history: bool = False
class Repo(BaseModel):
@@ -86,4 +91,4 @@ class Repo(BaseModel):
private: bool
default_branch: str
default_branch_protection: Optional[Protection]
securitymd: Optional[bool] = False
securitymd: bool = False
@@ -0,0 +1,117 @@
from unittest import mock
from prowler.providers.github.services.repository.repository_service import (
Protection,
Repo,
)
from tests.providers.github.github_fixtures import set_mocked_github_provider
class Test_repository_merging_requires_linear_history_test:
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_merging_requires_linear_history.repository_merging_requires_linear_history.repository_client",
new=repository_client,
),
):
from prowler.providers.github.services.repository.repository_merging_requires_linear_history.repository_merging_requires_linear_history import (
repository_merging_requires_linear_history,
)
check = repository_merging_requires_linear_history()
result = check.execute()
assert len(result) == 0
def test_linear_history_disabled(self):
repository_client = mock.MagicMock
repo_name = "repo1"
default_branch = "main"
repository_client.repositories = {
1: Repo(
id=1,
name=repo_name,
full_name="account-name/repo1",
default_branch=default_branch,
default_branch_protection=Protection(
require_pull_request=True, approval_count=2, linear_history=False
),
private=False,
securitymd=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_merging_requires_linear_history.repository_merging_requires_linear_history.repository_client",
new=repository_client,
),
):
from prowler.providers.github.services.repository.repository_merging_requires_linear_history.repository_merging_requires_linear_history import (
repository_merging_requires_linear_history,
)
check = repository_merging_requires_linear_history()
result = check.execute()
assert len(result) == 1
assert result[0].resource_id == 1
assert result[0].resource_name == "repo1"
assert result[0].status == "FAIL"
assert (
result[0].status_extended
== f"Repository {repo_name} does not require linear history on default branch ({default_branch})."
)
def test_linear_history_enabled(self):
repository_client = mock.MagicMock
repo_name = "repo1"
default_branch = "main"
repository_client.repositories = {
1: Repo(
id=1,
name=repo_name,
full_name="account-name/repo1",
private=False,
default_branch=default_branch,
default_branch_protection=Protection(
require_pull_request=True, approval_count=2, linear_history=True
),
securitymd=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_merging_requires_linear_history.repository_merging_requires_linear_history.repository_client",
new=repository_client,
),
):
from prowler.providers.github.services.repository.repository_merging_requires_linear_history.repository_merging_requires_linear_history import (
repository_merging_requires_linear_history,
)
check = repository_merging_requires_linear_history()
result = check.execute()
assert len(result) == 1
assert result[0].resource_id == 1
assert result[0].resource_name == "repo1"
assert result[0].status == "PASS"
assert (
result[0].status_extended
== f"Repository {repo_name} does require linear history on default branch ({default_branch})."
)
@@ -17,7 +17,7 @@ def mock_list_repositories(_):
private=False,
default_branch="main",
default_branch_protection=Protection(
require_pull_request=True, approval_count=2
require_pull_request=True, approval_count=2, linear_history=True
),
securitymd=True,
),
@@ -52,5 +52,8 @@ class Test_Repository_Service:
repository_service.repositories[1].default_branch_protection.approval_count
== 2
)
assert repository_service.repositories[
1
].default_branch_protection.linear_history
# Repo
assert repository_service.repositories[1].securitymd