mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-10-09 21:14:22 +00:00
145 lines
6.3 KiB
Python
145 lines
6.3 KiB
Python
from operator import attrgetter
|
|
|
|
from prowler.config.config import timestamp
|
|
from prowler.lib.logger import logger
|
|
from prowler.lib.outputs.common_models import FindingOutput
|
|
from prowler.lib.outputs.utils import unroll_list, unroll_tags
|
|
from prowler.lib.utils.utils import outputs_unix_timestamp
|
|
|
|
|
|
def get_provider_data_mapping(provider) -> dict:
|
|
data = {}
|
|
for generic_field, provider_field in provider.get_output_mapping.items():
|
|
try:
|
|
provider_value = attrgetter(provider_field)(provider)
|
|
data[generic_field] = provider_value
|
|
except AttributeError:
|
|
data[generic_field] = ""
|
|
except Exception as error:
|
|
logger.error(
|
|
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
|
)
|
|
return data
|
|
|
|
|
|
def generate_provider_output(provider, finding, csv_data) -> FindingOutput:
|
|
"""
|
|
generate_provider_output returns the provider's Finding output model
|
|
"""
|
|
# TODO: we have to standardize this between the above mapping and the provider.get_output_mapping()
|
|
try:
|
|
if provider.type == "aws":
|
|
# TODO: probably Organization UID is without the account id
|
|
csv_data["auth_method"] = f"profile: {csv_data['auth_method']}"
|
|
csv_data["resource_name"] = finding.resource_id
|
|
csv_data["resource_uid"] = finding.resource_arn
|
|
csv_data["region"] = finding.region
|
|
|
|
elif provider.type == "azure":
|
|
# TODO: we should show the authentication method used I think
|
|
csv_data["auth_method"] = (
|
|
f"{provider.identity.identity_type}: {provider.identity.identity_id}"
|
|
)
|
|
# Get the first tenant domain ID, just in case
|
|
csv_data["account_organization_uid"] = csv_data["account_organization_uid"][
|
|
0
|
|
]
|
|
csv_data["account_uid"] = (
|
|
csv_data["account_organization_uid"]
|
|
if "Tenant:" in finding.subscription
|
|
else provider.identity.subscriptions[finding.subscription]
|
|
)
|
|
csv_data["account_name"] = finding.subscription
|
|
csv_data["resource_name"] = finding.resource_name
|
|
csv_data["resource_uid"] = finding.resource_id
|
|
csv_data["region"] = finding.location
|
|
|
|
elif provider.type == "gcp":
|
|
csv_data["auth_method"] = f"Principal: {csv_data['auth_method']}"
|
|
csv_data["account_uid"] = provider.projects[finding.project_id].number
|
|
csv_data["account_name"] = provider.projects[finding.project_id].name
|
|
csv_data["account_tags"] = provider.projects[finding.project_id].labels
|
|
csv_data["resource_name"] = finding.resource_name
|
|
csv_data["resource_uid"] = finding.resource_id
|
|
csv_data["region"] = finding.location
|
|
|
|
if (
|
|
provider.projects
|
|
and finding.project_id in provider.projects
|
|
and getattr(provider.projects[finding.project_id], "organization")
|
|
):
|
|
csv_data["account_organization_uid"] = provider.projects[
|
|
finding.project_id
|
|
].organization.id
|
|
# TODO: for now is None since we don't retrieve that data
|
|
csv_data["account_organization"] = provider.projects[
|
|
finding.project_id
|
|
].organization.display_name
|
|
|
|
elif provider.type == "kubernetes":
|
|
if provider.identity.context == "In-Cluster":
|
|
csv_data["auth_method"] = "in-cluster"
|
|
else:
|
|
csv_data["auth_method"] = "kubeconfig"
|
|
csv_data["resource_name"] = finding.resource_name
|
|
csv_data["resource_uid"] = finding.resource_id
|
|
csv_data["account_name"] = f"context: {provider.identity.context}"
|
|
csv_data["region"] = f"namespace: {finding.namespace}"
|
|
|
|
# Finding Unique ID
|
|
# TODO: move this to a function
|
|
# TODO: in Azure, GCP and K8s there are fidings without resource_name
|
|
csv_data["finding_uid"] = (
|
|
f"prowler-{provider.type}-{finding.check_metadata.CheckID}-{csv_data['account_uid']}-{csv_data['region']}-{csv_data['resource_name']}"
|
|
)
|
|
|
|
finding_output = FindingOutput(**csv_data)
|
|
|
|
except Exception as error:
|
|
logger.error(
|
|
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
|
)
|
|
else:
|
|
return finding_output
|
|
|
|
|
|
# TODO: add test for outputs_unix_timestamp
|
|
def fill_common_finding_data(finding: dict, unix_timestamp: bool) -> dict:
|
|
finding_data = {
|
|
"timestamp": outputs_unix_timestamp(unix_timestamp, timestamp),
|
|
"check_id": finding.check_metadata.CheckID,
|
|
"check_title": finding.check_metadata.CheckTitle,
|
|
"check_type": ",".join(finding.check_metadata.CheckType),
|
|
"status": finding.status,
|
|
"status_extended": finding.status_extended,
|
|
"muted": finding.muted,
|
|
"service_name": finding.check_metadata.ServiceName,
|
|
"subservice_name": finding.check_metadata.SubServiceName,
|
|
"severity": finding.check_metadata.Severity,
|
|
"resource_type": finding.check_metadata.ResourceType,
|
|
"resource_details": finding.resource_details,
|
|
"resource_tags": unroll_tags(finding.resource_tags),
|
|
"description": finding.check_metadata.Description,
|
|
"risk": finding.check_metadata.Risk,
|
|
"related_url": finding.check_metadata.RelatedUrl,
|
|
"remediation_recommendation_text": (
|
|
finding.check_metadata.Remediation.Recommendation.Text
|
|
),
|
|
"remediation_recommendation_url": (
|
|
finding.check_metadata.Remediation.Recommendation.Url
|
|
),
|
|
"remediation_code_nativeiac": (
|
|
finding.check_metadata.Remediation.Code.NativeIaC
|
|
),
|
|
"remediation_code_terraform": (
|
|
finding.check_metadata.Remediation.Code.Terraform
|
|
),
|
|
"remediation_code_cli": (finding.check_metadata.Remediation.Code.CLI),
|
|
"remediation_code_other": (finding.check_metadata.Remediation.Code.Other),
|
|
"categories": unroll_list(finding.check_metadata.Categories),
|
|
"depends_on": unroll_list(finding.check_metadata.DependsOn),
|
|
"related_to": unroll_list(finding.check_metadata.RelatedTo),
|
|
"notes": finding.check_metadata.Notes,
|
|
}
|
|
return finding_data
|