"""
def get_aws_html_header(args: list) -> str:
"""
Generate the HTML header for AWS
Args:
args (list): List of arguments passed to the script
Returns:
str: HTML header for AWS
"""
aws_html_header = f"""
Prowler - The Handy Cloud Security Tool
Report Information
Version: {prowler_version}
Parameters used: {redact_argv(args)}
Date: {timestamp.isoformat()}
{aws_html_assessment_summary}
Assessment Overview
Total Findings: 30
Passed: 25
Passed (Muted): 20
Failed: 5
Failed (Muted): 5
Total Resources: 1
Status
Severity
Service Name
Region
Check ID
Check Title
Resource ID
Resource Tags
Status Extended
Risk
Recommendation
Compliance
"""
return aws_html_header
html_footer = """
"""
class TestHTML:
def test_transform_fail_finding(self):
findings = [
generate_finding_output(
status="FAIL",
resource_tags={"key1": "value1", "key2": "value2"},
severity="high",
service_name="service",
region=AWS_REGION_EU_WEST_1,
check_id="service_test_check_id",
check_title="service_test_check_id",
resource_uid="test-resource-uid",
status_extended="test-status-extended",
risk="test-risk",
remediation_recommendation_text="test-remediation-recommendation-text",
remediation_recommendation_url="https://hub.prowler.com/check/check-id",
compliance={"test-compliance": "test-compliance"},
)
]
html = HTML(findings)
output_data = html.data[0]
assert isinstance(output_data, str)
assert output_data == fail_html_finding
def test_transform_pass_finding(self):
findings = [
generate_finding_output(
remediation_recommendation_url="https://hub.prowler.com/check/check-id"
)
]
html = HTML(findings)
output_data = html.data[0]
assert isinstance(output_data, str)
assert output_data == pass_html_finding
def test_transform_muted_finding(self):
findings = [
generate_finding_output(
muted=True,
remediation_recommendation_url="https://hub.prowler.com/check/check-id",
)
]
html = HTML(findings)
output_data = html.data[0]
assert isinstance(output_data, str)
assert output_data == muted_html_finding
def test_transform_manual_finding(self):
findings = [
generate_finding_output(
status="MANUAL",
remediation_recommendation_url="https://hub.prowler.com/check/check-id",
)
]
html = HTML(findings)
output_data = html.data[0]
assert isinstance(output_data, str)
assert output_data == manual_html_finding
def test_batch_write_data_to_file(self):
mock_file = StringIO()
findings = [
generate_finding_output(
remediation_recommendation_url="https://hub.prowler.com/check/check-id"
)
]
output = HTML(findings)
output._file_descriptor = mock_file
provider = set_mocked_aws_provider(audited_regions=[AWS_REGION_EU_WEST_1])
with patch.object(mock_file, "close", return_value=None):
output.batch_write_data_to_file(provider, html_stats)
mock_file.seek(0)
content = mock_file.read()
args = sys.argv[1:]
assert content == get_aws_html_header(args) + pass_html_finding + html_footer
def test_batch_write_data_to_file_without_findings(self):
assert not HTML([])._file_descriptor
def test_write_header(self):
mock_file = StringIO()
findings = [
generate_finding_output(
remediation_recommendation_url="https://hub.prowler.com/check/check-id"
)
]
output = HTML(findings)
output._file_descriptor = mock_file
provider = set_mocked_aws_provider(audited_regions=[AWS_REGION_EU_WEST_1])
output.write_header(mock_file, provider, html_stats)
mock_file.seek(0)
content = mock_file.read()
args = sys.argv[1:]
assert content == get_aws_html_header(args)
def test_write_footer(self):
mock_file = StringIO()
findings = [
generate_finding_output(
remediation_recommendation_url="https://hub.prowler.com/check/check-id"
)
]
output = HTML(findings)
output._file_descriptor = mock_file
output.write_footer(mock_file)
mock_file.seek(0)
content = mock_file.read()
assert content == html_footer
def test_aws_get_assessment_summary(self):
findings = [
generate_finding_output(
remediation_recommendation_url="https://hub.prowler.com/check/check-id"
)
]
output = HTML(findings)
provider = set_mocked_aws_provider(audited_regions=[AWS_REGION_EU_WEST_1])
summary = output.get_assessment_summary(provider)
assert summary == aws_html_assessment_summary
def test_azure_get_assessment_summary(self):
findings = [
generate_finding_output(
remediation_recommendation_url="https://hub.prowler.com/check/check-id"
)
]
output = HTML(findings)
provider = set_mocked_azure_provider()
summary = output.get_assessment_summary(provider)
assert summary == summary
def test_gcp_get_assessment_summary(self):
findings = [
generate_finding_output(
remediation_recommendation_url="https://hub.prowler.com/check/check-id"
)
]
output = HTML(findings)
provider = set_mocked_gcp_provider(project_ids=[GCP_PROJECT_ID])
summary = output.get_assessment_summary(provider)
assert summary == gcp_html_assessment_summary
def test_kubernetes_get_assessment_summary(self):
findings = [
generate_finding_output(
remediation_recommendation_url="https://hub.prowler.com/check/check-id"
)
]
output = HTML(findings)
provider = set_mocked_kubernetes_provider()
summary = output.get_assessment_summary(provider)
assert summary == kubernetes_html_assessment_summary
def test_m365_get_assessment_summary(self):
findings = [
generate_finding_output(
remediation_recommendation_url="https://hub.prowler.com/check/check-id"
)
]
output = HTML(findings)
provider = set_mocked_m365_provider()
summary = output.get_assessment_summary(provider)
expected_summary = m365_html_assessment_summary
assert summary == expected_summary
def test_github_personal_access_token_get_assessment_summary(self):
"""Test GitHub HTML assessment summary generation with Personal Access Token authentication."""
findings = [
generate_finding_output(
remediation_recommendation_url="https://hub.prowler.com/check/check-id"
)
]
output = HTML(findings)
provider = set_mocked_github_provider(auth_method="Personal Access Token")
summary = output.get_assessment_summary(provider)
# Check for expected content in the summary
assert "GitHub Assessment Summary" in summary
assert "GitHub Credentials" in summary
assert "GitHub account: account-name" in summary
assert "GitHub authentication method: Personal Access Token" in summary
# Note: account_email is None in the default fixture, so it shouldn't appear
def test_github_app_get_assessment_summary(self):
"""Test GitHub HTML assessment summary generation with GitHub App authentication."""
findings = [
generate_finding_output(
remediation_recommendation_url="https://hub.prowler.com/check/check-id"
)
]
output = HTML(findings)
provider = set_mocked_github_provider(
auth_method="GitHub App Token",
identity=GithubAppIdentityInfo(
app_id=APP_ID, app_name="test-app", installations=["test-org"]
),
)
summary = output.get_assessment_summary(provider)
logger.error(summary)
# Check for expected content in the summary
assert "GitHub Assessment Summary" in summary
assert "GitHub Credentials" in summary
assert "GitHub App Name: test-app" in summary
assert "Installations: test-org" in summary
assert "GitHub authentication method: GitHub App Token" in summary
assert f"GitHub App ID: {APP_ID}" in summary
def test_mongodbatlas_get_assessment_summary(self):
"""Test MongoDB Atlas HTML assessment summary generation."""
findings = [generate_finding_output()]
output = HTML(findings)
provider = set_mocked_mongodbatlas_provider()
summary = output.get_assessment_summary(provider)
assert summary == mongodbatlas_html_assessment_summary
def test_googleworkspace_get_assessment_summary(self):
"""Test Google Workspace HTML assessment summary generation."""
findings = [generate_finding_output()]
output = HTML(findings)
provider = set_mocked_googleworkspace_provider()
summary = output.get_assessment_summary(provider)
assert "Google Workspace Assessment Summary" in summary
assert "Google Workspace Credentials" in summary
assert "Domain: test-company.com" in summary
assert "Customer ID: C1234567" in summary
assert "Delegated User: prowler-reader@test-company.com" in summary
assert (
"Authentication Method: Service Account with Domain-Wide Delegation"
in summary
)
def test_image_get_assessment_summary_with_registry(self):
"""Test Image HTML assessment summary with registry URL."""
findings = [generate_finding_output()]
output = HTML(findings)
provider = MagicMock()
provider.type = "image"
provider.registry = "myregistry.io"
provider.images = ["nginx:latest", "alpine:3.18"]
provider.auth_method = "Docker login"
summary = output.get_assessment_summary(provider)
assert summary == image_registry_html_assessment_summary
def test_image_get_assessment_summary_with_images(self):
"""Test Image HTML assessment summary with image list."""
findings = [generate_finding_output()]
output = HTML(findings)
provider = MagicMock()
provider.type = "image"
provider.registry = None
provider.images = ["nginx:latest", "alpine:3.18"]
provider.auth_method = "No auth"
summary = output.get_assessment_summary(provider)
assert summary == image_list_html_assessment_summary
def test_stackit_get_assessment_summary(self):
"""Test StackIT HTML assessment summary shows the project ID."""
findings = [generate_finding_output()]
output = HTML(findings)
provider = MagicMock()
provider.type = "stackit"
provider.identity.project_id = "f033ea6d-8697-40eb-a60e-acfa9128480d"
provider.identity.project_name = "ProwlerDev"
provider.identity.audited_regions = {"eu01", "eu02"}
summary = output.get_assessment_summary(provider)
assert "StackIT Assessment Summary" in summary
assert "StackIT Credentials" in summary
assert "Project ID: f033ea6d-8697-40eb-a60e-acfa9128480d" in summary
assert "Project Name: ProwlerDev" in summary
assert "Regions: eu01, eu02" in summary
assert "Authentication Type: Service Account Key" in summary
def test_stackit_get_assessment_summary_without_project_name(self):
"""Project ID is always shown; the Project Name line is omitted when
the service account cannot read it from Resource Manager."""
findings = [generate_finding_output()]
output = HTML(findings)
provider = MagicMock()
provider.type = "stackit"
provider.identity.project_id = "f033ea6d-8697-40eb-a60e-acfa9128480d"
provider.identity.project_name = ""
provider.identity.audited_regions = {"eu01"}
summary = output.get_assessment_summary(provider)
assert "Project ID: f033ea6d-8697-40eb-a60e-acfa9128480d" in summary
assert "Project Name:" not in summary
def test_process_markdown_bold_text(self):
"""Test that **text** is converted to text"""
test_text = "This is **bold text** and this is **also bold**"
result = HTML.process_markdown(test_text)
expected = (
"This is bold text and this is also bold"
)
assert result == expected
def test_process_markdown_italic_text(self):
"""Test that *text* is converted to text"""
test_text = "This is *italic text* and this is *also italic*"
result = HTML.process_markdown(test_text)
expected = "This is italic text and this is also italic"
assert result == expected
def test_process_markdown_code_text(self):
"""Test that `text` is converted to text"""
test_text = "Use the `ls` command to list files and `cd` to change directories"
result = HTML.process_markdown(test_text)
expected = "Use the ls command to list files and cd to change directories"
assert result == expected
def test_process_markdown_line_breaks(self):
"""Test that line breaks are converted to tags"""
test_text = "Line 1\nLine 2\nLine 3"
result = HTML.process_markdown(test_text)
expected = "Line 1 \nLine 2 \nLine 3"
assert result == expected
def test_process_markdown_mixed_formatting(self):
"""Test mixed markdown formatting"""
test_text = "**Bold text** with *italic* and `code` elements.\n\nNew paragraph with **more bold**."
result = HTML.process_markdown(test_text)
expected = "Bold text with italic and code elements. \n \nNew paragraph with more bold."
assert result == expected
def test_process_markdown_empty_string(self):
"""Test that empty string returns empty string"""
result = HTML.process_markdown("")
assert result == ""
def test_process_markdown_none_input(self):
"""Test that None input returns None"""
result = HTML.process_markdown(None)
assert result is None
def test_process_markdown_no_markdown(self):
"""Test that plain text without markdown is returned unchanged"""
test_text = "This is plain text without any markdown formatting"
result = HTML.process_markdown(test_text)
assert result == test_text
def test_transform_with_markdown_risk(self):
"""Test that Risk field with markdown is properly converted"""
findings = [
generate_finding_output(
risk="Outdated contacts delay **security notifications** and slow **incident response**",
remediation_recommendation_url="https://hub.prowler.com/check/check-id",
)
]
html = HTML(findings)
output_data = html.data[0]
# Check that markdown is converted to HTML
assert "security notifications" in output_data
assert "incident response" in output_data
def test_transform_with_markdown_recommendation(self):
"""Test that Recommendation field with markdown is properly converted"""
findings = [
generate_finding_output(
risk="test-risk",
remediation_recommendation_text="Adopt:\n- **Primary** and **alternate contacts**\n- Use `monitored aliases`",
remediation_recommendation_url="https://hub.prowler.com/check/check-id",
)
]
html = HTML(findings)
output_data = html.data[0]
# Check that markdown is converted to HTML
assert "Primary" in output_data
assert "alternate contacts" in output_data
assert "monitored aliases" in output_data
assert " " in output_data # Line breaks converted