Skip to content

Commit 6f046ba

Browse files
manuel-sommervalentijnscholtenMaffooch
authored
🎉 Add Xeol parser #12816 (#12846)
* 🎉 Add Xeol parser #12816 * fix * update * Update docs/content/en/connecting_your_tools/parsers/file/xeol.md Co-authored-by: valentijnscholten <valentijnscholten@gmail.com> * update * fix * update * ruff * udpate * Ruff Stuff --------- Co-authored-by: valentijnscholten <valentijnscholten@gmail.com> Co-authored-by: Cody Maffucci <46459665+Maffooch@users.noreply.github.com>
1 parent 3771266 commit 6f046ba

7 files changed

Lines changed: 6121 additions & 0 deletions

File tree

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
---
2+
title: "Xeol Parser"
3+
toc_hide: true
4+
---
5+
Import JSON reports of Xeolscans.
6+
7+
### Parser
8+
You can find the parser [here](https://github.com/xeol-io/xeol).
9+
10+
### Severity
11+
The severity of a EOL detected findings is as follows:
12+
- Critical: The component is already 8 weeks end of life
13+
- High: The component is already 6 weeks end of life
14+
- Medium: The component is already 4 weeks end of life
15+
- Low: The component is already 2 weeks end of life
16+
- Info: The component is not yet end of life, but was included in the Xeol report
17+
18+
### Sample Scan Data
19+
Sample kube-bench Scanner scans can be found [here](https://github.com/DefectDojo/django-DefectDojo/tree/master/unittests/scans/xeol).

dojo/tools/xeol/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
__author__ = "manuel-sommer"

dojo/tools/xeol/parser.py

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
import json
2+
from datetime import datetime, timedelta
3+
4+
from dojo.models import Finding
5+
6+
7+
class XeolParser:
8+
def get_scan_types(self):
9+
return ["Xeol Parser"]
10+
11+
def get_label_for_scan_types(self, scan_type):
12+
return "Xeol Parser"
13+
14+
def get_description_for_scan_types(self, scan_type):
15+
return "Import JSON report"
16+
17+
def get_findings(self, file, test):
18+
findings = []
19+
data = json.load(file)
20+
21+
if not isinstance(data, dict) or "matches" not in data:
22+
return findings
23+
24+
distro = data.get("distro", {})
25+
for match in data["matches"]:
26+
cycle = match.get("Cycle", {})
27+
artifact = match.get("artifact", {})
28+
29+
title = f"{cycle.get('ProductName', 'Unknown Product')} EOL Information"
30+
31+
description_lines = [
32+
f"**Product Name:** {cycle.get('ProductName', 'N/A')}",
33+
f"**Release Cycle:** {cycle.get('ReleaseCycle', 'N/A')}",
34+
f"**EOL Date:** {cycle.get('Eol', 'N/A')}",
35+
f"**Latest Release Date:** {cycle.get('LatestReleaseDate', 'N/A')}",
36+
f"**Release Date:** {cycle.get('ReleaseDate', 'N/A')}",
37+
f"**Artifact Name:** {artifact.get('name', 'N/A')}",
38+
f"**Artifact Version:** {artifact.get('version', 'N/A')}",
39+
f"**Artifact Type:** {artifact.get('type', 'N/A')}",
40+
f"**Licenses:** {', '.join(artifact.get('licenses', [])) if artifact.get('licenses') else 'N/A'}",
41+
f"**Package URL:** {artifact.get('purl', 'N/A')}",
42+
f"**CPEs:** {', '.join(artifact.get('cpes', [])) if artifact.get('cpes') else 'N/A'}",
43+
f"**Distro Name:** {distro.get('name', 'N/A')}",
44+
f"**Distro Version:** {distro.get('version', 'N/A')}",
45+
]
46+
47+
locations = artifact.get("locations", [])
48+
if locations:
49+
location_info = [
50+
f"Path: {loc.get('path', '')}, LayerID: {loc.get('layerID', '')}"
51+
for loc in locations
52+
]
53+
description_lines.append("**Locations:**\n" + "\n".join(location_info))
54+
55+
metadata = artifact.get("metadata", {})
56+
if isinstance(metadata, dict) and "files" in metadata:
57+
file_paths = [f.get("path", "") for f in metadata["files"] if "path" in f]
58+
if file_paths:
59+
description_lines.append("**Files:**\n" + "\n".join(file_paths))
60+
61+
description = "\n".join(description_lines)
62+
63+
# Determine severity based on EOL date
64+
severity = "Info"
65+
eol_str = cycle.get("Eol", "")
66+
try:
67+
eol_date = datetime.strptime(eol_str, "%Y-%m-%d")
68+
now = datetime.now()
69+
if eol_date < now:
70+
delta = now - eol_date
71+
if delta <= timedelta(weeks=2):
72+
severity = "Low"
73+
elif delta <= timedelta(weeks=4):
74+
severity = "Medium"
75+
elif delta <= timedelta(weeks=6):
76+
severity = "High"
77+
else:
78+
severity = "Critical"
79+
except Exception:
80+
severity = "Info"
81+
82+
finding = Finding(
83+
title=title,
84+
test=test,
85+
severity=severity,
86+
description=description,
87+
component_name=artifact.get("name", ""),
88+
component_version=artifact.get("version", ""),
89+
static_finding=True,
90+
dynamic_finding=False,
91+
nb_occurences=1,
92+
cwe=672,
93+
references=cycle.get("ProductPermalink", "") + "\n[www.xeol.io/explorer](https://www.xeol.io/explorer)",
94+
)
95+
96+
findings.append(finding)
97+
98+
return findings

0 commit comments

Comments
 (0)