-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Feature/parser jfrog xray binary scan #9015
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
mtesauro
merged 40 commits into
DefectDojo:dev
from
bancolombia:feature/parser_jfrog_xray_binary_scan
Dec 2, 2023
Merged
Changes from all commits
Commits
Show all changes
40 commits
Select commit
Hold shift + click to select a range
4770db2
new parser Jfrog Xray on Demand Binary Scan
renejal 7c645ac
new parser Jfrog Xray on Demand Binary Scan
renejal c477ed3
Merge branch 'feature/parser_jfrog_xray_binary_scan' of https://githu…
renejal 77572a4
Merge branch 'feature/parser_jfrog_xray_binary_scan' of https://githu…
renejal 4eefc39
Merge branch 'feature/parser_jfrog_xray_binary_scan' of https://githu…
renejal 5364a6a
Merge branch 'feature/parser_jfrog_xray_binary_scan' of https://githu…
renejal 259a808
Merge branch 'feature/parser_jfrog_xray_binary_scan' of https://githu…
renejal 0dff403
delete blank line at end of file
renejal 83aefa5
rename function
renejal 7781ef8
More sample reports
kiblik 9591890
Update docs/content/en/integrations/parsers/file/jfrog_xray_on_demand…
renejal 5c10366
Update docs/content/en/integrations/parsers/file/jfrog_xray_on_demand…
renejal 7a139f3
Update docs/content/en/integrations/parsers/file/jfrog_xray_on_demand…
renejal 2f01430
Update dojo/settings/settings.dist.py
renejal 3ba1a15
Update dojo/settings/settings.dist.py
renejal 8b7c29c
Update dojo/tools/jfrog_xray_on_demand_binary_scan/parser.py
renejal 8bd9320
Update dojo/tools/jfrog_xray_on_demand_binary_scan/parser.py
renejal 165084a
Update dojo/tools/jfrog_xray_on_demand_binary_scan/parser.py
renejal f9419a9
Update dojo/tools/jfrog_xray_on_demand_binary_scan/parser.py
renejal adde828
Update dojo/tools/jfrog_xray_on_demand_binary_scan/parser.py
renejal c186480
Update dojo/tools/jfrog_xray_on_demand_binary_scan/parser.py
renejal 56f1ec9
Update dojo/tools/jfrog_xray_on_demand_binary_scan/parser.py
renejal 1b8d10f
Merge pull request #80 from kiblik/feature/parser_jfrog_xray_binary_s…
renejal 2471106
First round of Improvements
kiblik b257771
Drop duplicates in component_id and full_path
kiblik 0dc2ea7
Process per component
kiblik 241e29d
Visual improvements
kiblik ce10533
Use+clean summary in Title, fix dedup, parse version, drop useless fu…
kiblik bcb0ecc
Update dojo/tools/jfrog_xray_on_demand_binary_scan/parser.py
renejal 7e03894
Update dojo/tools/jfrog_xray_on_demand_binary_scan/parser.py
renejal 57c2a83
Update dojo/tools/jfrog_xray_on_demand_binary_scan/parser.py
renejal 65ab485
Update dojo/tools/jfrog_xray_on_demand_binary_scan/parser.py
renejal f49f0d1
Update dojo/tools/jfrog_xray_on_demand_binary_scan/parser.py
renejal 73a4ba3
Update dojo/tools/jfrog_xray_on_demand_binary_scan/parser.py
renejal 26fce7a
Merge branch 'feature/parser_jfrog_xray_binary_scan' into jfrog_more_…
renejal 3c5a58c
Merge pull request #81 from kiblik/jfrog_more_tests
renejal c3347af
fix test rename class
renejal 23b3087
Last Improvements and tests
kiblik 9cb5893
Merge pull request #82 from kiblik/jfrog_more_tests
renejal ccae048
capitalization skills
renejal File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 hidden or 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 hidden or 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 hidden or 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 hidden or 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 "" | ||
|
renejal marked this conversation as resolved.
|
||
| 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 hidden or 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", | ||
|
renejal marked this conversation as resolved.
|
||
| "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.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.