mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-10-09 21:14:22 +00:00
feat(github): support repository rulesets in default branch protection checks (#11723)
This commit is contained in:
+120
@@ -155,3 +155,123 @@ class Test_repository_default_branch_deletion_disabled_test:
|
||||
result[0].status_extended
|
||||
== f"Repository {repo_name} does deny default branch deletion."
|
||||
)
|
||||
|
||||
def test_active_ruleset_passes(self):
|
||||
repository_client = mock.MagicMock
|
||||
repo_name = "repo1"
|
||||
default_branch = Branch(
|
||||
name="main",
|
||||
protected=False,
|
||||
default_branch=True,
|
||||
require_pull_request=False,
|
||||
approval_count=0,
|
||||
required_linear_history=False,
|
||||
allow_force_pushes=True,
|
||||
branch_deletion=False,
|
||||
branch_deletion_source="ruleset",
|
||||
status_checks=False,
|
||||
enforce_admins=False,
|
||||
require_code_owner_reviews=False,
|
||||
require_signed_commits=False,
|
||||
conversation_resolution=False,
|
||||
)
|
||||
now = datetime.now(timezone.utc)
|
||||
|
||||
repository_client.repositories = {
|
||||
1: Repo(
|
||||
id=1,
|
||||
name=repo_name,
|
||||
owner="account-name",
|
||||
full_name="account-name/repo1",
|
||||
default_branch=default_branch,
|
||||
private=False,
|
||||
archived=False,
|
||||
pushed_at=now,
|
||||
securitymd=False,
|
||||
codeowners_exists=False,
|
||||
secret_scanning_enabled=False,
|
||||
dependabot_alerts_enabled=False,
|
||||
delete_branch_on_merge=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_default_branch_deletion_disabled.repository_default_branch_deletion_disabled.repository_client",
|
||||
new=repository_client,
|
||||
),
|
||||
):
|
||||
from prowler.providers.github.services.repository.repository_default_branch_deletion_disabled.repository_default_branch_deletion_disabled import (
|
||||
repository_default_branch_deletion_disabled,
|
||||
)
|
||||
|
||||
check = repository_default_branch_deletion_disabled()
|
||||
result = check.execute()
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "PASS"
|
||||
|
||||
def test_inactive_ruleset_fails(self):
|
||||
repository_client = mock.MagicMock
|
||||
repo_name = "repo1"
|
||||
default_branch = Branch(
|
||||
name="main",
|
||||
protected=False,
|
||||
default_branch=True,
|
||||
require_pull_request=False,
|
||||
approval_count=0,
|
||||
required_linear_history=False,
|
||||
allow_force_pushes=True,
|
||||
branch_deletion=True,
|
||||
branch_deletion_source="ruleset_not_active",
|
||||
status_checks=False,
|
||||
enforce_admins=False,
|
||||
require_code_owner_reviews=False,
|
||||
require_signed_commits=False,
|
||||
conversation_resolution=False,
|
||||
)
|
||||
now = datetime.now(timezone.utc)
|
||||
|
||||
repository_client.repositories = {
|
||||
1: Repo(
|
||||
id=1,
|
||||
name=repo_name,
|
||||
owner="account-name",
|
||||
full_name="account-name/repo1",
|
||||
default_branch=default_branch,
|
||||
private=False,
|
||||
archived=False,
|
||||
pushed_at=now,
|
||||
securitymd=False,
|
||||
codeowners_exists=False,
|
||||
secret_scanning_enabled=False,
|
||||
dependabot_alerts_enabled=False,
|
||||
delete_branch_on_merge=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_default_branch_deletion_disabled.repository_default_branch_deletion_disabled.repository_client",
|
||||
new=repository_client,
|
||||
),
|
||||
):
|
||||
from prowler.providers.github.services.repository.repository_default_branch_deletion_disabled.repository_default_branch_deletion_disabled import (
|
||||
repository_default_branch_deletion_disabled,
|
||||
)
|
||||
|
||||
check = repository_default_branch_deletion_disabled()
|
||||
result = check.execute()
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"Repository {repo_name} has default branch deletion disabled in a ruleset, but the ruleset is not active."
|
||||
)
|
||||
|
||||
+116
@@ -149,3 +149,119 @@ class Test_repository_default_branch_disallows_force_push_test:
|
||||
result[0].status_extended
|
||||
== f"Repository {repo_name} does deny force pushes on default branch ({default_branch})."
|
||||
)
|
||||
|
||||
def test_active_ruleset_passes(self):
|
||||
repository_client = mock.MagicMock
|
||||
repo_name = "repo1"
|
||||
default_branch = "main"
|
||||
repository_client.repositories = {
|
||||
1: Repo(
|
||||
id=1,
|
||||
name=repo_name,
|
||||
owner="account-name",
|
||||
full_name="account-name/repo1",
|
||||
default_branch=Branch(
|
||||
name=default_branch,
|
||||
protected=True,
|
||||
default_branch=True,
|
||||
require_pull_request=True,
|
||||
approval_count=1,
|
||||
required_linear_history=True,
|
||||
allow_force_pushes=False,
|
||||
allow_force_pushes_source="ruleset",
|
||||
branch_deletion=False,
|
||||
status_checks=True,
|
||||
enforce_admins=True,
|
||||
require_code_owner_reviews=True,
|
||||
require_signed_commits=True,
|
||||
conversation_resolution=True,
|
||||
),
|
||||
private=False,
|
||||
archived=False,
|
||||
pushed_at=datetime.now(timezone.utc),
|
||||
securitymd=True,
|
||||
codeowners_exists=True,
|
||||
secret_scanning_enabled=True,
|
||||
dependabot_alerts_enabled=True,
|
||||
delete_branch_on_merge=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_default_branch_disallows_force_push.repository_default_branch_disallows_force_push.repository_client",
|
||||
new=repository_client,
|
||||
),
|
||||
):
|
||||
from prowler.providers.github.services.repository.repository_default_branch_disallows_force_push.repository_default_branch_disallows_force_push import (
|
||||
repository_default_branch_disallows_force_push,
|
||||
)
|
||||
|
||||
check = repository_default_branch_disallows_force_push()
|
||||
result = check.execute()
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "PASS"
|
||||
|
||||
def test_inactive_ruleset_fails(self):
|
||||
repository_client = mock.MagicMock
|
||||
repo_name = "repo1"
|
||||
default_branch = "main"
|
||||
repository_client.repositories = {
|
||||
1: Repo(
|
||||
id=1,
|
||||
name=repo_name,
|
||||
owner="account-name",
|
||||
full_name="account-name/repo1",
|
||||
default_branch=Branch(
|
||||
name=default_branch,
|
||||
protected=False,
|
||||
default_branch=True,
|
||||
require_pull_request=False,
|
||||
approval_count=0,
|
||||
required_linear_history=False,
|
||||
allow_force_pushes=True,
|
||||
allow_force_pushes_source="ruleset_not_active",
|
||||
branch_deletion=True,
|
||||
status_checks=False,
|
||||
enforce_admins=False,
|
||||
require_code_owner_reviews=False,
|
||||
require_signed_commits=False,
|
||||
conversation_resolution=False,
|
||||
),
|
||||
private=False,
|
||||
archived=False,
|
||||
pushed_at=datetime.now(timezone.utc),
|
||||
securitymd=False,
|
||||
codeowners_exists=False,
|
||||
secret_scanning_enabled=False,
|
||||
dependabot_alerts_enabled=False,
|
||||
delete_branch_on_merge=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_default_branch_disallows_force_push.repository_default_branch_disallows_force_push.repository_client",
|
||||
new=repository_client,
|
||||
),
|
||||
):
|
||||
from prowler.providers.github.services.repository.repository_default_branch_disallows_force_push.repository_default_branch_disallows_force_push import (
|
||||
repository_default_branch_disallows_force_push,
|
||||
)
|
||||
|
||||
check = repository_default_branch_disallows_force_push()
|
||||
result = check.execute()
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"Repository {repo_name} has force pushes disallowed in a ruleset on default branch ({default_branch}), but the ruleset is not active."
|
||||
)
|
||||
|
||||
+116
@@ -149,3 +149,119 @@ class Test_repository_default_branch_protection_applies_to_admins_test:
|
||||
result[0].status_extended
|
||||
== f"Repository {repo_name} does enforce administrators to be subject to the same branch protection rules as other users."
|
||||
)
|
||||
|
||||
def test_active_ruleset_passes(self):
|
||||
repository_client = mock.MagicMock
|
||||
repo_name = "repo1"
|
||||
default_branch = "main"
|
||||
repository_client.repositories = {
|
||||
1: Repo(
|
||||
id=1,
|
||||
name=repo_name,
|
||||
owner="account-name",
|
||||
full_name="account-name/repo1",
|
||||
default_branch=Branch(
|
||||
name=default_branch,
|
||||
protected=True,
|
||||
default_branch=True,
|
||||
require_pull_request=True,
|
||||
approval_count=1,
|
||||
required_linear_history=True,
|
||||
allow_force_pushes=False,
|
||||
branch_deletion=False,
|
||||
status_checks=True,
|
||||
enforce_admins=True,
|
||||
enforce_admins_source="ruleset",
|
||||
require_code_owner_reviews=True,
|
||||
require_signed_commits=True,
|
||||
conversation_resolution=True,
|
||||
),
|
||||
private=False,
|
||||
archived=False,
|
||||
pushed_at=datetime.now(timezone.utc),
|
||||
securitymd=True,
|
||||
codeowners_exists=True,
|
||||
secret_scanning_enabled=True,
|
||||
dependabot_alerts_enabled=True,
|
||||
delete_branch_on_merge=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_default_branch_protection_applies_to_admins.repository_default_branch_protection_applies_to_admins.repository_client",
|
||||
new=repository_client,
|
||||
),
|
||||
):
|
||||
from prowler.providers.github.services.repository.repository_default_branch_protection_applies_to_admins.repository_default_branch_protection_applies_to_admins import (
|
||||
repository_default_branch_protection_applies_to_admins,
|
||||
)
|
||||
|
||||
check = repository_default_branch_protection_applies_to_admins()
|
||||
result = check.execute()
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "PASS"
|
||||
|
||||
def test_inactive_ruleset_fails(self):
|
||||
repository_client = mock.MagicMock
|
||||
repo_name = "repo1"
|
||||
default_branch = "main"
|
||||
repository_client.repositories = {
|
||||
1: Repo(
|
||||
id=1,
|
||||
name=repo_name,
|
||||
owner="account-name",
|
||||
full_name="account-name/repo1",
|
||||
default_branch=Branch(
|
||||
name=default_branch,
|
||||
protected=False,
|
||||
default_branch=True,
|
||||
require_pull_request=False,
|
||||
approval_count=0,
|
||||
required_linear_history=False,
|
||||
allow_force_pushes=True,
|
||||
branch_deletion=True,
|
||||
status_checks=False,
|
||||
enforce_admins=False,
|
||||
enforce_admins_source="ruleset_not_active",
|
||||
require_code_owner_reviews=False,
|
||||
require_signed_commits=False,
|
||||
conversation_resolution=False,
|
||||
),
|
||||
private=False,
|
||||
archived=False,
|
||||
pushed_at=datetime.now(timezone.utc),
|
||||
securitymd=False,
|
||||
codeowners_exists=False,
|
||||
secret_scanning_enabled=False,
|
||||
dependabot_alerts_enabled=False,
|
||||
delete_branch_on_merge=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_default_branch_protection_applies_to_admins.repository_default_branch_protection_applies_to_admins.repository_client",
|
||||
new=repository_client,
|
||||
),
|
||||
):
|
||||
from prowler.providers.github.services.repository.repository_default_branch_protection_applies_to_admins.repository_default_branch_protection_applies_to_admins import (
|
||||
repository_default_branch_protection_applies_to_admins,
|
||||
)
|
||||
|
||||
check = repository_default_branch_protection_applies_to_admins()
|
||||
result = check.execute()
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"Repository {repo_name} has a ruleset that would apply to administrators, but the ruleset is not active."
|
||||
)
|
||||
|
||||
+116
@@ -149,3 +149,119 @@ class Test_repository_default_branch_protection_enabled_test:
|
||||
result[0].status_extended
|
||||
== f"Repository {repo_name} does enforce branch protection on default branch ({default_branch})."
|
||||
)
|
||||
|
||||
def test_active_ruleset_passes(self):
|
||||
repository_client = mock.MagicMock
|
||||
repo_name = "repo1"
|
||||
default_branch = "main"
|
||||
repository_client.repositories = {
|
||||
1: Repo(
|
||||
id=1,
|
||||
name=repo_name,
|
||||
owner="account-name",
|
||||
full_name="account-name/repo1",
|
||||
default_branch=Branch(
|
||||
name=default_branch,
|
||||
protected=True,
|
||||
protected_source="ruleset",
|
||||
default_branch=True,
|
||||
require_pull_request=True,
|
||||
approval_count=1,
|
||||
required_linear_history=True,
|
||||
allow_force_pushes=False,
|
||||
branch_deletion=False,
|
||||
status_checks=True,
|
||||
enforce_admins=True,
|
||||
require_code_owner_reviews=True,
|
||||
require_signed_commits=True,
|
||||
conversation_resolution=True,
|
||||
),
|
||||
private=False,
|
||||
archived=False,
|
||||
pushed_at=datetime.now(timezone.utc),
|
||||
securitymd=True,
|
||||
codeowners_exists=True,
|
||||
secret_scanning_enabled=True,
|
||||
dependabot_alerts_enabled=True,
|
||||
delete_branch_on_merge=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_default_branch_protection_enabled.repository_default_branch_protection_enabled.repository_client",
|
||||
new=repository_client,
|
||||
),
|
||||
):
|
||||
from prowler.providers.github.services.repository.repository_default_branch_protection_enabled.repository_default_branch_protection_enabled import (
|
||||
repository_default_branch_protection_enabled,
|
||||
)
|
||||
|
||||
check = repository_default_branch_protection_enabled()
|
||||
result = check.execute()
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "PASS"
|
||||
|
||||
def test_inactive_ruleset_fails(self):
|
||||
repository_client = mock.MagicMock
|
||||
repo_name = "repo1"
|
||||
default_branch = "main"
|
||||
repository_client.repositories = {
|
||||
1: Repo(
|
||||
id=1,
|
||||
name=repo_name,
|
||||
owner="account-name",
|
||||
full_name="account-name/repo1",
|
||||
default_branch=Branch(
|
||||
name=default_branch,
|
||||
protected=False,
|
||||
protected_source="ruleset_not_active",
|
||||
default_branch=True,
|
||||
require_pull_request=False,
|
||||
approval_count=0,
|
||||
required_linear_history=False,
|
||||
allow_force_pushes=True,
|
||||
branch_deletion=True,
|
||||
status_checks=False,
|
||||
enforce_admins=False,
|
||||
require_code_owner_reviews=False,
|
||||
require_signed_commits=False,
|
||||
conversation_resolution=False,
|
||||
),
|
||||
private=False,
|
||||
archived=False,
|
||||
pushed_at=datetime.now(timezone.utc),
|
||||
securitymd=False,
|
||||
codeowners_exists=False,
|
||||
secret_scanning_enabled=False,
|
||||
dependabot_alerts_enabled=False,
|
||||
delete_branch_on_merge=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_default_branch_protection_enabled.repository_default_branch_protection_enabled.repository_client",
|
||||
new=repository_client,
|
||||
),
|
||||
):
|
||||
from prowler.providers.github.services.repository.repository_default_branch_protection_enabled.repository_default_branch_protection_enabled import (
|
||||
repository_default_branch_protection_enabled,
|
||||
)
|
||||
|
||||
check = repository_default_branch_protection_enabled()
|
||||
result = check.execute()
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"Repository {repo_name} has a ruleset configured on default branch ({default_branch}), but the ruleset is not active."
|
||||
)
|
||||
|
||||
+114
@@ -147,3 +147,117 @@ class Test_repository_default_branch_requires_codeowners_review:
|
||||
result[0].status_extended
|
||||
== f"Repository {repo_name} requires code owner approval for changes to owned code."
|
||||
)
|
||||
|
||||
def test_active_ruleset_passes(self):
|
||||
repository_client = mock.MagicMock
|
||||
repo_name = "repo1"
|
||||
repository_client.repositories = {
|
||||
1: Repo(
|
||||
id=1,
|
||||
name=repo_name,
|
||||
owner="account-name",
|
||||
full_name="account-name/repo1",
|
||||
default_branch=Branch(
|
||||
name="main",
|
||||
protected=True,
|
||||
default_branch=True,
|
||||
require_pull_request=True,
|
||||
approval_count=1,
|
||||
required_linear_history=True,
|
||||
allow_force_pushes=False,
|
||||
branch_deletion=False,
|
||||
status_checks=True,
|
||||
enforce_admins=True,
|
||||
require_code_owner_reviews=True,
|
||||
require_code_owner_reviews_source="ruleset",
|
||||
require_signed_commits=True,
|
||||
conversation_resolution=True,
|
||||
),
|
||||
private=False,
|
||||
archived=False,
|
||||
pushed_at=datetime.now(timezone.utc),
|
||||
securitymd=True,
|
||||
codeowners_exists=True,
|
||||
secret_scanning_enabled=True,
|
||||
dependabot_alerts_enabled=True,
|
||||
delete_branch_on_merge=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_default_branch_requires_codeowners_review.repository_default_branch_requires_codeowners_review.repository_client",
|
||||
new=repository_client,
|
||||
),
|
||||
):
|
||||
from prowler.providers.github.services.repository.repository_default_branch_requires_codeowners_review.repository_default_branch_requires_codeowners_review import (
|
||||
repository_default_branch_requires_codeowners_review,
|
||||
)
|
||||
|
||||
check = repository_default_branch_requires_codeowners_review()
|
||||
result = check.execute()
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "PASS"
|
||||
|
||||
def test_inactive_ruleset_fails(self):
|
||||
repository_client = mock.MagicMock
|
||||
repo_name = "repo1"
|
||||
repository_client.repositories = {
|
||||
1: Repo(
|
||||
id=1,
|
||||
name=repo_name,
|
||||
owner="account-name",
|
||||
full_name="account-name/repo1",
|
||||
default_branch=Branch(
|
||||
name="main",
|
||||
protected=False,
|
||||
default_branch=True,
|
||||
require_pull_request=False,
|
||||
approval_count=0,
|
||||
required_linear_history=False,
|
||||
allow_force_pushes=True,
|
||||
branch_deletion=True,
|
||||
status_checks=False,
|
||||
enforce_admins=False,
|
||||
require_code_owner_reviews=False,
|
||||
require_code_owner_reviews_source="ruleset_not_active",
|
||||
require_signed_commits=False,
|
||||
conversation_resolution=False,
|
||||
),
|
||||
private=False,
|
||||
archived=False,
|
||||
pushed_at=datetime.now(timezone.utc),
|
||||
securitymd=True,
|
||||
codeowners_exists=False,
|
||||
secret_scanning_enabled=False,
|
||||
dependabot_alerts_enabled=False,
|
||||
delete_branch_on_merge=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_default_branch_requires_codeowners_review.repository_default_branch_requires_codeowners_review.repository_client",
|
||||
new=repository_client,
|
||||
),
|
||||
):
|
||||
from prowler.providers.github.services.repository.repository_default_branch_requires_codeowners_review.repository_default_branch_requires_codeowners_review import (
|
||||
repository_default_branch_requires_codeowners_review,
|
||||
)
|
||||
|
||||
check = repository_default_branch_requires_codeowners_review()
|
||||
result = check.execute()
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"Repository {repo_name} has code owner approval configured in a ruleset, but the ruleset is not active."
|
||||
)
|
||||
|
||||
+116
@@ -149,3 +149,119 @@ class Test_repository_default_branch_requires_conversation_resolution_test:
|
||||
result[0].status_extended
|
||||
== f"Repository {repo_name} does require conversation resolution on default branch ({default_branch})."
|
||||
)
|
||||
|
||||
def test_active_ruleset_passes(self):
|
||||
repository_client = mock.MagicMock
|
||||
repo_name = "repo1"
|
||||
default_branch = "main"
|
||||
repository_client.repositories = {
|
||||
1: Repo(
|
||||
id=1,
|
||||
name=repo_name,
|
||||
owner="account-name",
|
||||
full_name="account-name/repo1",
|
||||
default_branch=Branch(
|
||||
name=default_branch,
|
||||
protected=True,
|
||||
default_branch=True,
|
||||
require_pull_request=True,
|
||||
approval_count=1,
|
||||
required_linear_history=True,
|
||||
allow_force_pushes=False,
|
||||
branch_deletion=False,
|
||||
status_checks=True,
|
||||
enforce_admins=True,
|
||||
require_code_owner_reviews=True,
|
||||
require_signed_commits=True,
|
||||
conversation_resolution=True,
|
||||
conversation_resolution_source="ruleset",
|
||||
),
|
||||
private=False,
|
||||
archived=False,
|
||||
pushed_at=datetime.now(timezone.utc),
|
||||
securitymd=True,
|
||||
codeowners_exists=True,
|
||||
secret_scanning_enabled=True,
|
||||
dependabot_alerts_enabled=True,
|
||||
delete_branch_on_merge=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_default_branch_requires_conversation_resolution.repository_default_branch_requires_conversation_resolution.repository_client",
|
||||
new=repository_client,
|
||||
),
|
||||
):
|
||||
from prowler.providers.github.services.repository.repository_default_branch_requires_conversation_resolution.repository_default_branch_requires_conversation_resolution import (
|
||||
repository_default_branch_requires_conversation_resolution,
|
||||
)
|
||||
|
||||
check = repository_default_branch_requires_conversation_resolution()
|
||||
result = check.execute()
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "PASS"
|
||||
|
||||
def test_inactive_ruleset_fails(self):
|
||||
repository_client = mock.MagicMock
|
||||
repo_name = "repo1"
|
||||
default_branch = "main"
|
||||
repository_client.repositories = {
|
||||
1: Repo(
|
||||
id=1,
|
||||
name=repo_name,
|
||||
owner="account-name",
|
||||
full_name="account-name/repo1",
|
||||
default_branch=Branch(
|
||||
name=default_branch,
|
||||
protected=False,
|
||||
default_branch=True,
|
||||
require_pull_request=False,
|
||||
approval_count=0,
|
||||
required_linear_history=False,
|
||||
allow_force_pushes=True,
|
||||
branch_deletion=True,
|
||||
status_checks=False,
|
||||
enforce_admins=False,
|
||||
require_code_owner_reviews=False,
|
||||
require_signed_commits=False,
|
||||
conversation_resolution=False,
|
||||
conversation_resolution_source="ruleset_not_active",
|
||||
),
|
||||
private=False,
|
||||
archived=False,
|
||||
pushed_at=datetime.now(timezone.utc),
|
||||
securitymd=False,
|
||||
codeowners_exists=False,
|
||||
secret_scanning_enabled=False,
|
||||
dependabot_alerts_enabled=False,
|
||||
delete_branch_on_merge=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_default_branch_requires_conversation_resolution.repository_default_branch_requires_conversation_resolution.repository_client",
|
||||
new=repository_client,
|
||||
),
|
||||
):
|
||||
from prowler.providers.github.services.repository.repository_default_branch_requires_conversation_resolution.repository_default_branch_requires_conversation_resolution import (
|
||||
repository_default_branch_requires_conversation_resolution,
|
||||
)
|
||||
|
||||
check = repository_default_branch_requires_conversation_resolution()
|
||||
result = check.execute()
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"Repository {repo_name} has conversation resolution configured in a ruleset on default branch ({default_branch}), but the ruleset is not active."
|
||||
)
|
||||
|
||||
+116
@@ -149,3 +149,119 @@ class Test_repository_default_branch_requires_linear_history_test:
|
||||
result[0].status_extended
|
||||
== f"Repository {repo_name} does require linear history on default branch ({default_branch})."
|
||||
)
|
||||
|
||||
def test_active_ruleset_passes(self):
|
||||
repository_client = mock.MagicMock
|
||||
repo_name = "repo1"
|
||||
default_branch = "main"
|
||||
repository_client.repositories = {
|
||||
1: Repo(
|
||||
id=1,
|
||||
name=repo_name,
|
||||
owner="account-name",
|
||||
full_name="account-name/repo1",
|
||||
default_branch=Branch(
|
||||
name=default_branch,
|
||||
protected=True,
|
||||
default_branch=True,
|
||||
require_pull_request=True,
|
||||
approval_count=1,
|
||||
required_linear_history=True,
|
||||
required_linear_history_source="ruleset",
|
||||
allow_force_pushes=False,
|
||||
branch_deletion=False,
|
||||
status_checks=True,
|
||||
enforce_admins=True,
|
||||
require_code_owner_reviews=True,
|
||||
require_signed_commits=True,
|
||||
conversation_resolution=True,
|
||||
),
|
||||
private=False,
|
||||
archived=False,
|
||||
pushed_at=datetime.now(timezone.utc),
|
||||
securitymd=True,
|
||||
codeowners_exists=True,
|
||||
secret_scanning_enabled=True,
|
||||
dependabot_alerts_enabled=True,
|
||||
delete_branch_on_merge=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_default_branch_requires_linear_history.repository_default_branch_requires_linear_history.repository_client",
|
||||
new=repository_client,
|
||||
),
|
||||
):
|
||||
from prowler.providers.github.services.repository.repository_default_branch_requires_linear_history.repository_default_branch_requires_linear_history import (
|
||||
repository_default_branch_requires_linear_history,
|
||||
)
|
||||
|
||||
check = repository_default_branch_requires_linear_history()
|
||||
result = check.execute()
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "PASS"
|
||||
|
||||
def test_inactive_ruleset_fails(self):
|
||||
repository_client = mock.MagicMock
|
||||
repo_name = "repo1"
|
||||
default_branch = "main"
|
||||
repository_client.repositories = {
|
||||
1: Repo(
|
||||
id=1,
|
||||
name=repo_name,
|
||||
owner="account-name",
|
||||
full_name="account-name/repo1",
|
||||
default_branch=Branch(
|
||||
name=default_branch,
|
||||
protected=False,
|
||||
default_branch=True,
|
||||
require_pull_request=False,
|
||||
approval_count=0,
|
||||
required_linear_history=False,
|
||||
required_linear_history_source="ruleset_not_active",
|
||||
allow_force_pushes=True,
|
||||
branch_deletion=True,
|
||||
status_checks=False,
|
||||
enforce_admins=False,
|
||||
require_code_owner_reviews=False,
|
||||
require_signed_commits=False,
|
||||
conversation_resolution=False,
|
||||
),
|
||||
private=False,
|
||||
archived=False,
|
||||
pushed_at=datetime.now(timezone.utc),
|
||||
securitymd=False,
|
||||
codeowners_exists=False,
|
||||
secret_scanning_enabled=False,
|
||||
dependabot_alerts_enabled=False,
|
||||
delete_branch_on_merge=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_default_branch_requires_linear_history.repository_default_branch_requires_linear_history.repository_client",
|
||||
new=repository_client,
|
||||
),
|
||||
):
|
||||
from prowler.providers.github.services.repository.repository_default_branch_requires_linear_history.repository_default_branch_requires_linear_history import (
|
||||
repository_default_branch_requires_linear_history,
|
||||
)
|
||||
|
||||
check = repository_default_branch_requires_linear_history()
|
||||
result = check.execute()
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"Repository {repo_name} has linear history configured in a ruleset on default branch ({default_branch}), but the ruleset is not active."
|
||||
)
|
||||
|
||||
+114
@@ -207,3 +207,117 @@ class Test_repository_default_branch_requires_multiple_approvals:
|
||||
result[0].status_extended
|
||||
== f"Repository {repo_name} does enforce at least 2 approvals for code changes."
|
||||
)
|
||||
|
||||
def test_active_ruleset_passes(self):
|
||||
repository_client = mock.MagicMock
|
||||
repo_name = "repo1"
|
||||
repository_client.repositories = {
|
||||
1: Repo(
|
||||
id=1,
|
||||
name=repo_name,
|
||||
owner="account-name",
|
||||
full_name="account-name/repo1",
|
||||
default_branch=Branch(
|
||||
name="main",
|
||||
protected=True,
|
||||
default_branch=True,
|
||||
require_pull_request=True,
|
||||
approval_count=2,
|
||||
approval_count_source="ruleset",
|
||||
required_linear_history=False,
|
||||
allow_force_pushes=True,
|
||||
branch_deletion=True,
|
||||
status_checks=False,
|
||||
enforce_admins=False,
|
||||
require_code_owner_reviews=False,
|
||||
require_signed_commits=False,
|
||||
conversation_resolution=False,
|
||||
),
|
||||
private=False,
|
||||
archived=False,
|
||||
pushed_at=datetime.now(timezone.utc),
|
||||
securitymd=True,
|
||||
codeowners_exists=False,
|
||||
secret_scanning_enabled=False,
|
||||
dependabot_alerts_enabled=False,
|
||||
delete_branch_on_merge=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_default_branch_requires_multiple_approvals.repository_default_branch_requires_multiple_approvals.repository_client",
|
||||
new=repository_client,
|
||||
),
|
||||
):
|
||||
from prowler.providers.github.services.repository.repository_default_branch_requires_multiple_approvals.repository_default_branch_requires_multiple_approvals import (
|
||||
repository_default_branch_requires_multiple_approvals,
|
||||
)
|
||||
|
||||
check = repository_default_branch_requires_multiple_approvals()
|
||||
result = check.execute()
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "PASS"
|
||||
|
||||
def test_inactive_ruleset_fails(self):
|
||||
repository_client = mock.MagicMock
|
||||
repo_name = "repo1"
|
||||
repository_client.repositories = {
|
||||
1: Repo(
|
||||
id=1,
|
||||
name=repo_name,
|
||||
owner="account-name",
|
||||
full_name="account-name/repo1",
|
||||
default_branch=Branch(
|
||||
name="main",
|
||||
protected=False,
|
||||
default_branch=True,
|
||||
require_pull_request=True,
|
||||
approval_count=0,
|
||||
approval_count_source="ruleset_not_active",
|
||||
required_linear_history=False,
|
||||
allow_force_pushes=True,
|
||||
branch_deletion=True,
|
||||
status_checks=False,
|
||||
enforce_admins=False,
|
||||
require_code_owner_reviews=False,
|
||||
require_signed_commits=False,
|
||||
conversation_resolution=False,
|
||||
),
|
||||
private=False,
|
||||
archived=False,
|
||||
pushed_at=datetime.now(timezone.utc),
|
||||
securitymd=False,
|
||||
codeowners_exists=False,
|
||||
secret_scanning_enabled=False,
|
||||
dependabot_alerts_enabled=False,
|
||||
delete_branch_on_merge=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_default_branch_requires_multiple_approvals.repository_default_branch_requires_multiple_approvals.repository_client",
|
||||
new=repository_client,
|
||||
),
|
||||
):
|
||||
from prowler.providers.github.services.repository.repository_default_branch_requires_multiple_approvals.repository_default_branch_requires_multiple_approvals import (
|
||||
repository_default_branch_requires_multiple_approvals,
|
||||
)
|
||||
|
||||
check = repository_default_branch_requires_multiple_approvals()
|
||||
result = check.execute()
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"Repository {repo_name} has at least 2 approvals configured in a ruleset, but the ruleset is not active."
|
||||
)
|
||||
|
||||
+116
@@ -149,3 +149,119 @@ class Test_repository_default_branch_requires_signed_commits:
|
||||
result[0].status_extended
|
||||
== f"Repository {repo_name} does require signed commits on default branch ({default_branch})."
|
||||
)
|
||||
|
||||
def test_active_ruleset_passes(self):
|
||||
repository_client = mock.MagicMock
|
||||
repo_name = "repo1"
|
||||
default_branch = "main"
|
||||
repository_client.repositories = {
|
||||
1: Repo(
|
||||
id=1,
|
||||
name=repo_name,
|
||||
owner="account-name",
|
||||
full_name="account-name/repo1",
|
||||
default_branch=Branch(
|
||||
name=default_branch,
|
||||
protected=True,
|
||||
default_branch=True,
|
||||
require_pull_request=True,
|
||||
approval_count=1,
|
||||
required_linear_history=True,
|
||||
allow_force_pushes=False,
|
||||
branch_deletion=False,
|
||||
status_checks=True,
|
||||
enforce_admins=True,
|
||||
require_code_owner_reviews=True,
|
||||
require_signed_commits=True,
|
||||
require_signed_commits_source="ruleset",
|
||||
conversation_resolution=True,
|
||||
),
|
||||
private=False,
|
||||
archived=False,
|
||||
pushed_at=datetime.now(timezone.utc),
|
||||
securitymd=True,
|
||||
codeowners_exists=True,
|
||||
secret_scanning_enabled=True,
|
||||
dependabot_alerts_enabled=True,
|
||||
delete_branch_on_merge=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_default_branch_requires_signed_commits.repository_default_branch_requires_signed_commits.repository_client",
|
||||
new=repository_client,
|
||||
),
|
||||
):
|
||||
from prowler.providers.github.services.repository.repository_default_branch_requires_signed_commits.repository_default_branch_requires_signed_commits import (
|
||||
repository_default_branch_requires_signed_commits,
|
||||
)
|
||||
|
||||
check = repository_default_branch_requires_signed_commits()
|
||||
result = check.execute()
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "PASS"
|
||||
|
||||
def test_inactive_ruleset_fails(self):
|
||||
repository_client = mock.MagicMock
|
||||
repo_name = "repo1"
|
||||
default_branch = "main"
|
||||
repository_client.repositories = {
|
||||
1: Repo(
|
||||
id=1,
|
||||
name=repo_name,
|
||||
owner="account-name",
|
||||
full_name="account-name/repo1",
|
||||
default_branch=Branch(
|
||||
name=default_branch,
|
||||
protected=False,
|
||||
default_branch=True,
|
||||
require_pull_request=False,
|
||||
approval_count=0,
|
||||
required_linear_history=False,
|
||||
allow_force_pushes=True,
|
||||
branch_deletion=True,
|
||||
status_checks=False,
|
||||
enforce_admins=False,
|
||||
require_code_owner_reviews=False,
|
||||
require_signed_commits=False,
|
||||
require_signed_commits_source="ruleset_not_active",
|
||||
conversation_resolution=False,
|
||||
),
|
||||
private=False,
|
||||
archived=False,
|
||||
pushed_at=datetime.now(timezone.utc),
|
||||
securitymd=False,
|
||||
codeowners_exists=False,
|
||||
secret_scanning_enabled=False,
|
||||
dependabot_alerts_enabled=False,
|
||||
delete_branch_on_merge=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_default_branch_requires_signed_commits.repository_default_branch_requires_signed_commits.repository_client",
|
||||
new=repository_client,
|
||||
),
|
||||
):
|
||||
from prowler.providers.github.services.repository.repository_default_branch_requires_signed_commits.repository_default_branch_requires_signed_commits import (
|
||||
repository_default_branch_requires_signed_commits,
|
||||
)
|
||||
|
||||
check = repository_default_branch_requires_signed_commits()
|
||||
result = check.execute()
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"Repository {repo_name} has signed commits configured in a ruleset on default branch ({default_branch}), but the ruleset is not active."
|
||||
)
|
||||
|
||||
+118
@@ -151,3 +151,121 @@ class Test_repository_default_branch_status_checks_required_test:
|
||||
result[0].status_extended
|
||||
== f"Repository {repo_name} does enforce status checks."
|
||||
)
|
||||
|
||||
def test_active_ruleset_passes(self):
|
||||
repository_client = mock.MagicMock
|
||||
repo_name = "repo1"
|
||||
default_branch = "main"
|
||||
repository_client.repositories = {
|
||||
1: Repo(
|
||||
id=1,
|
||||
name=repo_name,
|
||||
owner="account-name",
|
||||
full_name="account-name/repo1",
|
||||
private=False,
|
||||
default_branch=Branch(
|
||||
name=default_branch,
|
||||
protected=False,
|
||||
default_branch=True,
|
||||
require_pull_request=False,
|
||||
approval_count=0,
|
||||
required_linear_history=False,
|
||||
allow_force_pushes=True,
|
||||
branch_deletion=True,
|
||||
status_checks=True,
|
||||
status_checks_source="ruleset",
|
||||
enforce_admins=False,
|
||||
require_code_owner_reviews=False,
|
||||
require_signed_commits=False,
|
||||
conversation_resolution=False,
|
||||
),
|
||||
status_checks=True,
|
||||
archived=False,
|
||||
pushed_at=datetime.now(timezone.utc),
|
||||
securitymd=True,
|
||||
codeowners_exists=False,
|
||||
secret_scanning_enabled=False,
|
||||
dependabot_alerts_enabled=False,
|
||||
delete_branch_on_merge=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_default_branch_status_checks_required.repository_default_branch_status_checks_required.repository_client",
|
||||
new=repository_client,
|
||||
),
|
||||
):
|
||||
from prowler.providers.github.services.repository.repository_default_branch_status_checks_required.repository_default_branch_status_checks_required import (
|
||||
repository_default_branch_status_checks_required,
|
||||
)
|
||||
|
||||
check = repository_default_branch_status_checks_required()
|
||||
result = check.execute()
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "PASS"
|
||||
|
||||
def test_inactive_ruleset_fails(self):
|
||||
repository_client = mock.MagicMock
|
||||
repo_name = "repo1"
|
||||
default_branch = "main"
|
||||
repository_client.repositories = {
|
||||
1: Repo(
|
||||
id=1,
|
||||
name=repo_name,
|
||||
owner="account-name",
|
||||
full_name="account-name/repo1",
|
||||
default_branch=Branch(
|
||||
name=default_branch,
|
||||
protected=False,
|
||||
default_branch=True,
|
||||
require_pull_request=False,
|
||||
approval_count=0,
|
||||
required_linear_history=False,
|
||||
allow_force_pushes=True,
|
||||
branch_deletion=True,
|
||||
status_checks=False,
|
||||
status_checks_source="ruleset_not_active",
|
||||
enforce_admins=False,
|
||||
require_code_owner_reviews=False,
|
||||
require_signed_commits=False,
|
||||
conversation_resolution=False,
|
||||
),
|
||||
status_checks=False,
|
||||
archived=False,
|
||||
pushed_at=datetime.now(timezone.utc),
|
||||
private=False,
|
||||
securitymd=False,
|
||||
codeowners_exists=False,
|
||||
secret_scanning_enabled=False,
|
||||
dependabot_alerts_enabled=False,
|
||||
delete_branch_on_merge=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_default_branch_status_checks_required.repository_default_branch_status_checks_required.repository_client",
|
||||
new=repository_client,
|
||||
),
|
||||
):
|
||||
from prowler.providers.github.services.repository.repository_default_branch_status_checks_required.repository_default_branch_status_checks_required import (
|
||||
repository_default_branch_status_checks_required,
|
||||
)
|
||||
|
||||
check = repository_default_branch_status_checks_required()
|
||||
result = check.execute()
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"Repository {repo_name} has status checks configured in a ruleset, but the ruleset is not active."
|
||||
)
|
||||
|
||||
@@ -464,7 +464,7 @@ class Test_Repository_ErrorHandling:
|
||||
assert "Rate limit exceeded" in str(mock_logger.error.call_args)
|
||||
|
||||
|
||||
class Test_Repository_DismissStaleReviewsRulesets:
|
||||
class Test_Repository_BranchProtectionRulesets:
|
||||
def setup_method(self):
|
||||
self.repository_service = Repository.__new__(Repository)
|
||||
self.repository_service.provider = set_mocked_github_provider()
|
||||
@@ -550,6 +550,27 @@ class Test_Repository_DismissStaleReviewsRulesets:
|
||||
],
|
||||
}
|
||||
|
||||
def _build_ruleset(
|
||||
self,
|
||||
*,
|
||||
enforcement,
|
||||
include,
|
||||
rules,
|
||||
bypass_actors=None,
|
||||
ruleset_id=201,
|
||||
):
|
||||
return {
|
||||
"id": ruleset_id,
|
||||
"name": "Branch protection ruleset",
|
||||
"target": "branch",
|
||||
"source_type": "Repository",
|
||||
"source": "owner1/repo1",
|
||||
"enforcement": enforcement,
|
||||
"bypass_actors": bypass_actors or [],
|
||||
"conditions": {"ref_name": {"include": include, "exclude": []}},
|
||||
"rules": rules,
|
||||
}
|
||||
|
||||
def test_process_repository_uses_classic_branch_protection(self):
|
||||
repo = self._build_repo(branch_protected=True, dismiss_stale_reviews=True)
|
||||
repos = {}
|
||||
@@ -606,3 +627,320 @@ class Test_Repository_DismissStaleReviewsRulesets:
|
||||
assert (
|
||||
repos[1].default_branch.dismiss_stale_reviews_source == "ruleset_not_active"
|
||||
)
|
||||
|
||||
def test_ruleset_non_fast_forward_disallows_force_push(self):
|
||||
repo = self._build_repo(
|
||||
branch_protected=False,
|
||||
ruleset_details=[
|
||||
self._build_ruleset(
|
||||
enforcement="active",
|
||||
include=["~DEFAULT_BRANCH"],
|
||||
rules=[{"type": "non_fast_forward"}],
|
||||
)
|
||||
],
|
||||
)
|
||||
repos = {}
|
||||
|
||||
self.repository_service._process_repository(repo, repos)
|
||||
|
||||
assert repos[1].default_branch.allow_force_pushes is False
|
||||
assert repos[1].default_branch.allow_force_pushes_source == "ruleset"
|
||||
|
||||
def test_ruleset_non_fast_forward_inactive_keeps_force_push_allowed(self):
|
||||
repo = self._build_repo(
|
||||
branch_protected=False,
|
||||
ruleset_details=[
|
||||
self._build_ruleset(
|
||||
enforcement="disabled",
|
||||
include=["~DEFAULT_BRANCH"],
|
||||
rules=[{"type": "non_fast_forward"}],
|
||||
)
|
||||
],
|
||||
)
|
||||
repos = {}
|
||||
|
||||
self.repository_service._process_repository(repo, repos)
|
||||
|
||||
assert repos[1].default_branch.allow_force_pushes is True
|
||||
assert repos[1].default_branch.allow_force_pushes_source == "ruleset_not_active"
|
||||
|
||||
def test_classic_protection_takes_precedence_over_inactive_ruleset(self):
|
||||
# Classic protection already disallows force pushes, so an inactive ruleset
|
||||
# must not downgrade the result.
|
||||
repo = self._build_repo(
|
||||
branch_protected=True,
|
||||
ruleset_details=[
|
||||
self._build_ruleset(
|
||||
enforcement="disabled",
|
||||
include=["~DEFAULT_BRANCH"],
|
||||
rules=[{"type": "non_fast_forward"}],
|
||||
)
|
||||
],
|
||||
)
|
||||
repos = {}
|
||||
|
||||
self.repository_service._process_repository(repo, repos)
|
||||
|
||||
assert repos[1].default_branch.allow_force_pushes is False
|
||||
assert repos[1].default_branch.allow_force_pushes_source == "classic"
|
||||
|
||||
def test_ruleset_required_signatures_requires_signed_commits(self):
|
||||
repo = self._build_repo(
|
||||
branch_protected=False,
|
||||
ruleset_details=[
|
||||
self._build_ruleset(
|
||||
enforcement="active",
|
||||
include=["~DEFAULT_BRANCH"],
|
||||
rules=[{"type": "required_signatures"}],
|
||||
)
|
||||
],
|
||||
)
|
||||
repos = {}
|
||||
|
||||
self.repository_service._process_repository(repo, repos)
|
||||
|
||||
assert repos[1].default_branch.require_signed_commits is True
|
||||
assert repos[1].default_branch.require_signed_commits_source == "ruleset"
|
||||
|
||||
def test_ruleset_required_linear_history(self):
|
||||
repo = self._build_repo(
|
||||
branch_protected=False,
|
||||
ruleset_details=[
|
||||
self._build_ruleset(
|
||||
enforcement="active",
|
||||
include=["~DEFAULT_BRANCH"],
|
||||
rules=[{"type": "required_linear_history"}],
|
||||
)
|
||||
],
|
||||
)
|
||||
repos = {}
|
||||
|
||||
self.repository_service._process_repository(repo, repos)
|
||||
|
||||
assert repos[1].default_branch.required_linear_history is True
|
||||
assert repos[1].default_branch.required_linear_history_source == "ruleset"
|
||||
|
||||
def test_ruleset_required_status_checks(self):
|
||||
repo = self._build_repo(
|
||||
branch_protected=False,
|
||||
ruleset_details=[
|
||||
self._build_ruleset(
|
||||
enforcement="active",
|
||||
include=["~DEFAULT_BRANCH"],
|
||||
rules=[
|
||||
{
|
||||
"type": "required_status_checks",
|
||||
"parameters": {
|
||||
"required_status_checks": [{"context": "ci/build"}],
|
||||
"strict_required_status_checks_policy": True,
|
||||
},
|
||||
}
|
||||
],
|
||||
)
|
||||
],
|
||||
)
|
||||
repos = {}
|
||||
|
||||
self.repository_service._process_repository(repo, repos)
|
||||
|
||||
assert repos[1].default_branch.status_checks is True
|
||||
assert repos[1].default_branch.status_checks_source == "ruleset"
|
||||
|
||||
def test_ruleset_required_status_checks_without_configured_checks(self):
|
||||
# A required_status_checks rule with an empty list enforces nothing, so it
|
||||
# must not be treated as a passing status-checks requirement.
|
||||
repo = self._build_repo(
|
||||
branch_protected=False,
|
||||
ruleset_details=[
|
||||
self._build_ruleset(
|
||||
enforcement="active",
|
||||
include=["~DEFAULT_BRANCH"],
|
||||
rules=[
|
||||
{
|
||||
"type": "required_status_checks",
|
||||
"parameters": {
|
||||
"required_status_checks": [],
|
||||
"strict_required_status_checks_policy": True,
|
||||
},
|
||||
}
|
||||
],
|
||||
)
|
||||
],
|
||||
)
|
||||
repos = {}
|
||||
|
||||
self.repository_service._process_repository(repo, repos)
|
||||
|
||||
assert repos[1].default_branch.status_checks is False
|
||||
assert repos[1].default_branch.status_checks_source is None
|
||||
|
||||
def test_ruleset_deletion_disables_branch_deletion(self):
|
||||
repo = self._build_repo(
|
||||
branch_protected=False,
|
||||
ruleset_details=[
|
||||
self._build_ruleset(
|
||||
enforcement="active",
|
||||
include=["~DEFAULT_BRANCH"],
|
||||
rules=[{"type": "deletion"}],
|
||||
)
|
||||
],
|
||||
)
|
||||
repos = {}
|
||||
|
||||
self.repository_service._process_repository(repo, repos)
|
||||
|
||||
assert repos[1].default_branch.branch_deletion is False
|
||||
assert repos[1].default_branch.branch_deletion_source == "ruleset"
|
||||
|
||||
def test_active_ruleset_marks_default_branch_protected(self):
|
||||
repo = self._build_repo(
|
||||
branch_protected=False,
|
||||
ruleset_details=[
|
||||
self._build_ruleset(
|
||||
enforcement="active",
|
||||
include=["~DEFAULT_BRANCH"],
|
||||
rules=[{"type": "non_fast_forward"}],
|
||||
)
|
||||
],
|
||||
)
|
||||
repos = {}
|
||||
|
||||
self.repository_service._process_repository(repo, repos)
|
||||
|
||||
assert repos[1].default_branch.protected is True
|
||||
assert repos[1].default_branch.protected_source == "ruleset"
|
||||
|
||||
def test_ruleset_pull_request_parameters_map_to_attributes(self):
|
||||
repo = self._build_repo(
|
||||
branch_protected=False,
|
||||
ruleset_details=[
|
||||
self._build_ruleset(
|
||||
enforcement="active",
|
||||
include=["~DEFAULT_BRANCH"],
|
||||
rules=[
|
||||
{
|
||||
"type": "pull_request",
|
||||
"parameters": {
|
||||
"dismiss_stale_reviews_on_push": False,
|
||||
"require_code_owner_review": True,
|
||||
"require_last_push_approval": False,
|
||||
"required_approving_review_count": 2,
|
||||
"required_review_thread_resolution": True,
|
||||
},
|
||||
}
|
||||
],
|
||||
)
|
||||
],
|
||||
)
|
||||
repos = {}
|
||||
|
||||
self.repository_service._process_repository(repo, repos)
|
||||
|
||||
branch = repos[1].default_branch
|
||||
assert branch.require_pull_request is True
|
||||
assert branch.require_pull_request_source == "ruleset"
|
||||
assert branch.require_code_owner_reviews is True
|
||||
assert branch.require_code_owner_reviews_source == "ruleset"
|
||||
assert branch.conversation_resolution is True
|
||||
assert branch.conversation_resolution_source == "ruleset"
|
||||
assert branch.approval_count == 2
|
||||
assert branch.approval_count_source == "ruleset"
|
||||
|
||||
def test_inactive_ruleset_approval_count_is_fail_signal(self):
|
||||
repo = self._build_repo(
|
||||
branch_protected=False,
|
||||
ruleset_details=[
|
||||
self._build_ruleset(
|
||||
enforcement="disabled",
|
||||
include=["~DEFAULT_BRANCH"],
|
||||
rules=[
|
||||
{
|
||||
"type": "pull_request",
|
||||
"parameters": {
|
||||
"required_approving_review_count": 2,
|
||||
},
|
||||
}
|
||||
],
|
||||
)
|
||||
],
|
||||
)
|
||||
repos = {}
|
||||
|
||||
self.repository_service._process_repository(repo, repos)
|
||||
|
||||
assert repos[1].default_branch.approval_count == 0
|
||||
assert repos[1].default_branch.approval_count_source == "ruleset_not_active"
|
||||
|
||||
def test_active_ruleset_without_bypass_actors_applies_to_admins(self):
|
||||
repo = self._build_repo(
|
||||
branch_protected=False,
|
||||
ruleset_details=[
|
||||
self._build_ruleset(
|
||||
enforcement="active",
|
||||
include=["~DEFAULT_BRANCH"],
|
||||
rules=[{"type": "non_fast_forward"}],
|
||||
bypass_actors=[],
|
||||
)
|
||||
],
|
||||
)
|
||||
repos = {}
|
||||
|
||||
self.repository_service._process_repository(repo, repos)
|
||||
|
||||
assert repos[1].default_branch.enforce_admins is True
|
||||
assert repos[1].default_branch.enforce_admins_source == "ruleset"
|
||||
|
||||
def test_active_ruleset_with_bypass_actors_does_not_apply_to_admins(self):
|
||||
repo = self._build_repo(
|
||||
branch_protected=False,
|
||||
ruleset_details=[
|
||||
self._build_ruleset(
|
||||
enforcement="active",
|
||||
include=["~DEFAULT_BRANCH"],
|
||||
rules=[{"type": "non_fast_forward"}],
|
||||
bypass_actors=[
|
||||
{
|
||||
"actor_id": 1,
|
||||
"actor_type": "RepositoryRole",
|
||||
"bypass_mode": "always",
|
||||
}
|
||||
],
|
||||
)
|
||||
],
|
||||
)
|
||||
repos = {}
|
||||
|
||||
self.repository_service._process_repository(repo, repos)
|
||||
|
||||
# Admins can bypass, so the rulesets do not enforce protection for them.
|
||||
assert repos[1].default_branch.enforce_admins is False
|
||||
assert repos[1].default_branch.enforce_admins_source is None
|
||||
|
||||
def test_inactive_ruleset_with_bypass_actors_is_not_admin_fail_signal(self):
|
||||
# A disabled ruleset that has bypass actors would not apply to admins even if
|
||||
# activated, so it must not raise the enforce-admins ruleset_not_active signal.
|
||||
repo = self._build_repo(
|
||||
branch_protected=False,
|
||||
ruleset_details=[
|
||||
self._build_ruleset(
|
||||
enforcement="disabled",
|
||||
include=["~DEFAULT_BRANCH"],
|
||||
rules=[{"type": "non_fast_forward"}],
|
||||
bypass_actors=[
|
||||
{
|
||||
"actor_id": 1,
|
||||
"actor_type": "RepositoryRole",
|
||||
"bypass_mode": "always",
|
||||
}
|
||||
],
|
||||
)
|
||||
],
|
||||
)
|
||||
repos = {}
|
||||
|
||||
self.repository_service._process_repository(repo, repos)
|
||||
|
||||
assert repos[1].default_branch.enforce_admins is False
|
||||
assert repos[1].default_branch.enforce_admins_source is None
|
||||
# The branch is still reported as protected-but-inactive regardless of bypass.
|
||||
assert repos[1].default_branch.protected_source == "ruleset_not_active"
|
||||
|
||||
Reference in New Issue
Block a user