forked from DefectDojo/django-DefectDojo
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Feature/parser jfrog xray binary scan (DefectDojo#9015)
* new parser Jfrog Xray on Demand Binary Scan * new parser Jfrog Xray on Demand Binary Scan * delete blank line at end of file * rename function * More sample reports * Update docs/content/en/integrations/parsers/file/jfrog_xray_on_demand_binary_scan.md Co-authored-by: Charles Neill <1749665+cneill@users.noreply.github.com> * Update docs/content/en/integrations/parsers/file/jfrog_xray_on_demand_binary_scan.md Co-authored-by: Charles Neill <1749665+cneill@users.noreply.github.com> * Update docs/content/en/integrations/parsers/file/jfrog_xray_on_demand_binary_scan.md Co-authored-by: Charles Neill <1749665+cneill@users.noreply.github.com> * Update dojo/settings/settings.dist.py Co-authored-by: Charles Neill <1749665+cneill@users.noreply.github.com> * Update dojo/settings/settings.dist.py Co-authored-by: Charles Neill <1749665+cneill@users.noreply.github.com> * Update dojo/tools/jfrog_xray_on_demand_binary_scan/parser.py Co-authored-by: Charles Neill <1749665+cneill@users.noreply.github.com> * Update dojo/tools/jfrog_xray_on_demand_binary_scan/parser.py Co-authored-by: Charles Neill <1749665+cneill@users.noreply.github.com> * Update dojo/tools/jfrog_xray_on_demand_binary_scan/parser.py Co-authored-by: Charles Neill <1749665+cneill@users.noreply.github.com> * Update dojo/tools/jfrog_xray_on_demand_binary_scan/parser.py Co-authored-by: Charles Neill <1749665+cneill@users.noreply.github.com> * Update dojo/tools/jfrog_xray_on_demand_binary_scan/parser.py Co-authored-by: Charles Neill <1749665+cneill@users.noreply.github.com> * Update dojo/tools/jfrog_xray_on_demand_binary_scan/parser.py Co-authored-by: kiblik <kiblik@gjh.sk> * Update dojo/tools/jfrog_xray_on_demand_binary_scan/parser.py Co-authored-by: kiblik <kiblik@gjh.sk> * First round of Improvements * Drop duplicates in component_id and full_path * Process per component * Visual improvements * Use+clean summary in Title, fix dedup, parse version, drop useless functions * Update dojo/tools/jfrog_xray_on_demand_binary_scan/parser.py Co-authored-by: Charles Neill <1749665+cneill@users.noreply.github.com> * Update dojo/tools/jfrog_xray_on_demand_binary_scan/parser.py Co-authored-by: kiblik <kiblik@gjh.sk> * Update dojo/tools/jfrog_xray_on_demand_binary_scan/parser.py Co-authored-by: kiblik <kiblik@gjh.sk> * Update dojo/tools/jfrog_xray_on_demand_binary_scan/parser.py Co-authored-by: kiblik <kiblik@gjh.sk> * Update dojo/tools/jfrog_xray_on_demand_binary_scan/parser.py Co-authored-by: kiblik <kiblik@gjh.sk> * Update dojo/tools/jfrog_xray_on_demand_binary_scan/parser.py Co-authored-by: kiblik <kiblik@gjh.sk> * fix test rename class * Last Improvements and tests * capitalization skills --------- Co-authored-by: Tomas Kubla <tomas@kubla.sk> Co-authored-by: Charles Neill <1749665+cneill@users.noreply.github.com> Co-authored-by: kiblik <kiblik@gjh.sk>
- Loading branch information
1 parent
35808ed
commit b42c374
Showing
10 changed files
with
9,710 additions
and
0 deletions.
There are no files selected for viewing
9 changes: 9 additions & 0 deletions
9
docs/content/en/integrations/parsers/file/jfrog_xray_on_demand_binary_scan.md
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,9 @@ | ||
--- | ||
title: "JFrog Xray On Demand Binary Scan" | ||
toc_hide: true | ||
--- | ||
Import the JSON format for the \"JFrog Xray On Demand Binary Scan\" file. Use this importer for Xray version 3.X | ||
-- | ||
JFrog file documentation: | ||
|
||
https://jfrog.com/help/r/jfrog-cli/on-demand-binary-scan |
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
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
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,190 @@ | ||
import json | ||
import re | ||
|
||
from cvss import CVSS3 | ||
|
||
from dojo.models import Finding | ||
|
||
|
||
class JFrogXrayOnDemandBinaryScanParser(object): | ||
"""jfrog_xray_scan JSON reports""" | ||
|
||
def get_scan_types(self): | ||
return ["JFrog Xray On Demand Binary Scan"] | ||
|
||
def get_label_for_scan_types(self, scan_type): | ||
return scan_type | ||
|
||
def get_description_for_scan_types(self, scan_type): | ||
return "Import Xray findings in JSON format." | ||
|
||
def get_findings(self, json_output, test): | ||
tree = json.load(json_output) | ||
return self.get_items(tree) | ||
|
||
def get_items(self, tree): | ||
items = {} | ||
for data in tree: | ||
if "vulnerabilities" in data: | ||
vulnerability_tree = data["vulnerabilities"] | ||
|
||
for node in vulnerability_tree: | ||
item_set = get_item_set(node) | ||
|
||
for item in item_set: | ||
unique_key = item.title + item.component_name + item.component_version | ||
items[unique_key] = item | ||
|
||
return list(items.values()) | ||
|
||
|
||
def get_component_name_version(name): | ||
match = re.match(r"([a-z]+://[a-z\d\.:]+):([a-z\d\.\-]+)", name, re.IGNORECASE) | ||
if match is None: | ||
return name, "" | ||
return match[1], match[2] | ||
|
||
|
||
def get_severity(vulnerability): | ||
if "severity" in vulnerability: | ||
if vulnerability["severity"] == "Unknown": | ||
severity = "Info" | ||
else: | ||
severity = vulnerability["severity"].title() | ||
else: | ||
severity = "Info" | ||
return severity | ||
|
||
|
||
def get_references(vulnerability): | ||
if "references" in vulnerability: | ||
ref = "" | ||
references = vulnerability["references"] | ||
for reference in references: | ||
if reference[:2] == "- ": | ||
ref += reference + "\n" | ||
else: | ||
ref += "- " + reference + "\n" | ||
return ref | ||
else: | ||
return None | ||
|
||
|
||
def get_remediation(extended_information): | ||
remediation = "" | ||
if "remediation" in extended_information: | ||
remediation = "\n\n**Remediation**\n" | ||
remediation += extended_information["remediation"] + "\n" | ||
return remediation | ||
|
||
|
||
def get_severity_justification(vulnerability): | ||
severity_desc = "" | ||
remediation = "" | ||
extended_information = vulnerability.get("extended_information") | ||
if extended_information: | ||
remediation += get_remediation(extended_information) | ||
if "short_description" in extended_information: | ||
severity_desc += "**Short description**\n" | ||
severity_desc += extended_information["short_description"] + "\n" | ||
if "full_description" in extended_information: | ||
severity_desc += "**Full description**\n" | ||
severity_desc += extended_information["full_description"] + "\n" | ||
if "jfrog_research_severity" in extended_information: | ||
severity_desc += "**JFrog research severity**\n" | ||
severity_desc += extended_information["jfrog_research_severity"] + "\n" | ||
if "jfrog_research_severity_reasons" in extended_information: | ||
severity_desc += "**JFrog research severity reasons**\n" | ||
for item in extended_information["jfrog_research_severity_reasons"]: | ||
severity_desc += item["name"] + "\n" if item.get("name") else "" | ||
severity_desc += item["description"] + "\n" if item.get("description") else "" | ||
severity_desc += "_Is positive:_ " + str(item["is_positive"]).lower() + "\n" if item.get("is_positive") else "" | ||
return severity_desc, remediation | ||
|
||
|
||
def process_component(component): | ||
mitigation = "" | ||
impact = "**Impact paths**\n\n- " | ||
fixed_versions = component.get("fixed_versions") | ||
if fixed_versions: | ||
mitigation = "**Versions containing a fix:**\n\n- " | ||
mitigation = mitigation + "\n- ".join(fixed_versions) | ||
if "impact_paths" in component: | ||
refs = [] | ||
impact_paths_l1 = component["impact_paths"] | ||
for impact_paths_l2 in impact_paths_l1: | ||
for item in impact_paths_l2: | ||
if "component_id" in item: | ||
refs.append(item["component_id"]) | ||
if "full_path" in item: | ||
refs.append(item["full_path"]) | ||
if refs: | ||
impact += "\n- ".join(sorted(set(refs))) # deduplication | ||
return mitigation, impact | ||
|
||
|
||
def get_cve(vulnerability): | ||
if "cves" in vulnerability: | ||
cves = vulnerability["cves"] | ||
return cves | ||
return [] | ||
|
||
|
||
def get_vuln_id_from_tool(vulnerability): | ||
if "issue_id" in vulnerability: | ||
return vulnerability["issue_id"] | ||
return None | ||
|
||
|
||
def clean_title(title): | ||
if title.startswith("Issue summary: "): | ||
title = title[len("Issue summary: "):] | ||
if '\n' in title: | ||
title = title[:title.index('\n')] | ||
return title | ||
|
||
|
||
def get_item_set(vulnerability): | ||
item_set = [] | ||
severity_justification, remediation = get_severity_justification(vulnerability) | ||
severity = get_severity(vulnerability) | ||
references = get_references(vulnerability) | ||
vuln_id_from_tool = get_vuln_id_from_tool(vulnerability) | ||
vulnerability_ids = list() | ||
cvssv3 = None | ||
cvss_v3 = "No CVSS v3 score." | ||
# Some entries have no CVE entries, despite they exist. Example CVE-2017-1000502. | ||
cves = get_cve(vulnerability) | ||
if len(cves) > 0: | ||
for item in cves: | ||
if item.get("cve"): | ||
vulnerability_ids.append(item.get("cve")) | ||
if "cvss_v3_vector" in cves[0]: | ||
cvss_v3 = cves[0]["cvss_v3_vector"] | ||
cvssv3 = CVSS3(cvss_v3).clean_vector() | ||
|
||
for component_name, component in vulnerability.get("components", {}).items(): | ||
component_name, component_version = get_component_name_version(component_name) | ||
mitigation, impact = process_component(component) | ||
|
||
title = clean_title(vulnerability["summary"]) | ||
# create the finding object | ||
finding = Finding( | ||
title=title, | ||
severity_justification=severity_justification or None, | ||
severity=severity, | ||
description=(vulnerability["summary"]).strip(), | ||
mitigation=(mitigation + remediation) or None, | ||
component_name=component_name, | ||
component_version=component_version, | ||
impact=impact or None, | ||
references=references or None, | ||
static_finding=True, | ||
dynamic_finding=False, | ||
cvssv3=cvssv3, | ||
vuln_id_from_tool=vuln_id_from_tool, | ||
) | ||
if vulnerability_ids: | ||
finding.unsaved_vulnerability_ids = vulnerability_ids | ||
item_set.append(finding) | ||
return item_set |
111 changes: 111 additions & 0 deletions
111
unittests/scans/jfrog_xray_on_demand_binary_scan/many_vulns.json
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,111 @@ | ||
[ | ||
{ | ||
"scan_id": "dd8f-4927-5db6-fb188ae8d984", | ||
"vulnerabilities": [ | ||
{ | ||
"cves": [ | ||
{ | ||
"cve": "CVE-2017-8923", | ||
"cvss_v2_score": "5.0", | ||
"cvss_v2_vector": "CVSS:2.0/AV:N/AC:L/Au:N/C:N/I:N/A:P", | ||
"cvss_v3_score": "7.5", | ||
"cvss_v3_vector": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H" | ||
} | ||
], | ||
"summary": "Summary of test", | ||
"severity": "High", | ||
"components": { | ||
"gav://org.yaml:snakeyaml:1.16": { | ||
"fixed_versions": [ | ||
"[1.26]" | ||
], | ||
"impact_paths": [ | ||
[ | ||
{ | ||
"component_id": "gav://co.com.test.com" | ||
}, | ||
{ | ||
"component_id": "gav://co.com.test.com", | ||
"full_path": "lib/snakeyaml-1.16.jar" | ||
} | ||
] | ||
] | ||
} | ||
}, | ||
"issue_id": "XRAY-92904", | ||
"references": [ | ||
"https://test.com.co" | ||
] | ||
}, | ||
{ | ||
"cves": [ | ||
{ | ||
"cve": "CVE-2014-0114", | ||
"cvss_v2_score": "7.5", | ||
"cvss_v2_vector": "CVSS:2.0/AV:N/AC:L/Au:N/C:P/I:P/A:P" | ||
} | ||
], | ||
"summary": "Summary test", | ||
"severity": "High", | ||
"components": { | ||
"gav://test": { | ||
"fixed_versions": [ | ||
"[1.9.4]" | ||
], | ||
"impact_paths": [ | ||
[ | ||
{ | ||
"component_id": "gav://co.com.test.test:core:1.0.0-test" | ||
}, | ||
{ | ||
"component_id": "gav://test", | ||
"full_path": "lib/commons-beanutils-1.9.2.jar" | ||
} | ||
] | ||
] | ||
} | ||
}, | ||
"issue_id": "XRAY-55616", | ||
"references": [ | ||
"https://test.com.co" | ||
] | ||
}, | ||
{ | ||
"cves": [ | ||
{ | ||
"cvss_v2_score": "7.5", | ||
"cvss_v2_vector": "CVSS:2.0/AV:N/AC:L/Au:N/C:P/I:P/A:P" | ||
} | ||
], | ||
"summary": "Summary test", | ||
"severity": "High", | ||
"components": { | ||
"test_item": { | ||
"fixed_versions": [ | ||
"[1.2.8.RELEASE]", | ||
"[1.3.1.RELEASE]" | ||
], | ||
"impact_paths": [ | ||
[ | ||
{ | ||
"component_id": "gav://co.com.test.test:core:1.0.0-test" | ||
}, | ||
{ | ||
"component_id": "gav://test.com.co", | ||
"full_path": "lib/test/libtest" | ||
} | ||
] | ||
] | ||
} | ||
}, | ||
"issue_id": "XRAY-79870", | ||
"references": [ | ||
"https://test.com.co" | ||
] | ||
} | ||
], | ||
"component_id": "gav://co.com.test.test:core:1.0.0-test", | ||
"package_type": "Maven", | ||
"status": "completed" | ||
} | ||
] |
Oops, something went wrong.