diff --git a/util/compliance_report/compliance_generator.py b/util/compliance_report/compliance_generator.py index e8e59df58b..bc0f6deff6 100644 --- a/util/compliance_report/compliance_generator.py +++ b/util/compliance_report/compliance_generator.py @@ -136,6 +136,7 @@ def generate_compliance_report( compliance_name = resp_reqs[0]["attributes"]["framework"] compliance_version = resp_reqs[0]["attributes"]["version"] + compliance_description = resp_attrs[0]["attributes"]["framework_description"] attrs_map = {item["id"]: item["attributes"] for item in resp_attrs} @@ -340,7 +341,7 @@ def generate_compliance_report( return buffer def get_finding_info(check_id: str): - url_find = f"http://localhost:8080/api/v1/findings?filter[check_id]={check_id}&filter[scan_id]={scan_id}" + url_find = f"http://localhost:8080/api/v1/findings?filter[check_id]={check_id}&filter[scan]={scan_id}" value = ( requests.get(url_find, headers={"Authorization": f"Bearer {token}"}) .json() @@ -368,27 +369,41 @@ def generate_compliance_report( elements.append(logo) except Exception: pass - elements.append(Spacer(1, 0.3 * inch)) + elements.append(Spacer(1, 0.5 * inch)) elements.append(Paragraph("Compliance Report - Prowler", title_style)) - elements.append(Spacer(1, 5 * inch)) + elements.append(Spacer(1, 0.5 * inch)) + + pretty_description = ( + ( + "Prowler ThreatScore Compliance Framework for Azure ensures that the Azure subscription is compliant, " + "taking into account four main pillars:
" + "- Identity and Access Management
" + "- Attack Surface
" + "- Forensic Readiness
" + "- Encryption" + ) + if compliance_id == "prowler_threatscore_azure" + else compliance_description + ) info_data = [ ["Compliance Framework:", compliance_name], ["Compliance ID:", compliance_id], ["Version:", compliance_version], ["Scan ID:", scan_id], + ["Description:", ""], + [Paragraph(pretty_description, normal), ""], ] - info_table = Table(info_data, colWidths=[2 * inch, 4 * inch]) info_table.setStyle( TableStyle( [ - ("BACKGROUND", (0, 0), (0, -1), colors.Color(0.2, 0.4, 0.6)), - ("TEXTCOLOR", (0, 0), (0, -1), colors.white), - ("FONTNAME", (0, 0), (0, -1), "Helvetica-Bold"), - ("BACKGROUND", (1, 0), (1, -1), colors.Color(0.95, 0.97, 1.0)), - ("TEXTCOLOR", (1, 0), (1, -1), colors.Color(0.2, 0.2, 0.2)), - ("FONTNAME", (1, 0), (1, -1), "Helvetica"), + ("BACKGROUND", (0, 0), (0, 4), colors.Color(0.2, 0.4, 0.6)), + ("TEXTCOLOR", (0, 0), (0, 4), colors.white), + ("FONTNAME", (0, 0), (0, 4), "Helvetica-Bold"), + ("BACKGROUND", (1, 0), (1, 3), colors.Color(0.95, 0.97, 1.0)), + ("TEXTCOLOR", (1, 0), (1, 3), colors.Color(0.2, 0.2, 0.2)), + ("FONTNAME", (1, 0), (1, 3), "Helvetica"), ("ALIGN", (0, 0), (-1, -1), "LEFT"), ("VALIGN", (0, 0), (-1, -1), "MIDDLE"), ("FONTSIZE", (0, 0), (-1, -1), 11), @@ -397,6 +412,8 @@ def generate_compliance_report( ("RIGHTPADDING", (0, 0), (-1, -1), 10), ("TOPPADDING", (0, 0), (-1, -1), 8), ("BOTTOMPADDING", (0, 0), (-1, -1), 8), + ("SPAN", (0, 5), (1, 5)), + ("VALIGN", (0, 5), (1, 5), "TOP"), ] ) ) @@ -718,13 +735,90 @@ def generate_compliance_report( Paragraph("- No information for this finding currently", normal) ) else: + # Render findings as a table + findings_table_data = [ + [ + "Finding", + "Resource name", + "Severity", + "Status", + "Last seen", + "Region", + ] + ] for f in finds: - fid = f.get("id") - resource = f.get("attributes", {}).get("resource_id", "") - message = f.get("attributes", {}).get("message", "") - elements.append( - Paragraph(f"- [{fid}] {resource}: {message}", normal) + attr = f["attributes"] + check_meta = attr.get("check_metadata", {}) + title = check_meta.get("checktitle", attr.get("check_id", "")) + # Resource name: try to get from relationships/resources/data[0].id if not in attributes + resource_name = attr.get("resource_id") + if not resource_name: + rel_resources = ( + f.get("relationships", {}) + .get("resources", {}) + .get("data", []) + ) + if rel_resources and isinstance(rel_resources, list): + resource_name = rel_resources[0].get("id", "") + else: + resource_name = "" + severity = attr.get("severity", "").capitalize() + status = attr.get("status", "").upper() + last_seen = attr.get("inserted_at", "") + if last_seen: + last_seen = last_seen.replace("T", " ")[:16] + region = attr.get("region", "global") + + findings_table_data.append( + [ + Paragraph(title, normal), + Paragraph(resource_name, normal), + Paragraph(severity, normal), + Paragraph(status, normal), + Paragraph(last_seen, normal), + Paragraph(region, normal), + ] ) + findings_table = Table( + findings_table_data, + colWidths=[ + 2.5 * inch, + 1.2 * inch, + 0.8 * inch, + 0.8 * inch, + 1.2 * inch, + 0.8 * inch, + ], + ) + findings_table.setStyle( + TableStyle( + [ + ( + "BACKGROUND", + (0, 0), + (-1, 0), + colors.Color(0.2, 0.4, 0.6), + ), + ("TEXTCOLOR", (0, 0), (-1, 0), colors.white), + ("FONTNAME", (0, 0), (-1, 0), "Helvetica-Bold"), + ("ALIGN", (0, 0), (-1, -1), "LEFT"), + ("VALIGN", (0, 0), (-1, -1), "MIDDLE"), + ("FONTSIZE", (0, 0), (-1, -1), 9), + ( + "GRID", + (0, 0), + (-1, -1), + 0.5, + colors.Color(0.7, 0.8, 0.9), + ), + ("LEFTPADDING", (0, 0), (-1, -1), 6), + ("RIGHTPADDING", (0, 0), (-1, -1), 6), + ("TOPPADDING", (0, 0), (-1, -1), 4), + ("BOTTOMPADDING", (0, 0), (-1, -1), 4), + ] + ) + ) + elements.append(findings_table) elements.append(Spacer(1, 0.1 * inch)) elements.append(PageBreak())