mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-01-25 02:08:11 +00:00
210 lines
9.3 KiB
Plaintext
210 lines
9.3 KiB
Plaintext
---
|
||
title: 'Create a Custom Output Format'
|
||
---
|
||
|
||
## Introduction
|
||
|
||
Prowler supports multiple output formats, allowing users to tailor findings presentation to their needs. Custom output formats are valuable when integrating Prowler with third-party tools, generating specialized reports, or adapting data for specific workflows. By defining a custom output format, users can refine how findings are structured, extracting and displaying only the most relevant information.
|
||
|
||
- Output Organization in Prowler
|
||
|
||
Prowler outputs are managed within the `/lib/outputs` directory. Each format—such as JSON, CSV, HTML—is implemented as a Python class.
|
||
|
||
- Outputs are generated based on scan findings, which are stored as structured dictionaries containing details such as:
|
||
|
||
- Resource IDs
|
||
|
||
- Severities
|
||
|
||
- Descriptions
|
||
|
||
- Other relevant metadata
|
||
|
||
- Creation Guidelines
|
||
|
||
Refer to the [Prowler Developer Guide](https://docs.prowler.com/projects/prowler-open-source/en/latest/) for insights into Prowler’s architecture and best practices for creating custom outputs.
|
||
|
||
- Identify the most suitable integration method for the output being targeted.
|
||
|
||
## Steps to Create a Custom Output Format
|
||
|
||
### Schema
|
||
|
||
- Output Class:
|
||
|
||
- The class must inherit from `Output`. Review the [Output Class](https://github.com/prowler-cloud/prowler/blob/master/prowler/lib/outputs/output.py).
|
||
|
||
- Create a class that encapsulates the required attributes and methods for interacting with the target platform. Below the code for the `CSV` class is presented:
|
||
|
||
```python title="CSV Class"
|
||
class CSV(Output):
|
||
def transform(self, findings: List[Finding]) -> None:
|
||
"""Transforms the findings into the CSV format.
|
||
|
||
Args:
|
||
findings (list[Finding]): a list of Finding objects
|
||
|
||
"""
|
||
...
|
||
```
|
||
|
||
|
||
- Transform Method:
|
||
|
||
- This method will transform the findings provided by Prowler to a specific format.
|
||
|
||
#### Method Implementation
|
||
|
||
The following example demonstrates the `transform` method for the `CSV` class:
|
||
|
||
```python title="Transform"
|
||
def transform(self, findings: List[Finding]) -> None:
|
||
"""Transforms the findings into the CSV format.
|
||
|
||
Args:
|
||
findings (list[Finding]): a list of Finding objects
|
||
|
||
"""
|
||
try:
|
||
for finding in findings:
|
||
finding_dict = {}
|
||
finding_dict["AUTH_METHOD"] = finding.auth_method
|
||
finding_dict["TIMESTAMP"] = finding.timestamp
|
||
finding_dict["ACCOUNT_UID"] = finding.account_uid
|
||
finding_dict["ACCOUNT_NAME"] = finding.account_name
|
||
finding_dict["ACCOUNT_EMAIL"] = finding.account_email
|
||
finding_dict["ACCOUNT_ORGANIZATION_UID"] = (
|
||
finding.account_organization_uid
|
||
)
|
||
finding_dict["ACCOUNT_ORGANIZATION_NAME"] = (
|
||
finding.account_organization_name
|
||
)
|
||
finding_dict["ACCOUNT_TAGS"] = unroll_dict(
|
||
finding.account_tags, separator=":"
|
||
)
|
||
finding_dict["FINDING_UID"] = finding.uid
|
||
finding_dict["PROVIDER"] = finding.metadata.Provider
|
||
finding_dict["CHECK_ID"] = finding.metadata.CheckID
|
||
finding_dict["CHECK_TITLE"] = finding.metadata.CheckTitle
|
||
finding_dict["CHECK_TYPE"] = unroll_list(finding.metadata.CheckType)
|
||
finding_dict["STATUS"] = finding.status.value
|
||
finding_dict["STATUS_EXTENDED"] = finding.status_extended
|
||
finding_dict["MUTED"] = finding.muted
|
||
finding_dict["SERVICE_NAME"] = finding.metadata.ServiceName
|
||
finding_dict["SUBSERVICE_NAME"] = finding.metadata.SubServiceName
|
||
finding_dict["SEVERITY"] = finding.metadata.Severity.value
|
||
finding_dict["RESOURCE_TYPE"] = finding.metadata.ResourceType
|
||
finding_dict["RESOURCE_UID"] = finding.resource_uid
|
||
finding_dict["RESOURCE_NAME"] = finding.resource_name
|
||
finding_dict["RESOURCE_DETAILS"] = finding.resource_details
|
||
finding_dict["RESOURCE_TAGS"] = unroll_dict(finding.resource_tags)
|
||
finding_dict["PARTITION"] = finding.partition
|
||
finding_dict["REGION"] = finding.region
|
||
finding_dict["DESCRIPTION"] = finding.metadata.Description
|
||
finding_dict["RISK"] = finding.metadata.Risk
|
||
finding_dict["RELATED_URL"] = finding.metadata.RelatedUrl
|
||
finding_dict["ADDITIONAL_URLS"] = unroll_list(finding.metadata.AdditionalURLs)
|
||
finding_dict["REMEDIATION_RECOMMENDATION_TEXT"] = (
|
||
finding.metadata.Remediation.Recommendation.Text
|
||
)
|
||
finding_dict["REMEDIATION_RECOMMENDATION_URL"] = (
|
||
finding.metadata.Remediation.Recommendation.Url
|
||
)
|
||
finding_dict["REMEDIATION_CODE_NATIVEIAC"] = (
|
||
finding.metadata.Remediation.Code.NativeIaC
|
||
)
|
||
finding_dict["REMEDIATION_CODE_TERRAFORM"] = (
|
||
finding.metadata.Remediation.Code.Terraform
|
||
)
|
||
finding_dict["REMEDIATION_CODE_CLI"] = (
|
||
finding.metadata.Remediation.Code.CLI
|
||
)
|
||
finding_dict["REMEDIATION_CODE_OTHER"] = (
|
||
finding.metadata.Remediation.Code.Other
|
||
)
|
||
finding_dict["COMPLIANCE"] = unroll_dict(
|
||
finding.compliance, separator=": "
|
||
)
|
||
finding_dict["CATEGORIES"] = unroll_list(finding.metadata.Categories)
|
||
finding_dict["DEPENDS_ON"] = unroll_list(finding.metadata.DependsOn)
|
||
finding_dict["RELATED_TO"] = unroll_list(finding.metadata.RelatedTo)
|
||
finding_dict["NOTES"] = finding.metadata.Notes
|
||
finding_dict["PROWLER_VERSION"] = finding.prowler_version
|
||
self._data.append(finding_dict)
|
||
except Exception as error:
|
||
logger.error(
|
||
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||
)
|
||
```
|
||
|
||
- Batch Write Data To File Method:
|
||
|
||
- This method will write the modeled object to a file.
|
||
|
||
#### Method Implementation
|
||
|
||
The following example demonstrates the `batch_write_data_to_file` method for the `CSV` class:
|
||
|
||
```python title="Batch Write Data To File"
|
||
def batch_write_data_to_file(self) -> None:
|
||
"""Writes the findings to a file using the CSV format using the `Output._file_descriptor`."""
|
||
try:
|
||
if (
|
||
getattr(self, "_file_descriptor", None)
|
||
and not self._file_descriptor.closed
|
||
and self._data
|
||
):
|
||
csv_writer = DictWriter(
|
||
self._file_descriptor,
|
||
fieldnames=self._data[0].keys(),
|
||
delimiter=";",
|
||
)
|
||
csv_writer.writeheader()
|
||
for finding in self._data:
|
||
csv_writer.writerow(finding)
|
||
self._file_descriptor.close()
|
||
except Exception as error:
|
||
logger.error(
|
||
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||
)
|
||
```
|
||
|
||
### Integrating the Custom Output Format into Prowler
|
||
|
||
Once the custom output format is created, it must be integrated into Prowler to ensure compatibility with the existing architecture.
|
||
|
||
#### Reviewing Current Supported Outputs
|
||
|
||
Before implementing the new output format, examine the usage of currently supported formats to understand their structure and integration approach. Example: CSV Output Creation in Prowler
|
||
|
||
Below is an example of how Prowler generates and processes CSV output within its [codebase](https://github.com/prowler-cloud/prowler/blob/master/prowler/__main__.py):
|
||
|
||
```python title="CSV creation"
|
||
if mode == "csv":
|
||
csv_output = CSV(
|
||
findings=finding_outputs,
|
||
create_file_descriptor=True,
|
||
file_path=f"{filename}{csv_file_suffix}",
|
||
)
|
||
generated_outputs["regular"].append(csv_output)
|
||
# Write CSV Finding Object to file.
|
||
csv_output.batch_write_data_to_file()
|
||
```
|
||
|
||
### Testing
|
||
|
||
* Verify that Prowler’s findings are accurately typed in the desired output format.
|
||
|
||
* Error Handling – Simulate edge cases to assess robustness and failure recovery mechanisms.
|
||
|
||
### Documentation
|
||
|
||
* Ensure the following elements are included:
|
||
|
||
* Setup Instructions – List all necessary dependencies and installation steps.
|
||
* Configuration details.
|
||
* Example Use Cases – Provide practical scenarios demonstrating functionality.
|
||
* Troubleshooting Guide – Document common issues and resolution steps.
|
||
|
||
* Comprehensive and clear documentation improves maintainability and simplifies onboarding of new users.
|