mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-10-14 23:44:15 +00:00
feat: add new check to service
This commit is contained in:
10 files changed
+150
-14
No files matched your search
@@ -505,7 +505,7 @@ def load_check_metadata(metadata_file: str) -> CheckMetadata:
|
||||
|
||||
|
||||
@dataclass
|
||||
class Check_Report_GitHub(Check_Report):
|
||||
class Check_Report_Github(Check_Report):
|
||||
"""Contains the GitHub Check's finding information."""
|
||||
|
||||
account_name: str
|
||||
|
||||
@@ -232,6 +232,14 @@ class Finding(BaseModel):
|
||||
)
|
||||
output_data["region"] = f"namespace: {check_output.namespace}"
|
||||
|
||||
elif provider.type == "github":
|
||||
output_data["auth_method"] = provider.auth_method
|
||||
output_data["resource_name"] = check_output.resource_name
|
||||
output_data["resource_uid"] = check_output.resource_id
|
||||
output_data["account_name"] = provider.identity.account_name
|
||||
output_data["account_uid"] = provider.identity.account_id
|
||||
output_data["region"] = "global"
|
||||
|
||||
# check_output Unique ID
|
||||
# TODO: move this to a function
|
||||
# TODO: in Azure, GCP and K8s there are fidings without resource_name
|
||||
|
||||
@@ -538,6 +538,51 @@ class HTML(Output):
|
||||
)
|
||||
return ""
|
||||
|
||||
@staticmethod
|
||||
def get_github_assessment_summary(provider: Provider) -> str:
|
||||
"""
|
||||
get_github_assessment_summary gets the HTML assessment summary for the provider
|
||||
|
||||
Args:
|
||||
provider (Provider): the provider object
|
||||
|
||||
Returns:
|
||||
str: the HTML assessment summary
|
||||
"""
|
||||
try:
|
||||
return f"""
|
||||
<div class="col-md-2">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
GitHub Assessment Summary
|
||||
</div>
|
||||
<ul class="list-group
|
||||
list-group-flush">
|
||||
<li class="list-group-item">
|
||||
<b>GitHub account:</b> {provider.identity.account_name}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
GitHub Credentials
|
||||
</div>
|
||||
<ul class="list-group
|
||||
list-group-flush">
|
||||
<li class="list-group-item">
|
||||
<b>GitHub authentication method:</b> {provider.auth_method}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>"""
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}] -- {error}"
|
||||
)
|
||||
return ""
|
||||
|
||||
@staticmethod
|
||||
def get_assessment_summary(provider: Provider) -> str:
|
||||
"""
|
||||
|
||||
@@ -17,6 +17,7 @@ from prowler.providers.github.models import GithubIdentityInfo, GithubSession
|
||||
|
||||
class GithubProvider(Provider):
|
||||
_type: str = "github"
|
||||
_auth_method: str
|
||||
_session: GithubSession
|
||||
_identity: GithubIdentityInfo
|
||||
_audit_config: dict
|
||||
@@ -81,15 +82,20 @@ class GithubProvider(Provider):
|
||||
Provider.set_global_provider(self)
|
||||
|
||||
@property
|
||||
def identity(self):
|
||||
"""Returns the identity information for the GitHub provider."""
|
||||
return self._identity
|
||||
def auth_method(self):
|
||||
"""Returns the authentication method for the GitHub provider."""
|
||||
return self._auth_method
|
||||
|
||||
@property
|
||||
def session(self):
|
||||
"""Returns the session object for the GitHub provider."""
|
||||
return self._session
|
||||
|
||||
@property
|
||||
def identity(self):
|
||||
"""Returns the identity information for the GitHub provider."""
|
||||
return self._identity
|
||||
|
||||
@property
|
||||
def type(self):
|
||||
"""Returns the type of the GitHub provider."""
|
||||
@@ -130,10 +136,13 @@ class GithubProvider(Provider):
|
||||
|
||||
if personal_access_token:
|
||||
session_token = getenv("GITHUB_PERSONAL_ACCESS_TOKEN")
|
||||
self._auth_method = "personal_access_token"
|
||||
elif github_app:
|
||||
session_token = getenv("GITHUB_APP_TOKEN")
|
||||
self._auth_method = "github_app"
|
||||
elif oauth_app:
|
||||
session_token = getenv("GITHUB_OAUTH_TOKEN")
|
||||
self._auth_method = "oauth_app"
|
||||
else:
|
||||
raise ValueError(
|
||||
"A GitHub API token of some kind is required to initialize GitHub provider."
|
||||
@@ -175,4 +184,4 @@ class GithubProvider(Provider):
|
||||
return identity
|
||||
|
||||
def print_credentials(self):
|
||||
print("Using GitHub PAT")
|
||||
print(f"You are using a {self.auth_method} as authentication method.")
|
||||
@@ -1,4 +1,4 @@
|
||||
from prowler.lib.check.models import Check_Report_GitHub
|
||||
from prowler.lib.check.models import Check_Report_Github
|
||||
from prowler.lib.mutelist.mutelist import Mutelist
|
||||
from prowler.lib.outputs.utils import unroll_dict, unroll_tags
|
||||
|
||||
@@ -6,7 +6,7 @@ from prowler.lib.outputs.utils import unroll_dict, unroll_tags
|
||||
class GitHubMutelist(Mutelist):
|
||||
def is_finding_muted(
|
||||
self,
|
||||
finding: Check_Report_GitHub,
|
||||
finding: Check_Report_Github,
|
||||
) -> bool:
|
||||
return self.is_muted(
|
||||
finding.account_name,
|
||||
|
||||
@@ -11,9 +11,7 @@ class GithubService:
|
||||
provider: GithubProvider,
|
||||
):
|
||||
self.client = self.__set_client__(
|
||||
provider.identity,
|
||||
provider.session,
|
||||
service,
|
||||
)
|
||||
|
||||
self.audit_config = provider.audit_config
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from prowler.providers.common.provider import Provider
|
||||
from prowler.providers.github.services.repository.repository_client import Repository
|
||||
from prowler.providers.github.services.repository.repository_service import Repository
|
||||
|
||||
repository_client = Repository(Provider.get_global_provider())
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"Provider": "github",
|
||||
"CheckID": "repository_public_has_securitymd_file",
|
||||
"CheckTitle": "Check if public repositories have a SECURITY.md file",
|
||||
"CheckType": [
|
||||
"CIS GitHub Benchmark v1.0.0"
|
||||
],
|
||||
"ServiceName": "repository",
|
||||
"SubServiceName": "",
|
||||
"ResourceIdTemplate": "github:user-id:repository/repository-name",
|
||||
"Severity": "low",
|
||||
"ResourceType": "Other",
|
||||
"Description": "Ensure that public repositories have a SECURITY.md file",
|
||||
"Risk": "Not having a SECURITY.md file in a public repository may lead to security vulnerabilities being overlooked by users and contributors.",
|
||||
"RelatedUrl": "https://gist.github.com/bayudwiyansatria/3aa6a865aad8d45d69cdb80090bea7c7",
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "",
|
||||
"NativeIaC": "",
|
||||
"Other": "",
|
||||
"Terraform": ""
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "Add a SECURITY.md file to the root of the repository. The file should contain information on how to report a security vulnerability, the security policy of the repository, and any other relevant information.",
|
||||
"Url": "https://github.blog/changelog/2019-05-23-security-policy/"
|
||||
}
|
||||
},
|
||||
"Categories": [],
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [],
|
||||
"Notes": ""
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
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_public_has_securitymd_file(Check):
|
||||
"""Check if a public repository has a SECURITY.md file
|
||||
|
||||
This class verifies wheter each public repository has a SECURITY.md file.
|
||||
"""
|
||||
|
||||
def execute(self) -> List[Check_Report_Github]:
|
||||
"""Execute the Github Repository Public Has Securitymd File check
|
||||
|
||||
Iterates over all public repositories and checks if they have a SECURITY.md file.
|
||||
|
||||
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 = "PASS"
|
||||
report.status_extended = (
|
||||
f"Repository {repo.name} does have a SECURITY.md file."
|
||||
)
|
||||
|
||||
if not repo.private:
|
||||
if not repo.securitymd:
|
||||
report.status = "FAIL"
|
||||
report.status_extended = (
|
||||
f"Repository {repo.name} does not have a SECURITY.md file."
|
||||
)
|
||||
|
||||
findings.append(report)
|
||||
|
||||
return findings
|
||||
@@ -10,19 +10,22 @@ class Repository(GithubService):
|
||||
def __init__(self, provider):
|
||||
super().__init__(__class__.__name__, provider)
|
||||
self.repositories = {}
|
||||
self.__threading_call__(self._list_repositories)
|
||||
self._list_repositories()
|
||||
|
||||
def _list_repositories(self):
|
||||
logger.info("Repository - Listing Repositories...")
|
||||
try:
|
||||
for repo in self.client.get_user().get_repos():
|
||||
try:
|
||||
securitymd_exists = repo.get_contents("SECURITY.md") is not None
|
||||
except Exception:
|
||||
securitymd_exists = False
|
||||
self.repositories[repo.id] = Repo(
|
||||
id=repo.id,
|
||||
name=repo.name,
|
||||
full_name=repo.full_name,
|
||||
private=repo.private,
|
||||
description=repo.description,
|
||||
securitymd=repo.get_contents("SECURITY.md") is not None,
|
||||
securitymd=securitymd_exists,
|
||||
)
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
@@ -40,5 +43,4 @@ class Repo(BaseModel):
|
||||
name: str
|
||||
full_name: str
|
||||
private: bool
|
||||
description: Optional[str]
|
||||
securitymd: Optional[bool] = False
|
||||
Reference in new issue
Block a user