-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implementation of a CSV parser for Sysdig Vulnerability reports based…
… around the 'new' engine. (#8868) Covers Pipeline, Registry and Runtime reports
- Loading branch information
1 parent
ad65ca2
commit 4ed3fc1
Showing
12 changed files
with
426 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
--- | ||
title: "Sysdig Vulnerability Reports" | ||
toc_hide: true | ||
--- | ||
Import CSV report files from Sysdig. | ||
Parser will accept Pipeline, Registry and Runtime reports created from the UI | ||
|
||
More information available at [our reporting docs page](https://docs.sysdig.com/en/docs/sysdig-secure/vulnerabilities/reporting) |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,160 @@ | ||
from dojo.models import Finding | ||
from dojo.tools.sysdig_reports.sysdig_csv_parser import CSVParser | ||
|
||
from cvss.cvss3 import CVSS3 | ||
import cvss.parser | ||
|
||
|
||
class SysdigReportsParser(object): | ||
""" | ||
Sysdig Report Importer - Runtime CSV | ||
""" | ||
|
||
def get_scan_types(self): | ||
return ["Sysdig Vulnerability Report - Pipeline, Registry and Runtime (CSV)"] | ||
|
||
def get_label_for_scan_types(self, scan_type): | ||
return "Sysdig Vulnerability Report Scan" | ||
|
||
def get_description_for_scan_types(self, scan_type): | ||
return "Import of Sysdig Pipeline, Registry and Runtime Vulnerability Report Scans in CSV format." | ||
|
||
def get_findings(self, filename, test): | ||
|
||
if filename is None: | ||
return () | ||
|
||
if filename.name.lower().endswith('.csv'): | ||
arr_data = CSVParser().parse(filename=filename) | ||
else: | ||
return () | ||
|
||
if len(arr_data) == 0: | ||
return () | ||
sysdig_report_findings = [] | ||
|
||
for row in arr_data: | ||
finding = Finding(test=test) | ||
|
||
# Generate finding | ||
if row.k8s_cluster_name != "": | ||
finding.title = f"{row.k8s_cluster_name} - {row.k8s_namespace_name} - {row.package_name} - {row.vulnerability_id}" | ||
else: | ||
finding.title = f"{row.vulnerability_id} - {row.package_name}" | ||
|
||
finding.vuln_id_from_tool = row.vulnerability_id | ||
finding.cve = row.vulnerability_id | ||
finding.severity = row.severity | ||
|
||
# Set Component Version | ||
finding.component_name = row.package_name | ||
finding.component_version = row.package_version | ||
|
||
# Set some finding tags | ||
tags = [] | ||
|
||
if row.k8s_cluster_name != "": | ||
tags.append("Cluster: " + row.k8s_cluster_name) | ||
if row.k8s_namespace_name != "": | ||
tags.append("Namespace: " + row.k8s_namespace_name) | ||
if row.k8s_workload_name != "": | ||
tags.append("WorkloadName: " + row.k8s_workload_name) | ||
if row.package_name != "": | ||
tags.append("PackageName: " + row.package_name) | ||
if row.package_version != "": | ||
tags.append("PackageVersion: " + row.package_version) | ||
if row.k8s_cluster_name != "": | ||
tags.append("InUse: " + str(row.in_use)) | ||
if row.vulnerability_id != "": | ||
tags.append("VulnId: " + row.vulnerability_id) | ||
finding.tags = tags | ||
|
||
if row.k8s_cluster_name != "": | ||
finding.dynamic_finding = True | ||
finding.static_finding = False | ||
finding.description += f"###Runtime Context {row.k8s_cluster_name}" f"\n - **Cluster:** {row.k8s_cluster_name}" | ||
finding.description += f"\n - **Namespace:** {row.k8s_namespace_name}" | ||
finding.description += f"\n - **Workload Name:** {row.k8s_workload_name} " | ||
finding.description += f"\n - **Workload Type:** {row.k8s_workload_type} " | ||
finding.description += f"\n - **Container Name:** {row.k8s_container_name}" | ||
else: | ||
finding.dynamic_finding = False | ||
finding.static_finding = True | ||
|
||
if row.cloud_provider_name != "" or row.cloud_provider_name != "" or row.cloud_provider_region != "": | ||
finding.description += "\n\n###Cloud Details" | ||
if row.cloud_provider_name != "": | ||
finding.description += f"\n - **Cloud Provider Name:** {row.cloud_provider_name}" | ||
if row.cloud_provider_account_id != "": | ||
finding.description += f"\n - **Cloud Provider Account Id:** {row.cloud_provider_account_id}" | ||
if row.cloud_provider_region != "": | ||
finding.description += f"\n - **Cloud Provider Region:** {row.cloud_provider_region}" | ||
|
||
if row.registry_name != "" or row.registry_image_repository != "" or row.registry_vendor != "": | ||
finding.description += "\n\n###Registry Details" | ||
if row.registry_name != "": | ||
finding.description += f"\n - **Registry Name:** {row.registry_name}" | ||
if row.registry_image_repository != "": | ||
finding.description += f"\n - **Registry Image Repository:** {row.registry_image_repository}" | ||
if row.registry_vendor != "": | ||
finding.description += f"\n - **Registry Vendor:** {row.registry_vendor}" | ||
|
||
finding.description += "\n\n###Vulnerability Details" | ||
finding.description += f"\n - **Vulnerability ID:** {row.vulnerability_id}" | ||
finding.description += f"\n - **Vulnerability Link:** {row.vuln_link}" | ||
finding.description += f"\n - **Severity:** {row.severity}" | ||
finding.description += f"\n - **Publish Date:** {row.vuln_publish_date}" | ||
finding.description += f"\n - **CVSS Version:** {row.cvss_version}" | ||
finding.description += f"\n - **CVSS Vector:** {row.cvss_vector}" | ||
if row.public_exploit != '': | ||
finding.description += f"\n - **Public Exploit:** {row.public_exploit}" | ||
|
||
finding.description += "\n\n###Package Details" | ||
if row.package_type == "os": | ||
finding.description += f"\n - **Package Type: {row.package_type} \\* Consider upgrading your Base OS \\***" | ||
else: | ||
finding.description += f"\n - **Package Type:** {row.package_type}" | ||
finding.description += f"\n - **Package Name:** {row.package_name}" | ||
finding.description += f"\n - **Package Version:** {row.package_version}" | ||
finding.description += f"\n - **In-Use:** {row.in_use}" | ||
|
||
if row.package_path != '': | ||
finding.description += f"\n - **Package Path:** {row.package_path}" | ||
finding.file_path = row.package_path | ||
if row.package_suggested_fix != '': | ||
finding.mitigation = f"Package suggested fix version: {row.package_suggested_fix}" | ||
finding.description += f"\n - **Package suggested fix version:** {row.package_suggested_fix}" | ||
if row.package_type == "os": | ||
finding.mitigation += "\n\\*** Consider upgrading your Base OS \\***" | ||
|
||
finding.description += "\n\n###Image Details" | ||
finding.description += f"\n - **Image Name:** {row.image}" | ||
finding.description += f"\n - **Image OS:** {row.os_name}" | ||
finding.description += f"\n - **Image ID:** {row.image_id}" | ||
|
||
# If we have registry information | ||
if row.registry_name != "": | ||
finding.description += f"\n - **Registry Name:** {row.registry_name}" | ||
finding.description += f"\n - **Registy Image Repository:** {row.registry_image_repository}" | ||
|
||
try: | ||
if float(row.cvss_version) >= 3: | ||
finding.cvssv3_score = row.cvss_score | ||
vectors = cvss.parser.parse_cvss_from_text(row.cvss_vector) | ||
if len(vectors) > 0 and isinstance(vectors[0], CVSS3): | ||
finding.cvss = vectors[0].clean_vector() | ||
|
||
except ValueError: | ||
continue | ||
|
||
finding.risk_accepted = row.risk_accepted | ||
|
||
# Set reference | ||
if row.vuln_link != "": | ||
finding.references = row.vuln_link | ||
finding.url = row.vuln_link | ||
|
||
# finally, Add finding to list | ||
sysdig_report_findings.append(finding) | ||
|
||
return sysdig_report_findings |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
import csv | ||
import io | ||
from dojo.tools.sysdig_reports.sysdig_data import SysdigData | ||
|
||
|
||
class CSVParser: | ||
""" | ||
Sysdig CSV Data Parser | ||
""" | ||
|
||
def parse(self, filename) -> SysdigData: | ||
|
||
if filename is None: | ||
return () | ||
|
||
content = filename.read() | ||
if type(content) is bytes: | ||
content = content.decode('utf-8') | ||
reader = csv.DictReader(io.StringIO(content), delimiter=',', quotechar='"') | ||
|
||
# normalise on lower case for consistency | ||
reader.fieldnames = [name.lower() for name in reader.fieldnames] | ||
|
||
csvarray = [] | ||
|
||
for row in reader: | ||
# Compare headers to values. | ||
if len(row) != len(reader.fieldnames): | ||
raise ValueError(f"Number of fields in row ({len(row)}) does not match number of headers ({len(reader.fieldnames)})") | ||
|
||
# Check for a CVE value to being with | ||
if not row[reader.fieldnames[0]].startswith("CVE"): | ||
raise ValueError(f"Expected 'CVE' at the start but got: {row[reader.fieldnames[0]]}") | ||
|
||
csvarray.append(row) | ||
|
||
arr_csv_data = [] | ||
|
||
for row in csvarray: | ||
|
||
csv_data_record = SysdigData() | ||
|
||
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.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', '') | ||
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', '') | ||
csv_data_record.vuln_link = row.get('vuln link', '') | ||
csv_data_record.vuln_publish_date = row.get('vuln publish date', '') | ||
csv_data_record.vuln_fix_date = row.get('vuln fix 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('k8s cluster name', '') | ||
csv_data_record.k8s_namespace_name = row.get('k8s namespace name', '') | ||
csv_data_record.k8s_workload_type = row.get('k8s workload type', '') | ||
csv_data_record.k8s_workload_name = row.get('k8s workload name', '') | ||
csv_data_record.k8s_container_name = row.get('k8s container name', '') | ||
csv_data_record.image_id = row.get('image id', '') | ||
csv_data_record.k8s_pod_count = row.get('k8s pod count', '') | ||
csv_data_record.package_suggested_fix = row.get('package suggested fix', '') | ||
csv_data_record.in_use = row.get('in use', '') == 'TRUE' | ||
csv_data_record.risk_accepted = row.get('risk accepted', '') == 'TRUE' | ||
csv_data_record.registry_name = row.get('registry name', '') | ||
csv_data_record.registry_image_repository = row.get('registry image repository', '') | ||
csv_data_record.cloud_provider_name = row.get('cloud provider name', '') | ||
csv_data_record.cloud_provider_account_id = row.get('cloud provider account ID', '') | ||
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) | ||
|
||
return arr_csv_data |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
import datetime | ||
|
||
|
||
class SysdigData: | ||
|
||
def _map_severity(self, severity): | ||
severity_mapping = { | ||
"CRITICAL": "Critical", | ||
"HIGH": "High", | ||
"MEDIUM": "Medium", | ||
"LOW": "Low", | ||
"NEGLIGIBLE": "Informational" | ||
} | ||
|
||
return severity_mapping.get(severity, "Informational") | ||
|
||
""" | ||
Data class to represent the Sysdig data extracted from sources like CSV or JSON. | ||
""" | ||
def __init__(self): | ||
self.vulnerability_id: str = "" | ||
self.url: str = "" | ||
self.severity: str = "" | ||
self.package_name: str = "" | ||
self.package_version: str = "" | ||
self.package_type: str = "" | ||
self.package_path: str = "" | ||
self.image: str = "" | ||
self.os_name: str = "" | ||
self.cvss_version: float = 0 | ||
self.cvss_score: float = 0 | ||
self.cvss_vector: str = "" | ||
self.vuln_link: str = "" | ||
self.vuln_publish_date: str = "" | ||
self.vuln_fix_date: datetime.date = None | ||
self.vuln_fix_version: str = "" | ||
self.public_exploit: str = "" | ||
self.k8s_cluster_name: str = "" | ||
self.k8s_namespace_name: str = "" | ||
self.k8s_workload_type: str = "" | ||
self.k8s_workload_name: str = "" | ||
self.k8s_container_name: str = "" | ||
self.image_id: str = "" | ||
self.k8s_pod_count: str = 0 | ||
self.in_use: bool = False | ||
self.risk_accepted: bool = False | ||
self.publish_date: datetime.date = None | ||
self.component_version: str = "" | ||
self.package_suggested_fix: str = "" | ||
self.image_type: str = "" | ||
self.registry_name: str = "" | ||
self.registry_image_repository: str = "" | ||
self.registry_vendor: str = "" | ||
self.cloud_provider_name: str = "" | ||
self.cloud_provider_account_id: str = "" | ||
self.cloud_provider_region: str = "" |
2 changes: 2 additions & 0 deletions
2
unittests/scans/sysdig_reports/sysdig_reports_empty_with_error.csv
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
Vulnerability ID,Severity,Package name,Package version,Package type,Package path,Image,OS Name,CVSS version,CVSS score,CVSS vector,Vuln link,Vuln Publish date,Vuln Fix date,Fix version,Public Exploit,Registry name,Registry image repository,Image ID,Package suggested fix,Risk accepted | ||
High,github.com/opencontainers/runc,v1.1.0,golang,/usr/local/bin/gosu,mongo,ubuntu 22.04,3.1,7.8,CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H,https://nvd.nist.gov/vuln/detail/CVE-2022-29162,2022-05-05,2022-05-12,v1.1.2,false,kubernetes,sock-shop,deployment,carts-db,carts-db,sha256:ee3b4d1239f12b094c4936dd08a2fbc227300beaf784c46c509e2f1ac5e6d879,1,v1.1.5,false,false |
Oops, something went wrong.