diff --git a/dojo/tools/sysdig_reports/parser.py b/dojo/tools/sysdig_reports/parser.py index c488c307ea2..c3b6de97046 100644 --- a/dojo/tools/sysdig_reports/parser.py +++ b/dojo/tools/sysdig_reports/parser.py @@ -24,10 +24,12 @@ def get_description_for_scan_types(self, scan_type): def get_findings(self, filename, test): if filename is None: - return () + return [] + if filename.name.lower().endswith(".csv"): arr_data = self.load_csv(filename=filename) return self.parse_csv(arr_data=arr_data, test=test) + if filename.name.lower().endswith(".json"): scan_data = filename.read() try: @@ -35,15 +37,23 @@ def get_findings(self, filename, test): except Exception: data = json.loads(scan_data) - if "data" in data: - return self.parse_json(data=data, test=test) + # Handle both old and new JSON formats + if "data" in data: + # Old format: data is at root level + return self.parse_json(data=data, test=test) + + if "panels" in data and len(data["panels"]) > 0 and "data" in data["panels"][0]: + # New 2025 format: data is nested in panels[0] + return self.parse_json(data=data["panels"][0], test=test) - if "result" in data: - msg = "JSON file is not in the expected format, it looks like a Sysdig CLI report." - else: - msg = "JSON file is not in the expected format, expected data element" + if "result" in data: + msg = "JSON file is not in the expected format, it looks like a Sysdig CLI report." raise ValueError(msg) - return () + msg = "JSON file is not in the expected format, expected data element" + raise ValueError(msg) + + msg = "Expected CSV or JSON file" + raise ValueError(msg) def parse_json(self, data, test): vulnerability = data.get("data", None) @@ -51,31 +61,33 @@ def parse_json(self, data, test): return [] findings = [] for item in vulnerability: - imageId = item.get("imageId", "") - imagePullString = item.get("imagePullString", "") - osName = item.get("osName", "") - k8sClusterName = item.get("k8sClusterName", "") - k8sNamespaceName = item.get("k8sNamespaceName", "") - k8sWorkloadType = item.get("k8sWorkloadType", "") - k8sWorkloadName = item.get("k8sWorkloadName", "") - k8sPodContainerName = item.get("k8sPodContainerName", "") - vulnName = item.get("vulnName", "") - vulnSeverity = item.get("vulnSeverity", "") - vulnLink = item.get("vulnLink", "") - vulnCvssVersion = item.get("vulnCvssVersion", "") - vulnCvssScore = item.get("vulnCvssScore", "") - vulnCvssVector = item.get("vulnCvssVector", "") - vulnDisclosureDate = item.get("vulnDisclosureDate", "") - vulnSolutionDate = item.get("vulnSolutionDate", "") - vulnExploitable = item.get("vulnExploitable", "") - vulnFixAvailable = item.get("vulnFixAvailable", "") - vulnFixVersion = item.get("vulnFixVersion", "") - packageName = item.get("packageName", "") - packageType = item.get("packageType", "") - packagePath = item.get("packagePath", "") - packageVersion = item.get("packageVersion", "") - packageSuggestedFix = item.get("packageSuggestedFix", "") - k8sPodCount = item.get("k8sPodCount", "") + # Handle both old and new JSON field names + # Old format uses camelCase, new format uses snake_case with different field names + imageId = item.get("imageId", "") or item.get("container_image_id", "") + imagePullString = item.get("imagePullString", "") or item.get("container_image", "") + osName = item.get("osName", "") or item.get("container_image_distro", "") + k8sClusterName = item.get("k8sClusterName", "") or item.get("kubernetes_cluster_name", "") + k8sNamespaceName = item.get("k8sNamespaceName", "") or item.get("kubernetes_namespace_name", "") + k8sWorkloadType = item.get("k8sWorkloadType", "") or item.get("kubernetes_workload_type", "") + k8sWorkloadName = item.get("k8sWorkloadName", "") or item.get("kubernetes_workload_name", "") + k8sPodContainerName = item.get("k8sPodContainerName", "") or item.get("kubernetes_pod_container_name", "") + vulnName = item.get("vulnName", "") or item.get("vuln_id", "") + vulnSeverity = item.get("vulnSeverity", "") or item.get("vuln_severity", "") + vulnLink = item.get("vulnLink", "") or "" # Not present in new format + vulnCvssVersion = item.get("vulnCvssVersion", "") or item.get("vuln_cvss_version", "") + vulnCvssScore = item.get("vulnCvssScore", "") or item.get("vuln_cvss_score", "") + vulnCvssVector = item.get("vulnCvssVector", "") or item.get("vuln_cvss_vector", "") + vulnDisclosureDate = item.get("vulnDisclosureDate", "") or item.get("vuln_disclosure_date", "") + vulnSolutionDate = item.get("vulnSolutionDate", "") or item.get("vuln_fix_available_date", "") + vulnExploitable = item.get("vulnExploitable", "") or item.get("vuln_has_exploit", "") + vulnFixAvailable = item.get("vulnFixAvailable", "") or item.get("vuln_fix_available", "") + vulnFixVersion = item.get("vulnFixVersion", "") or item.get("vuln_fixed_in_version", "") + packageName = item.get("packageName", "") or item.get("scan_package_name", "") + packageType = item.get("packageType", "") or item.get("scan_package_type", "") + packagePath = item.get("packagePath", "") or item.get("scan_package_path", "") + packageVersion = item.get("packageVersion", "") or item.get("scan_package_version", "") + packageSuggestedFix = item.get("packageSuggestedFix", "") or "" # Not present in new format + k8sPodCount = item.get("k8sPodCount", "") or "" # Not present in new format description = "" description += "imageId: " + imageId + "\n" @@ -266,8 +278,9 @@ def load_csv(self, filename) -> SysdigData: msg = "Unknown CSV format: CVE ID column found, looks like a SysDig CLI Report" raise ValueError(msg) - if "vulnerability id" not in reader.fieldnames: - msg = "Unknown CSV format: expected Vulnerability ID column" + # Support both old and new CSV formats + if "vulnerability id" not in reader.fieldnames and "vulnerability name" not in reader.fieldnames: + msg = "Unknown CSV format: expected either 'Vulnerability ID' or 'Vulnerability Name' column" raise ValueError(msg) arr_csv_data = [] @@ -275,10 +288,12 @@ def load_csv(self, filename) -> SysdigData: for row in csvarray: csv_data_record = SysdigData() + + # Handle both old and new CSV formats if "vulnerability id" in reader.fieldnames: - # Vulnerability Engine Format + # Old format: Vulnerability Engine Format csv_data_record.vulnerability_id = row.get("vulnerability id", "") - csv_data_record.severity = csv_data_record._map_severity(row.get("severity").upper()) + csv_data_record.severity = csv_data_record._map_severity(row.get("severity", "").upper()) csv_data_record.package_name = row.get("package name", "") csv_data_record.package_version = row.get("package version", "") csv_data_record.package_type = row.get("package type", "") @@ -310,6 +325,42 @@ def load_csv(self, filename) -> SysdigData: csv_data_record.cloud_provider_region = row.get("cloud provider region", "") csv_data_record.registry_vendor = row.get("registry vendor", "") - arr_csv_data.append(csv_data_record) + elif "vulnerability name" in reader.fieldnames: + # New 2025 format + csv_data_record.vulnerability_id = row.get("vulnerability name", "") + csv_data_record.severity = csv_data_record._map_severity(row.get("vulnerability severity", "").upper()) + csv_data_record.package_name = row.get("package name", "") + csv_data_record.package_version = row.get("package version", "") + csv_data_record.package_type = row.get("package type", "") + csv_data_record.package_path = row.get("package path", "") + csv_data_record.image = row.get("image name", "") + csv_data_record.os_name = row.get("os name", "") + csv_data_record.cvss_version = row.get("cvss version", "") + csv_data_record.cvss_score = row.get("cvss score", "") + csv_data_record.cvss_vector = row.get("cvss vector", "") + # Map new 2025 column names to existing fields + csv_data_record.vuln_link = "" # Not present in 2025 format + csv_data_record.vuln_publish_date = row.get("disclosure date", "") + csv_data_record.vuln_fix_date = row.get("fix available date", "") + csv_data_record.vuln_fix_version = row.get("fix version", "") + csv_data_record.public_exploit = row.get("public exploit", "") + csv_data_record.k8s_cluster_name = row.get("kubernetes cluster name", "") + csv_data_record.k8s_namespace_name = row.get("kubernetes namespace name", "") + csv_data_record.k8s_workload_type = row.get("kubernetes workload type", "") + csv_data_record.k8s_workload_name = row.get("kubernetes workload name", "") + csv_data_record.k8s_container_name = row.get("kubernetes container name", "") + csv_data_record.image_id = row.get("image id", "") + csv_data_record.k8s_pod_count = "" # Not present in 2025 format + csv_data_record.package_suggested_fix = "" # Not present in 2025 format + csv_data_record.in_use = row.get("package in use", "").lower() == "true" + csv_data_record.risk_accepted = row.get("risk accepted", "").lower() == "true" + csv_data_record.registry_name = "" # Not present in 2025 format + csv_data_record.registry_image_repository = "" # Not present in 2025 format + csv_data_record.cloud_provider_name = "" # Not present in 2025 format + csv_data_record.cloud_provider_account_id = "" # Not present in 2025 format + csv_data_record.cloud_provider_region = "" # Not present in 2025 format + csv_data_record.registry_vendor = "" # Not present in 2025 format + + arr_csv_data.append(csv_data_record) return arr_csv_data diff --git a/unittests/scans/sysdig_reports/sysdig-2025.csv b/unittests/scans/sysdig_reports/sysdig-2025.csv new file mode 100644 index 00000000000..b9bf2ca0faa --- /dev/null +++ b/unittests/scans/sysdig_reports/sysdig-2025.csv @@ -0,0 +1,7 @@ +Vulnerability Name,Vulnerability Severity,Package Name,Package Version,Package Type,Package Path,Image Name,OS Name,CVSS Version,CVSS Score,CVSS Vector,Disclosure Date,Fix Available Date,Fix Version,Public Exploit,Kubernetes Cluster Name,Kubernetes Namespace Name,Kubernetes Workload Type,Kubernetes Workload Name,Kubernetes Container Name,Image ID,Package In Use,Risk Accepted,CISA KEV Publish Date,CISA KEV Due Date,CISA KEV Known Ransomware,Fix Available +CVE-2005-2541,Negligible,package name 1,0.0.0.1,OS,/path/path/file.abc,defectdojo/defectdojo-django:latest,debian 12.9,2,10,AV:N/AC:L/Au:N/C:C/I:C/A:C,2005-08-10T04:00:00Z,1970-01-01T00:00:00Z,,false,k8s name 1,defectdojo,Deployment,defectdojo-celery-worker,celery,sha256:a520752f350909c191db45a598a88fcca2fa5db17a340dee6b3d0e36f4122e11,false,false,,,,false +CVE-2005-2541,Negligible,package name 2,0.0.0.2,OS,/path/path/file.abc,defectdojo/defectdojo-django:latest,debian 12.9,2,10,AV:N/AC:L/Au:N/C:C/I:C/A:C,2005-08-10T04:00:00Z,1970-01-01T00:00:00Z,,false,k8s name 2,defectdojo,Deployment,defectdojo-django,uwsgi,sha256:a520752f350909c191db45a598a88fcca2fa5db17a340dee6b3d0e36f4122e11,false,false,,,,false +CVE-2005-2541,Negligible,package name 3,0.0.0.3,OS,/path/path/file.abc,docker.io/bitnami/redis:7.2.5-debian-12-r4,debian 12.6,2,10,AV:N/AC:L/Au:N/C:C/I:C/A:C,2005-08-10T04:00:00Z,1970-01-01T00:00:00Z,,false,k8s name 3,defectdojo,Deployment,defectdojo-redis-master,redis,sha256:a520752f350909c191db45a598a88fcca2fa5db17a340dee6b3d0e36f4122e11,false,false,,,,false +CVE-2025-48379,Critical,package name 4,0.0.0.4,Python,/path/path/path/file.abc,defectdojo/defectdojo-django:latest,debian 12.9,3,9.8,CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H,2025-07-01T17:29:37Z,2025-07-01T09:15:58Z,11.3.0,false,k8s name 4,defectdojo,Deployment,defectdojo-celery-beat,celery,sha256:a520752f350909c191db45a598a88fcca2fa5db17a340dee6b3d0e36f4122e11,true,false,,,,true +CVE-2025-6021,Critical,package name 5,0.0.0.5,OS,/path/path/file.abc,defectdojo/defectdojo-django:latest,debian 12.9,3,9.8,CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H,2025-06-12T13:15:25Z,1970-01-01T00:00:00Z,,false,k8s name 5,defectdojo,Deployment,defectdojo-django,uwsgi,sha256:a520752f350909c191db45a598a88fcca2fa5db17a340dee6b3d0e36f4122e11,false,false,,,,false +CVE-2011-10007,Critical,package name 6,0.0.0.6,OS,/path/path/file.abc,docker.io/bitnami/redis:7.2.5-debian-12-r4,debian 12.6,3,9.8,CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H,2025-06-05T12:15:22Z,1970-01-01T00:00:00Z,0.34-4~deb12u1,false,k8s name 6,defectdojo,Deployment,defectdojo-redis-master,redis,sha256:a520752f350909c191db45a598a88fcca2fa5db17a340dee6b3d0e36f4122e11,false,false,,,,true \ No newline at end of file diff --git a/unittests/scans/sysdig_reports/sysdig-2025.json b/unittests/scans/sysdig_reports/sysdig-2025.json new file mode 100644 index 00000000000..e91c5f685a0 --- /dev/null +++ b/unittests/scans/sysdig_reports/sysdig-2025.json @@ -0,0 +1,220 @@ +{ + "name": "Kubernetes Workload Vulnerability Findings", + "description": "Provides a detailed list of individual vulnerability findings for every Kubernetes workload. Each row represents a unique vulnerability per package.", + "owner": "example@example.com", + "schedule": { + "user": "example@example.com", + "frequency": "ad-hoc" + }, + "id": "434", + "zones": [ + "sec-eng-tools-dev" + ], + "panels": [ + { + "name": "Kubernetes Workload Vulnerability Findings", + "type": "table", + "description": "Table of all vulnerabilities found in Kubernetes workloads. Replacement for the legacy workload report", + "filters": [ + { + "name": "VulnSeverity", + "op": "in", + "values": [ + "Critical", + "High", + "Medium", + "Low", + "Negligible", + "Other" + ] + }, + { + "name": "StartTime", + "op": "=", + "values": "1752012000" + }, + { + "name": "EndTime", + "op": "=", + "values": "1752616800" + } + ], + "data": [ + { + "container_image": "defectdojo/defectdojo-django:latest", + "container_image_distro": "debian 12.9", + "container_image_id": "sha256:a520752f350909c191db45a598a88fcca2fa5db17a340dee6b3d0e36f4122e11", + "kubernetes_cluster_name": "sec-eng-tools-dev", + "kubernetes_namespace_name": "defectdojo", + "kubernetes_pod_container_name": "celery", + "kubernetes_workload_name": "defectdojo-celery-worker", + "kubernetes_workload_type": "Deployment", + "scan_package_in_use": false, + "scan_package_name": "tar", + "scan_package_path": "/path/path/file.abc", + "scan_package_type": "OS", + "scan_package_version": "1.34+dfsg-1.2+deb12u1", + "vuln_cisa_kev_due_date": "", + "vuln_cisa_kev_known_ransomware": "", + "vuln_cisa_kev_publish_date": "", + "vuln_cvss_score": 10, + "vuln_cvss_vector": "AV:N/AC:L/Au:N/C:C/I:C/A:C", + "vuln_cvss_version": "2.0", + "vuln_disclosure_date": "2005-08-10T04:00:00Z", + "vuln_fix_available": false, + "vuln_fix_available_date": "1970-01-01T00:00:00Z", + "vuln_fixed_in_version": "", + "vuln_has_exploit": false, + "vuln_id": "CVE-2005-2541", + "vuln_risk_accepted": false, + "vuln_severity": "Negligible" + }, + { + "container_image": "defectdojo/defectdojo-django:latest", + "container_image_distro": "debian 12.9", + "container_image_id": "sha256:a520752f350909c191db45a598a88fcca2fa5db17a340dee6b3d0e36f4122e11000000", + "kubernetes_cluster_name": "sec-eng-tools-dev", + "kubernetes_namespace_name": "defectdojo", + "kubernetes_pod_container_name": "uwsgi", + "kubernetes_workload_name": "defectdojo-django", + "kubernetes_workload_type": "Deployment", + "scan_package_in_use": false, + "scan_package_name": "tar", + "scan_package_path": "/path/path/file.abc", + "scan_package_type": "OS", + "scan_package_version": "1.34+dfsg-1.2+deb12u1", + "vuln_cisa_kev_due_date": "", + "vuln_cisa_kev_known_ransomware": "", + "vuln_cisa_kev_publish_date": "", + "vuln_cvss_score": 10, + "vuln_cvss_vector": "AV:N/AC:L/Au:N/C:C/I:C/A:C", + "vuln_cvss_version": "2.0", + "vuln_disclosure_date": "2005-08-10T04:00:00Z", + "vuln_fix_available": false, + "vuln_fix_available_date": "1970-01-01T00:00:00Z", + "vuln_fixed_in_version": "", + "vuln_has_exploit": false, + "vuln_id": "CVE-2005-2541", + "vuln_risk_accepted": false, + "vuln_severity": "Negligible" + }, + { + "container_image": "defectdojo/defectdojo-django:latest", + "container_image_distro": "debian 12.9", + "container_image_id": "sha256:a520752f350909c191db45a598a88fcca2fa5db17a340dee6b3d0e36f4122e11", + "kubernetes_cluster_name": "sec-eng-tools-dev", + "kubernetes_namespace_name": "defectdojo", + "kubernetes_pod_container_name": "celery", + "kubernetes_workload_name": "defectdojo-celery-beat", + "kubernetes_workload_type": "Deployment", + "scan_package_in_use": false, + "scan_package_name": "tar", + "scan_package_path": "/path/path/file.abc", + "scan_package_type": "OS", + "scan_package_version": "1.34+dfsg-1.2+deb12u1", + "vuln_cisa_kev_due_date": "", + "vuln_cisa_kev_known_ransomware": "", + "vuln_cisa_kev_publish_date": "", + "vuln_cvss_score": 10, + "vuln_cvss_vector": "AV:N/AC:L/Au:N/C:C/I:C/A:C", + "vuln_cvss_version": "2.0", + "vuln_disclosure_date": "2005-08-10T04:00:00Z", + "vuln_fix_available": false, + "vuln_fix_available_date": "1970-01-01T00:00:00Z", + "vuln_fixed_in_version": "", + "vuln_has_exploit": false, + "vuln_id": "CVE-2005-2541", + "vuln_risk_accepted": false, + "vuln_severity": "Negligible" + }, + { + "container_image": "docker.io/bitnami/redis:7.2.5-debian-12-r4", + "container_image_distro": "debian 12.6", + "container_image_id": "sha256:a520752f350909c191db45a598a88fcca2fa5db17a340dee6b3d0e36f4122e11", + "kubernetes_cluster_name": "sec-eng-tools-dev", + "kubernetes_namespace_name": "defectdojo", + "kubernetes_pod_container_name": "redis", + "kubernetes_workload_name": "defectdojo-redis-master", + "kubernetes_workload_type": "StatefulSet", + "scan_package_in_use": false, + "scan_package_name": "tar", + "scan_package_path": "/path/path/file.abc", + "scan_package_type": "OS", + "scan_package_version": "1.34+dfsg-1.2+deb12u1", + "vuln_cisa_kev_due_date": "", + "vuln_cisa_kev_known_ransomware": "", + "vuln_cisa_kev_publish_date": "", + "vuln_cvss_score": 10, + "vuln_cvss_vector": "AV:N/AC:L/Au:N/C:C/I:C/A:C", + "vuln_cvss_version": "2.0", + "vuln_disclosure_date": "2005-08-10T04:00:00Z", + "vuln_fix_available": false, + "vuln_fix_available_date": "1970-01-01T00:00:00Z", + "vuln_fixed_in_version": "", + "vuln_has_exploit": false, + "vuln_id": "CVE-2005-2541", + "vuln_risk_accepted": false, + "vuln_severity": "Negligible" + }, + { + "container_image": "defectdojo/defectdojo-django:latest", + "container_image_distro": "debian 12.9", + "container_image_id": "sha256:a520752f350909c191db45a598a88fcca2fa5db17a340dee6b3d0e36f4122e11", + "kubernetes_cluster_name": "sec-eng-tools-dev", + "kubernetes_namespace_name": "defectdojo", + "kubernetes_pod_container_name": "celery", + "kubernetes_workload_name": "defectdojo-celery-beat", + "kubernetes_workload_type": "Deployment", + "scan_package_in_use": true, + "scan_package_name": "pillow", + "scan_package_path": "/path/path/path/file.abc", + "scan_package_type": "Python", + "scan_package_version": "11.2.1", + "vuln_cisa_kev_due_date": "", + "vuln_cisa_kev_known_ransomware": "", + "vuln_cisa_kev_publish_date": "", + "vuln_cvss_score": 9.8, + "vuln_cvss_vector": "CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H", + "vuln_cvss_version": "3.0", + "vuln_disclosure_date": "2025-07-01T17:29:37Z", + "vuln_fix_available": true, + "vuln_fix_available_date": "2025-07-01T09:15:58Z", + "vuln_fixed_in_version": "11.3.0", + "vuln_has_exploit": false, + "vuln_id": "CVE-2025-48379", + "vuln_risk_accepted": false, + "vuln_severity": "Critical" + }, + { + "container_image": "defectdojo/defectdojo-django:latest", + "container_image_distro": "debian 12.9", + "container_image_id": "sha256:a520752f350909c191db45a598a88fcca2fa5db17a340dee6b3d0e36f4122e11", + "kubernetes_cluster_name": "sec-eng-tools-dev", + "kubernetes_namespace_name": "defectdojo", + "kubernetes_pod_container_name": "uwsgi", + "kubernetes_workload_name": "defectdojo-django", + "kubernetes_workload_type": "Deployment", + "scan_package_in_use": false, + "scan_package_name": "libxml2", + "scan_package_path": "/path/path/file.abc", + "scan_package_type": "OS", + "scan_package_version": "2.9.14+dfsg-1.3~deb12u1", + "vuln_cisa_kev_due_date": "", + "vuln_cisa_kev_known_ransomware": "", + "vuln_cisa_kev_publish_date": "", + "vuln_cvss_score": 0, + "vuln_cvss_vector": "CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:N", + "vuln_cvss_version": "3.0", + "vuln_disclosure_date": "1999-01-01T00:00:00Z", + "vuln_fix_available": true, + "vuln_fix_available_date": "1970-01-01T00:00:00Z", + "vuln_fixed_in_version": "2.9.14+dfsg-1.3~deb12u2", + "vuln_has_exploit": false, + "vuln_id": "CVE-2023-45322", + "vuln_risk_accepted": false, + "vuln_severity": "Negligible" + } + ] + } + ] +} diff --git a/unittests/tools/test_sysdig_reports_parser.py b/unittests/tools/test_sysdig_reports_parser.py index a169ba17873..2fc9365b4e2 100644 --- a/unittests/tools/test_sysdig_reports_parser.py +++ b/unittests/tools/test_sysdig_reports_parser.py @@ -65,3 +65,46 @@ def test_sysdig_parser_json_with_many_findings(self): for endpoint in finding.unsaved_endpoints: endpoint.clean() self.assertEqual(207, len(findings)) + + # New test cases for 2025 format + def test_sysdig_parser_2025_csv_format(self): + """Test CSV parsing with new 2025 format headers (Vulnerability Name, Vulnerability Severity)""" + with (get_unit_tests_scans_path("sysdig_reports") / "sysdig-2025.csv").open(encoding="utf-8") as testfile: + parser = SysdigReportsParser() + findings = parser.get_findings(testfile, Test()) + for finding in findings: + for endpoint in finding.unsaved_endpoints: + endpoint.clean() + self.assertEqual(6, len(findings)) + + # Test specific finding details from the 2025 format + finding = findings[0] + self.assertEqual("CVE-2005-2541", finding.unsaved_vulnerability_ids[0]) + self.assertEqual("package name 1", finding.component_name) + self.assertEqual("0.0.0.1", finding.component_version) + self.assertEqual("Informational", finding.severity) # Negligible maps to Informational + + # Test a Critical severity finding + critical_findings = [f for f in findings if f.severity == "Critical"] + self.assertGreater(len(critical_findings), 0) + critical_finding = critical_findings[0] + self.assertIn("CVE-", critical_finding.unsaved_vulnerability_ids[0]) + + def test_sysdig_parser_2025_json_format(self): + """Test JSON parsing with new 2025 format that has metadata before data section""" + with (get_unit_tests_scans_path("sysdig_reports") / "sysdig-2025.json").open(encoding="utf-8") as testfile: + parser = SysdigReportsParser() + findings = parser.get_findings(testfile, Test()) + for finding in findings: + for endpoint in finding.unsaved_endpoints: + endpoint.clean() + + # Should parse successfully even with metadata before data section + self.assertGreater(len(findings), 0) + + # Test that vulnerability IDs are properly extracted + vuln_ids = [f.unsaved_vulnerability_ids[0] for f in findings if f.unsaved_vulnerability_ids] + self.assertGreater(len(vuln_ids), 0) + # Check that we have CVE IDs in the findings + cve_findings = [vid for vid in vuln_ids if vid.startswith("CVE-")] + self.assertGreater(len(cve_findings), 0)