mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-07-04 19:21:51 +00:00
feat(repositoy): add new check repository_inactive_not_archived (#7786)
Co-authored-by: Sergio Garcia <hello@mistercloudsec.com>
This commit is contained in:
@@ -109,6 +109,15 @@ The following list includes all the Microsoft 365 checks with configurable varia
|
||||
| `exchange_organization_mailtips_enabled` | `recommended_mailtips_large_audience_threshold` | Integer |
|
||||
|
||||
|
||||
## GitHub
|
||||
|
||||
### Configurable Checks
|
||||
The following list includes all the GitHub checks with configurable variables that can be changed in the configuration yaml file:
|
||||
|
||||
| Check Name | Value | Type |
|
||||
|--------------------------------------------|---------------------------------------------|---------|
|
||||
| `repository_inactive_not_archived` | `inactive_not_archived_days_threshold` | Integer |
|
||||
|
||||
## Config YAML File Structure
|
||||
|
||||
???+ note
|
||||
@@ -525,5 +534,10 @@ m365:
|
||||
# m365.exchange_organization_mailtips_enabled
|
||||
recommended_mailtips_large_audience_threshold: 25 # maximum number of recipients
|
||||
|
||||
# GitHub Configuration
|
||||
github:
|
||||
# github.repository_inactive_not_archived
|
||||
inactive_not_archived_days_threshold: 180
|
||||
|
||||
|
||||
```
|
||||
|
||||
@@ -15,6 +15,7 @@ All notable changes to the **Prowler SDK** are documented in this file.
|
||||
- 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_default_branch_requires_signed_commits` check for GitHub provider. [(#7777)](https://github.com/prowler-cloud/prowler/pull/7777)
|
||||
- Add `repository_inactive_not_archived` check for GitHub provider. [(#7786)](https://github.com/prowler-cloud/prowler/pull/7786)
|
||||
- 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)
|
||||
|
||||
@@ -515,3 +515,8 @@ m365:
|
||||
# m365.exchange_mailbox_properties_auditing_enabled
|
||||
# Maximum number of days to keep audit logs
|
||||
audit_log_age: 90
|
||||
|
||||
# GitHub Configuration
|
||||
github:
|
||||
# github.repository_inactive_not_archived --> CIS recommends 180 days (6 months)
|
||||
inactive_not_archived_days_threshold: 180
|
||||
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"Provider": "github",
|
||||
"CheckID": "repository_inactive_not_archived",
|
||||
"CheckTitle": "Check for inactive repositories that are not archived",
|
||||
"CheckType": [],
|
||||
"ServiceName": "repository",
|
||||
"SubServiceName": "",
|
||||
"ResourceIdTemplate": "",
|
||||
"Severity": "medium",
|
||||
"ResourceType": "GitHubRepository",
|
||||
"Description": "Ensure that repositories with no activity are reviewed and considered for archival. Inactive repositories may have outdated dependencies or security configurations that could pose security risks.",
|
||||
"Risk": "Inactive repositories that are not archived may contain outdated dependencies, unpatched vulnerabilities, or misconfigured security settings. These repositories increase the attack surface and could be targeted by malicious actors.",
|
||||
"RelatedUrl": "https://docs.github.com/en/repositories/archiving-a-github-repository/archiving-repositories",
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "",
|
||||
"NativeIaC": "",
|
||||
"Other": "",
|
||||
"Terraform": ""
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "Review inactive repositories and either: 1) Archive them if they are no longer needed, 2) Update their dependencies and security configurations if they are still required, or 3) Delete them if they contain no valuable information.",
|
||||
"Url": "https://docs.github.com/en/repositories/archiving-a-github-repository/archiving-repositories"
|
||||
}
|
||||
},
|
||||
"Categories": [],
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [],
|
||||
"Notes": ""
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
from datetime import datetime, timezone
|
||||
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_inactive_not_archived(Check):
|
||||
"""Check if unarchived repositories have been inactive for more than 6 months."""
|
||||
|
||||
def execute(self) -> List[CheckReportGithub]:
|
||||
findings = []
|
||||
|
||||
now = datetime.now(timezone.utc)
|
||||
|
||||
days_threshold = repository_client.audit_config.get(
|
||||
"inactive_not_archived_days_threshold", 180
|
||||
)
|
||||
|
||||
for repo in repository_client.repositories.values():
|
||||
report = CheckReportGithub(
|
||||
metadata=self.metadata(), resource=repo, repository=repo.name
|
||||
)
|
||||
|
||||
if repo.archived:
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"Repository {repo.name} is properly archived."
|
||||
findings.append(report)
|
||||
continue
|
||||
|
||||
latest_activity = repo.pushed_at
|
||||
days_inactive = (now - latest_activity).days
|
||||
|
||||
if days_inactive >= days_threshold:
|
||||
report.status = "FAIL"
|
||||
report.status_extended = f"Repository {repo.name} has been inactive for {days_inactive} days and is not archived (threshold: {days_threshold} days)."
|
||||
else:
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"Repository {repo.name} has been active within the last {days_threshold} days ({days_inactive} days ago)."
|
||||
|
||||
findings.append(report)
|
||||
|
||||
return findings
|
||||
@@ -1,3 +1,4 @@
|
||||
from datetime import datetime
|
||||
from typing import Optional
|
||||
|
||||
from pydantic import BaseModel
|
||||
@@ -163,6 +164,8 @@ class Repository(GithubService):
|
||||
full_name=repo.full_name,
|
||||
default_branch=repo.default_branch,
|
||||
private=repo.private,
|
||||
archived=repo.archived,
|
||||
pushed_at=repo.pushed_at,
|
||||
securitymd=securitymd_exists,
|
||||
require_pull_request=require_pr,
|
||||
approval_count=approval_cnt,
|
||||
@@ -197,6 +200,8 @@ class Repo(BaseModel):
|
||||
default_branch_protection: Optional[bool]
|
||||
default_branch: str
|
||||
private: bool
|
||||
archived: bool
|
||||
pushed_at: datetime
|
||||
securitymd: Optional[bool]
|
||||
require_pull_request: Optional[bool]
|
||||
required_linear_history: Optional[bool]
|
||||
|
||||
@@ -59,7 +59,9 @@ class TestGitHubProvider:
|
||||
account_id=ACCOUNT_ID,
|
||||
account_url=ACCOUNT_URL,
|
||||
)
|
||||
assert provider._audit_config == {}
|
||||
assert provider._audit_config == {
|
||||
"inactive_not_archived_days_threshold": 180,
|
||||
}
|
||||
assert provider._fixer_config == fixer_config
|
||||
|
||||
def test_github_provider_OAuth(self):
|
||||
@@ -99,7 +101,9 @@ class TestGitHubProvider:
|
||||
account_id=ACCOUNT_ID,
|
||||
account_url=ACCOUNT_URL,
|
||||
)
|
||||
assert provider._audit_config == {}
|
||||
assert provider._audit_config == {
|
||||
"inactive_not_archived_days_threshold": 180,
|
||||
}
|
||||
assert provider._fixer_config == fixer_config
|
||||
|
||||
def test_github_provider_App(self):
|
||||
@@ -133,5 +137,7 @@ class TestGitHubProvider:
|
||||
assert provider._type == "github"
|
||||
assert provider.session == GithubSession(token="", id=APP_ID, key=APP_KEY)
|
||||
assert provider.identity == GithubAppIdentityInfo(app_id=APP_ID)
|
||||
assert provider._audit_config == {}
|
||||
assert provider._audit_config == {
|
||||
"inactive_not_archived_days_threshold": 180,
|
||||
}
|
||||
assert provider._fixer_config == fixer_config
|
||||
|
||||
+5
@@ -1,3 +1,4 @@
|
||||
from datetime import datetime, timezone
|
||||
from unittest import mock
|
||||
|
||||
from prowler.providers.github.services.repository.repository_service import Repo
|
||||
@@ -39,6 +40,8 @@ class Test_repository_branch_delete_on_merge_enabled_test:
|
||||
private=False,
|
||||
securitymd=False,
|
||||
delete_branch_on_merge=False,
|
||||
archived=False,
|
||||
pushed_at=datetime.now(timezone.utc),
|
||||
),
|
||||
}
|
||||
|
||||
@@ -79,6 +82,8 @@ class Test_repository_branch_delete_on_merge_enabled_test:
|
||||
private=False,
|
||||
securitymd=True,
|
||||
delete_branch_on_merge=True,
|
||||
archived=False,
|
||||
pushed_at=datetime.now(timezone.utc),
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
+13
-6
@@ -1,3 +1,4 @@
|
||||
from datetime import datetime, timezone
|
||||
from unittest import mock
|
||||
|
||||
from prowler.providers.github.services.repository.repository_service import Repo
|
||||
@@ -31,15 +32,18 @@ class Test_repository_default_branch_deletion_disabled_test:
|
||||
repository_client = mock.MagicMock
|
||||
repo_name = "repo1"
|
||||
default_branch = "main"
|
||||
now = datetime.now(timezone.utc)
|
||||
|
||||
repository_client.repositories = {
|
||||
1: Repo(
|
||||
id=1,
|
||||
name=repo_name,
|
||||
full_name="account-name/repo1",
|
||||
default_branch=default_branch,
|
||||
default_branch_deletion=True,
|
||||
private=False,
|
||||
securitymd=False,
|
||||
archived=False,
|
||||
pushed_at=now,
|
||||
default_branch_deletion=True,
|
||||
),
|
||||
}
|
||||
|
||||
@@ -61,7 +65,7 @@ class Test_repository_default_branch_deletion_disabled_test:
|
||||
result = check.execute()
|
||||
assert len(result) == 1
|
||||
assert result[0].resource_id == 1
|
||||
assert result[0].resource_name == "repo1"
|
||||
assert result[0].resource_name == repo_name
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
@@ -72,15 +76,18 @@ class Test_repository_default_branch_deletion_disabled_test:
|
||||
repository_client = mock.MagicMock
|
||||
repo_name = "repo1"
|
||||
default_branch = "main"
|
||||
now = datetime.now(timezone.utc)
|
||||
|
||||
repository_client.repositories = {
|
||||
1: Repo(
|
||||
id=1,
|
||||
name=repo_name,
|
||||
full_name="account-name/repo1",
|
||||
private=False,
|
||||
default_branch=default_branch,
|
||||
private=False,
|
||||
archived=False,
|
||||
pushed_at=now,
|
||||
default_branch_deletion=False,
|
||||
securitymd=True,
|
||||
),
|
||||
}
|
||||
|
||||
@@ -102,7 +109,7 @@ class Test_repository_default_branch_deletion_disabled_test:
|
||||
result = check.execute()
|
||||
assert len(result) == 1
|
||||
assert result[0].resource_id == 1
|
||||
assert result[0].resource_name == "repo1"
|
||||
assert result[0].resource_name == repo_name
|
||||
assert result[0].status == "PASS"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
|
||||
+5
@@ -1,3 +1,4 @@
|
||||
from datetime import datetime, timezone
|
||||
from unittest import mock
|
||||
|
||||
from prowler.providers.github.services.repository.repository_service import Repo
|
||||
@@ -40,6 +41,8 @@ class Test_repository_default_branch_disallows_force_push_test:
|
||||
allow_force_pushes=True,
|
||||
private=False,
|
||||
securitymd=False,
|
||||
archived=False,
|
||||
pushed_at=datetime.now(timezone.utc),
|
||||
),
|
||||
}
|
||||
|
||||
@@ -81,6 +84,8 @@ class Test_repository_default_branch_disallows_force_push_test:
|
||||
default_branch=default_branch,
|
||||
allow_force_pushes=False,
|
||||
securitymd=True,
|
||||
archived=False,
|
||||
pushed_at=datetime.now(timezone.utc),
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
+5
@@ -1,3 +1,4 @@
|
||||
from datetime import datetime, timezone
|
||||
from unittest import mock
|
||||
|
||||
from prowler.providers.github.services.repository.repository_service import Repo
|
||||
@@ -40,6 +41,8 @@ class Test_repository_default_branch_protection_applies_to_admins_test:
|
||||
private=False,
|
||||
securitymd=False,
|
||||
enforce_admins=False,
|
||||
archived=False,
|
||||
pushed_at=datetime.now(timezone.utc),
|
||||
),
|
||||
}
|
||||
|
||||
@@ -81,6 +84,8 @@ class Test_repository_default_branch_protection_applies_to_admins_test:
|
||||
default_branch=default_branch,
|
||||
enforce_admins=True,
|
||||
securitymd=True,
|
||||
archived=False,
|
||||
pushed_at=datetime.now(timezone.utc),
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
+5
@@ -1,3 +1,4 @@
|
||||
from datetime import datetime, timezone
|
||||
from unittest import mock
|
||||
|
||||
from prowler.providers.github.services.repository.repository_service import Repo
|
||||
@@ -40,6 +41,8 @@ class Test_repository_default_branch_protection_enabled_test:
|
||||
private=False,
|
||||
default_branch_protection=False,
|
||||
securitymd=False,
|
||||
archived=False,
|
||||
pushed_at=datetime.now(timezone.utc),
|
||||
),
|
||||
}
|
||||
|
||||
@@ -81,6 +84,8 @@ class Test_repository_default_branch_protection_enabled_test:
|
||||
default_branch=default_branch,
|
||||
default_branch_protection=True,
|
||||
securitymd=True,
|
||||
archived=False,
|
||||
pushed_at=datetime.now(timezone.utc),
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
+5
@@ -1,3 +1,4 @@
|
||||
from datetime import datetime, timezone
|
||||
from unittest import mock
|
||||
|
||||
from prowler.providers.github.services.repository.repository_service import Repo
|
||||
@@ -41,6 +42,8 @@ class Test_repository_default_branch_requires_codeowners_review:
|
||||
require_pull_request=False,
|
||||
approval_count=0,
|
||||
require_code_owner_reviews=False,
|
||||
archived=False,
|
||||
pushed_at=datetime.now(timezone.utc),
|
||||
),
|
||||
}
|
||||
|
||||
@@ -83,6 +86,8 @@ class Test_repository_default_branch_requires_codeowners_review:
|
||||
require_pull_request=False,
|
||||
approval_count=0,
|
||||
require_code_owner_reviews=True,
|
||||
archived=False,
|
||||
pushed_at=datetime.now(timezone.utc),
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
+5
@@ -1,3 +1,4 @@
|
||||
from datetime import datetime, timezone
|
||||
from unittest import mock
|
||||
|
||||
from prowler.providers.github.services.repository.repository_service import Repo
|
||||
@@ -38,6 +39,8 @@ class Test_repository_default_branch_requires_conversation_resolution_test:
|
||||
full_name="account-name/repo1",
|
||||
default_branch=default_branch,
|
||||
conversation_resolution=False,
|
||||
archived=False,
|
||||
pushed_at=datetime.now(timezone.utc),
|
||||
private=False,
|
||||
securitymd=False,
|
||||
),
|
||||
@@ -80,6 +83,8 @@ class Test_repository_default_branch_requires_conversation_resolution_test:
|
||||
private=False,
|
||||
default_branch=default_branch,
|
||||
conversation_resolution=True,
|
||||
archived=False,
|
||||
pushed_at=datetime.now(timezone.utc),
|
||||
securitymd=True,
|
||||
),
|
||||
}
|
||||
|
||||
+5
@@ -1,3 +1,4 @@
|
||||
from datetime import datetime, timezone
|
||||
from unittest import mock
|
||||
|
||||
from prowler.providers.github.services.repository.repository_service import Repo
|
||||
@@ -40,6 +41,8 @@ class Test_repository_default_branch_requires_linear_history_test:
|
||||
required_linear_history=False,
|
||||
private=False,
|
||||
securitymd=False,
|
||||
archived=False,
|
||||
pushed_at=datetime.now(timezone.utc),
|
||||
),
|
||||
}
|
||||
|
||||
@@ -81,6 +84,8 @@ class Test_repository_default_branch_requires_linear_history_test:
|
||||
default_branch=default_branch,
|
||||
required_linear_history=True,
|
||||
securitymd=True,
|
||||
archived=False,
|
||||
pushed_at=datetime.now(timezone.utc),
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
+7
@@ -1,3 +1,4 @@
|
||||
from datetime import datetime, timezone
|
||||
from unittest import mock
|
||||
|
||||
from prowler.providers.github.services.repository.repository_service import Repo
|
||||
@@ -41,6 +42,8 @@ class Test_repository_default_branch_requires_multiple_approvals:
|
||||
securitymd=False,
|
||||
require_pull_request=False,
|
||||
approval_count=0,
|
||||
archived=False,
|
||||
pushed_at=datetime.now(timezone.utc),
|
||||
),
|
||||
}
|
||||
|
||||
@@ -83,6 +86,8 @@ class Test_repository_default_branch_requires_multiple_approvals:
|
||||
securitymd=False,
|
||||
require_pull_request=True,
|
||||
approval_count=0,
|
||||
archived=False,
|
||||
pushed_at=datetime.now(timezone.utc),
|
||||
),
|
||||
}
|
||||
|
||||
@@ -125,6 +130,8 @@ class Test_repository_default_branch_requires_multiple_approvals:
|
||||
securitymd=True,
|
||||
require_pull_request=True,
|
||||
approval_count=2,
|
||||
archived=False,
|
||||
pushed_at=datetime.now(timezone.utc),
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
+5
@@ -1,3 +1,4 @@
|
||||
from datetime import datetime, timezone
|
||||
from unittest import mock
|
||||
|
||||
from prowler.providers.github.services.repository.repository_service import Repo
|
||||
@@ -40,6 +41,8 @@ class Test_repository_default_branch_requires_signed_commits:
|
||||
default_branch=default_branch,
|
||||
require_signed_commits=False,
|
||||
securitymd=True,
|
||||
archived=False,
|
||||
pushed_at=datetime.now(timezone.utc),
|
||||
),
|
||||
}
|
||||
|
||||
@@ -81,6 +84,8 @@ class Test_repository_default_branch_requires_signed_commits:
|
||||
default_branch=default_branch,
|
||||
require_signed_commits=True,
|
||||
securitymd=True,
|
||||
archived=False,
|
||||
pushed_at=datetime.now(timezone.utc),
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
+5
@@ -1,3 +1,4 @@
|
||||
from datetime import datetime, timezone
|
||||
from unittest import mock
|
||||
|
||||
from prowler.providers.github.services.repository.repository_service import Repo
|
||||
@@ -38,6 +39,8 @@ class Test_repository_default_branch_status_checks_required_test:
|
||||
full_name="account-name/repo1",
|
||||
default_branch=default_branch,
|
||||
status_checks=False,
|
||||
archived=False,
|
||||
pushed_at=datetime.now(timezone.utc),
|
||||
private=False,
|
||||
securitymd=False,
|
||||
),
|
||||
@@ -80,6 +83,8 @@ class Test_repository_default_branch_status_checks_required_test:
|
||||
private=False,
|
||||
default_branch=default_branch,
|
||||
status_checks=True,
|
||||
archived=False,
|
||||
pushed_at=datetime.now(timezone.utc),
|
||||
securitymd=True,
|
||||
),
|
||||
}
|
||||
|
||||
+5
@@ -1,3 +1,4 @@
|
||||
from datetime import datetime, timezone
|
||||
from unittest import mock
|
||||
|
||||
from prowler.providers.github.services.repository.repository_service import Repo
|
||||
@@ -37,6 +38,8 @@ class Test_repository_dependency_scanning_enabled:
|
||||
full_name="account-name/repo1",
|
||||
default_branch="main",
|
||||
private=False,
|
||||
archived=False,
|
||||
pushed_at=datetime.now(timezone.utc),
|
||||
securitymd=True,
|
||||
require_pull_request=False,
|
||||
approval_count=0,
|
||||
@@ -80,6 +83,8 @@ class Test_repository_dependency_scanning_enabled:
|
||||
full_name="account-name/repo2",
|
||||
default_branch="main",
|
||||
private=False,
|
||||
archived=False,
|
||||
pushed_at=datetime.now(timezone.utc),
|
||||
securitymd=True,
|
||||
require_pull_request=False,
|
||||
approval_count=0,
|
||||
|
||||
+5
@@ -1,3 +1,4 @@
|
||||
from datetime import datetime, timezone
|
||||
from unittest import mock
|
||||
|
||||
from prowler.providers.github.services.repository.repository_service import Repo
|
||||
@@ -41,6 +42,8 @@ class Test_repository_has_codeowners_file:
|
||||
require_pull_request=False,
|
||||
approval_count=0,
|
||||
codeowners_exists=False,
|
||||
archived=False,
|
||||
pushed_at=datetime.now(timezone.utc),
|
||||
),
|
||||
}
|
||||
|
||||
@@ -83,6 +86,8 @@ class Test_repository_has_codeowners_file:
|
||||
require_pull_request=False,
|
||||
approval_count=0,
|
||||
codeowners_exists=True,
|
||||
archived=False,
|
||||
pushed_at=datetime.now(timezone.utc),
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
+206
@@ -0,0 +1,206 @@
|
||||
from datetime import datetime, timedelta, timezone
|
||||
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_inactive_not_archived:
|
||||
def test_no_repositories(self):
|
||||
repository_client = mock.MagicMock
|
||||
repository_client.repositories = {}
|
||||
repository_client.audit_config = {}
|
||||
|
||||
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_inactive_not_archived.repository_inactive_not_archived.repository_client",
|
||||
new=repository_client,
|
||||
),
|
||||
):
|
||||
from prowler.providers.github.services.repository.repository_inactive_not_archived.repository_inactive_not_archived import (
|
||||
repository_inactive_not_archived,
|
||||
)
|
||||
|
||||
check = repository_inactive_not_archived()
|
||||
result = check.execute()
|
||||
assert len(result) == 0
|
||||
|
||||
def test_repository_active_not_archived(self):
|
||||
repository_client = mock.MagicMock
|
||||
repo_name = "test-repo"
|
||||
default_branch = "main"
|
||||
now = datetime.now(timezone.utc)
|
||||
recent_activity = now - timedelta(days=30) # 30 days ago
|
||||
|
||||
repository_client.repositories = {
|
||||
1: Repo(
|
||||
id=1,
|
||||
name=repo_name,
|
||||
full_name="account-name/test-repo",
|
||||
private=False,
|
||||
default_branch=default_branch,
|
||||
archived=False,
|
||||
pushed_at=recent_activity,
|
||||
),
|
||||
}
|
||||
repository_client.audit_config = {}
|
||||
|
||||
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_inactive_not_archived.repository_inactive_not_archived.repository_client",
|
||||
new=repository_client,
|
||||
),
|
||||
):
|
||||
from prowler.providers.github.services.repository.repository_inactive_not_archived.repository_inactive_not_archived import (
|
||||
repository_inactive_not_archived,
|
||||
)
|
||||
|
||||
check = repository_inactive_not_archived()
|
||||
result = check.execute()
|
||||
assert len(result) == 1
|
||||
assert result[0].resource_id == 1
|
||||
assert result[0].resource_name == repo_name
|
||||
assert result[0].status == "PASS"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"Repository {repo_name} has been active within the last 180 days (30 days ago)."
|
||||
)
|
||||
|
||||
def test_repository_inactive_not_archived(self):
|
||||
repository_client = mock.MagicMock
|
||||
repo_name = "test-repo"
|
||||
default_branch = "main"
|
||||
now = datetime.now(timezone.utc)
|
||||
old_activity = now - timedelta(days=200) # 200 days ago
|
||||
|
||||
repository_client.repositories = {
|
||||
1: Repo(
|
||||
id=1,
|
||||
name=repo_name,
|
||||
full_name="account-name/test-repo",
|
||||
private=False,
|
||||
default_branch=default_branch,
|
||||
archived=False,
|
||||
pushed_at=old_activity,
|
||||
),
|
||||
}
|
||||
repository_client.audit_config = {}
|
||||
|
||||
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_inactive_not_archived.repository_inactive_not_archived.repository_client",
|
||||
new=repository_client,
|
||||
),
|
||||
):
|
||||
from prowler.providers.github.services.repository.repository_inactive_not_archived.repository_inactive_not_archived import (
|
||||
repository_inactive_not_archived,
|
||||
)
|
||||
|
||||
check = repository_inactive_not_archived()
|
||||
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 "has been inactive for 200 days" in result[0].status_extended
|
||||
assert "and is not archived" in result[0].status_extended
|
||||
|
||||
def test_repository_inactive_but_archived(self):
|
||||
repository_client = mock.MagicMock
|
||||
repo_name = "test-repo"
|
||||
default_branch = "main"
|
||||
now = datetime.now(timezone.utc)
|
||||
old_activity = now - timedelta(days=200) # 200 days ago
|
||||
|
||||
repository_client.repositories = {
|
||||
1: Repo(
|
||||
id=1,
|
||||
name=repo_name,
|
||||
full_name="account-name/test-repo",
|
||||
default_branch=default_branch,
|
||||
private=False,
|
||||
archived=True,
|
||||
pushed_at=old_activity,
|
||||
),
|
||||
}
|
||||
repository_client.audit_config = {}
|
||||
|
||||
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_inactive_not_archived.repository_inactive_not_archived.repository_client",
|
||||
new=repository_client,
|
||||
),
|
||||
):
|
||||
from prowler.providers.github.services.repository.repository_inactive_not_archived.repository_inactive_not_archived import (
|
||||
repository_inactive_not_archived,
|
||||
)
|
||||
|
||||
check = repository_inactive_not_archived()
|
||||
result = check.execute()
|
||||
assert len(result) == 1
|
||||
assert result[0].resource_id == 1
|
||||
assert result[0].resource_name == repo_name
|
||||
assert result[0].status == "PASS"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"Repository {repo_name} is properly archived."
|
||||
)
|
||||
|
||||
def test_custom_days_threshold(self):
|
||||
repository_client = mock.MagicMock
|
||||
repo_name = "test-repo"
|
||||
default_branch = "main"
|
||||
now = datetime.now(timezone.utc)
|
||||
old_activity = now - timedelta(days=50)
|
||||
|
||||
repository_client.repositories = {
|
||||
1: Repo(
|
||||
id=1,
|
||||
name=repo_name,
|
||||
full_name="account-name/test-repo",
|
||||
private=False,
|
||||
default_branch=default_branch,
|
||||
archived=False,
|
||||
pushed_at=old_activity,
|
||||
),
|
||||
}
|
||||
repository_client.audit_config = {"inactive_not_archived_days_threshold": 40}
|
||||
|
||||
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_inactive_not_archived.repository_inactive_not_archived.repository_client",
|
||||
new=repository_client,
|
||||
),
|
||||
):
|
||||
from prowler.providers.github.services.repository.repository_inactive_not_archived.repository_inactive_not_archived import (
|
||||
repository_inactive_not_archived,
|
||||
)
|
||||
|
||||
check = repository_inactive_not_archived()
|
||||
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 "has been inactive for 50 days" in result[0].status_extended
|
||||
assert "and is not archived" in result[0].status_extended
|
||||
+5
@@ -1,3 +1,4 @@
|
||||
from datetime import datetime, timezone
|
||||
from unittest import mock
|
||||
|
||||
from prowler.providers.github.services.repository.repository_service import Repo
|
||||
@@ -40,6 +41,8 @@ class Test_repository_public_has_securitymd_file_test:
|
||||
securitymd=False,
|
||||
require_pull_request=False,
|
||||
approval_count=0,
|
||||
archived=False,
|
||||
pushed_at=datetime.now(timezone.utc),
|
||||
),
|
||||
}
|
||||
|
||||
@@ -81,6 +84,8 @@ class Test_repository_public_has_securitymd_file_test:
|
||||
securitymd=True,
|
||||
require_pull_request=False,
|
||||
approval_count=0,
|
||||
archived=False,
|
||||
pushed_at=datetime.now(timezone.utc),
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
+5
@@ -1,3 +1,4 @@
|
||||
from datetime import datetime, timezone
|
||||
from unittest import mock
|
||||
|
||||
from prowler.providers.github.services.repository.repository_service import Repo
|
||||
@@ -41,6 +42,8 @@ class Test_repository_secret_scanning_enabled:
|
||||
require_pull_request=False,
|
||||
approval_count=0,
|
||||
secret_scanning_enabled=False,
|
||||
archived=False,
|
||||
pushed_at=datetime.now(timezone.utc),
|
||||
),
|
||||
}
|
||||
|
||||
@@ -83,6 +86,8 @@ class Test_repository_secret_scanning_enabled:
|
||||
require_pull_request=False,
|
||||
approval_count=0,
|
||||
secret_scanning_enabled=True,
|
||||
archived=False,
|
||||
pushed_at=datetime.now(timezone.utc),
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
from datetime import datetime, timezone
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from prowler.providers.github.services.repository.repository_service import (
|
||||
@@ -26,6 +27,9 @@ def mock_list_repositories(_):
|
||||
codeowners_exists=True,
|
||||
require_code_owner_reviews=True,
|
||||
secret_scanning_enabled=True,
|
||||
require_signed_commits=True,
|
||||
archived=False,
|
||||
pushed_at=datetime.now(timezone.utc),
|
||||
enforce_admins=True,
|
||||
delete_branch_on_merge=True,
|
||||
conversation_resolution=True,
|
||||
@@ -66,6 +70,9 @@ class Test_Repository_Service:
|
||||
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
|
||||
assert repository_service.repositories[1].require_signed_commits is True
|
||||
assert repository_service.repositories[1].archived is False
|
||||
assert repository_service.repositories[1].pushed_at is not None
|
||||
|
||||
|
||||
class Test_Repository_FileExists:
|
||||
|
||||
Reference in New Issue
Block a user