|
| 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