diff --git a/util/compliance_report/readme.md b/util/compliance_report/readme.md index da0d565fbb..4c18204e59 100644 --- a/util/compliance_report/readme.md +++ b/util/compliance_report/readme.md @@ -1,11 +1,11 @@ # ThreatScore Compliance Report Generator -This tool generates a PDF compliance report using Prowler's API endpoints, summarizing compliance requirements, risk levels, and findings for a given scan and compliance framework. +This tool generates a compliance PDF report using Prowler's API endpoints, summarizing requirements, risk levels, and findings for a given scan and compliance framework. ## Features -- Authenticates with the Prowler API using email and password -- Retrieves compliance requirements and attributes for a given scan and compliance framework -- Generates a visually rich PDF report, including: +- Authenticate with the Prowler API using email+password or token +- Retrieve compliance requirements and attributes for a given scan and framework +- Generate a visually rich PDF report, including: - Compliance summary and description - Compliance score by section (with charts) - Critical failed requirements (with risk and weight) @@ -23,30 +23,34 @@ pip install matplotlib requests reportlab ## Usage ```bash -python3 util/compliance_report/generate_threatscore_report.py \ +python3 util/compliance_report/threatscore_report_generator.py \ --scan-id \ --compliance-id \ --email \ --password \ + [--token ] \ [--output ] \ [--base-url ] \ [--only-failed] \ [--min-risk-level ] ``` +> **Note:** You must provide either both `--email` and `--password`, or a `--token`. One of these authentication methods is required. If you provide a token, email and password are ignored. + ### Arguments - `--scan-id` (required): ID of the scan executed by Prowler. - `--compliance-id` (required): Compliance framework ID (e.g., `prowler_threatscore_azure`, `nis2_azure`). -- `--email` (required): Email for API authentication. -- `--password` (required): Password for API authentication. +- `--email` (required*): Email for API authentication (*required if `--token` is not used). +- `--password` (required*): Password for API authentication (*required if `--token` is not used). +- `--token` (required*): JWT token for authentication (*required if `--email` and `--password` are not used). - `--output` (optional): Output PDF file path (default: `threatscore_report.pdf`). - `--base-url` (optional): Base URL for the API (default: `http://localhost:8080`). - `--only-failed` (optional): Only include failed requirements in the report. -- `--min-risk-level` (optional): Minimum risk level for critical failed requirements (default: 4). +- `--min-risk-level` (optional): Minimum risk level to show critical failed requirements (default: 4). ### Example ```bash -python3 util/compliance_report/generate_threatscore_report.py \ +python3 util/compliance_report/threatscore_report_generator.py \ --scan-id 12345678-1234-5678-1234-567812345678 \ --compliance-id prowler_threatscore_azure \ --email user@example.com \ @@ -57,6 +61,15 @@ python3 util/compliance_report/generate_threatscore_report.py \ --min-risk-level 4 ``` +Or using a token: +```bash +python3 util/compliance_report/threatscore_report_generator.py \ + --scan-id 12345678-1234-5678-1234-567812345678 \ + --compliance-id prowler_threatscore_azure \ + --token eyJhbGciOi... \ + --output my_report.pdf +``` + ## Output - The script will generate a PDF file with: - Compliance framework summary @@ -65,6 +78,6 @@ python3 util/compliance_report/generate_threatscore_report.py \ - Detailed breakdown of each requirement and its findings ## Notes -- The script authenticates with the API and retrieves all necessary data automatically. -- If you encounter authentication errors, check your email, password, and API URL. -- For more details, see the script source: `util/compliance_report/generate_threatscore_report.py` +- The script can authenticate with email/password or directly with a JWT token. **One of these authentication methods is mandatory.** +- If you encounter authentication errors, check your credentials, token, and API URL. +- For more details, see the source code: `util/compliance_report/threatscore_report_generator.py` diff --git a/util/compliance_report/threatscore_report_generator.py b/util/compliance_report/threatscore_report_generator.py index a6b670b18a..0ea49aa868 100644 --- a/util/compliance_report/threatscore_report_generator.py +++ b/util/compliance_report/threatscore_report_generator.py @@ -44,6 +44,7 @@ def generate_threatscore_report( output_path: str, email: str, password: str, + token: str, base_url: str, only_failed: bool = True, min_risk_level: int = 4, @@ -57,6 +58,7 @@ def generate_threatscore_report( - output_path: Output PDF file path (e.g., "threatscore_report.pdf"). - email: Email for the API authentication. - password: Password for the API. + - token: Token for the API. - base_url: Base URL for the API. - only_failed: If True, only requirements with status "FAIL" will be included in the list of requirements. - min_risk_level: Minimum risk level for critical failed requirements. @@ -134,40 +136,46 @@ def generate_threatscore_report( textColor=colors.Color(0.2, 0.2, 0.2), fontName="PlusJakartaSans", ) - - url_credentials = f"{base_url}/api/v1/tokens" - payload = { - "data": { - "type": "tokens", - "attributes": { - "email": email, - "password": password, - }, + if not token: + if not email or not password: + raise Exception("Email and password are required to generate a token") + url_credentials = f"{base_url}/api/v1/tokens" + payload = { + "data": { + "type": "tokens", + "attributes": { + "email": email, + "password": password, + }, + } } - } - resp_credentials = requests.post( - url_credentials, - json=payload, - headers={"Content-Type": "application/vnd.api+json"}, - ).json() - if resp_credentials.get("errors"): - print(resp_credentials.get("errors")) - raise Exception(resp_credentials.get("errors")) - token = resp_credentials.get("data", {}).get("attributes", {}).get("access") + resp_credentials = requests.post( + url_credentials, + json=payload, + headers={"Content-Type": "application/vnd.api+json"}, + ).json() + if resp_credentials.get("errors"): + print(resp_credentials.get("errors")) + raise Exception(resp_credentials.get("errors")) + token = resp_credentials.get("data", {}).get("attributes", {}).get("access") - url_reqs = f"{base_url}/api/v1/compliance-overviews/requirements?filter[compliance_id]={compliance_id}&filter[scan_id]={scan_id}" - resp_reqs = ( - requests.get(url_reqs, headers={"Authorization": f"Bearer {token}"}) - .json() - .get("data", []) - ) + try: + url_reqs = f"{base_url}/api/v1/compliance-overviews/requirements?filter[compliance_id]={compliance_id}&filter[scan_id]={scan_id}" + resp_reqs = ( + requests.get(url_reqs, headers={"Authorization": f"Bearer {token}"}) + .json() + .get("data", []) + ) - url_attrs = f"{base_url}/api/v1/compliance-overviews/attributes?filter[compliance_id]={compliance_id}" - resp_attrs = ( - requests.get(url_attrs, headers={"Authorization": f"Bearer {token}"}) - .json() - .get("data", []) - ) + url_attrs = f"{base_url}/api/v1/compliance-overviews/attributes?filter[compliance_id]={compliance_id}" + resp_attrs = ( + requests.get(url_attrs, headers={"Authorization": f"Bearer {token}"}) + .json() + .get("data", []) + ) + except Exception as e: + print(e) + raise Exception(e) compliance_name = resp_reqs[0]["attributes"]["framework"] compliance_version = resp_reqs[0]["attributes"]["version"] @@ -876,6 +884,11 @@ if __name__ == "__main__": action="store_true", help="Only include failed requirements in the list of requirements", ) + parser.add_argument( + "--token", + default="", + help="Token for the API", + ) parser.add_argument( "--base-url", default="http://localhost:8080", @@ -895,6 +908,7 @@ if __name__ == "__main__": args.output, args.email, args.password, + args.token, args.base_url, args.only_failed, args.min_risk_level,