Skip to content

Commit 4590d4b

Browse files
authored
Merge pull request #14064 from manuel-sommer/implement_cloudflareInsights
🎉 Implement Cloudflare insights parser
2 parents 60f5f7f + e392493 commit 4590d4b

6 files changed

Lines changed: 191 additions & 0 deletions

File tree

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
---
2+
title: "Cloudflare Insights"
3+
toc_hide: true
4+
---
5+
6+
Import Cloudflare Insights findings using the **CSV export** provided by Cloudflare.
7+
8+
### Sample Scan Data
9+
Sample Cloudflare Insights files can be found [here](https://github.com/DefectDojo/django-DefectDojo/tree/master/unittests/scans/cloudflare_insights).
10+
11+
### Supported Fields
12+
The parser supports the following CSV columns:
13+
14+
- `severity`
15+
- `issue_class`
16+
- `subject`
17+
- `issue_type`
18+
- `status`
19+
- `insight` *(optional)*
20+
- `detection_method` *(optional)*
21+
- `risk` *(optional)*
22+
- `recommended_action`

dojo/tools/cloudflare_insights/__init__.py

Whitespace-only changes.
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
import csv
2+
import io
3+
from urllib.parse import urlparse
4+
5+
from dojo.models import Endpoint, Finding
6+
7+
8+
class CloudflareInsightsParser:
9+
10+
"""
11+
DefectDojo parser for Cloudflare Insights CSV exports.
12+
13+
Expected columns:
14+
- severity
15+
- issue_class
16+
- subject (used as Endpoint host; not repeated in description)
17+
- issue_type
18+
- scan_performed_on (ignored)
19+
- status
20+
- insight (optional)
21+
- detection_method (optional)
22+
- risk (optional)
23+
- recommended_action (used as mitigation if present)
24+
"""
25+
26+
def get_scan_types(self):
27+
return ["Cloudflare Insights"]
28+
29+
def get_label_for_scan_types(self, scan_type):
30+
return scan_type
31+
32+
def get_description_for_scan_types(self, scan_type):
33+
return "Import Cloudflare Insights (CSV export)."
34+
35+
def _map_severity(self, value):
36+
normalized = value.strip().lower()
37+
mapping = {
38+
"low": "Low",
39+
"moderate": "Medium",
40+
"critical": "Critical",
41+
"high": "High", # optional: Cloudflare occasionally uses this
42+
}
43+
return mapping.get(normalized, "Info")
44+
45+
def _extract_host_from_subject(self, subject: str) -> str | None:
46+
if not subject:
47+
return None
48+
s = subject.strip()
49+
if not s:
50+
return None
51+
parsed = urlparse(s)
52+
netloc = parsed.netloc
53+
if not netloc and ("." in s or ":" in s or s.startswith("localhost")):
54+
parsed2 = urlparse(f"http://{s}")
55+
netloc = parsed2.netloc
56+
host = netloc or s
57+
if ":" in host:
58+
host = host.split(":", 1)[0]
59+
host = host.strip().strip("/").strip()
60+
61+
return host or None
62+
63+
def _is_inactive_status(self, status: str) -> bool:
64+
inactive_markers = {"resolved", "mitigated", "closed", "fixed"}
65+
return bool(status) and status.strip().lower() in inactive_markers
66+
67+
def get_findings(self, filename, test):
68+
content = filename.read()
69+
if isinstance(content, bytes):
70+
content = content.decode("utf-8", errors="replace")
71+
72+
reader = csv.DictReader(
73+
io.StringIO(content),
74+
delimiter=",",
75+
quotechar='"',
76+
skipinitialspace=True,
77+
)
78+
findings = []
79+
for row in reader:
80+
severity_raw = (row.get("severity") or "").strip()
81+
issue_class = (row.get("issue_class") or "").strip()
82+
subject = (row.get("subject") or "").strip()
83+
issue_type = (row.get("issue_type") or "").strip()
84+
status = (row.get("status") or "").strip()
85+
insight = (row.get("insight") or "").strip()
86+
detection_method = (row.get("detection_method") or "").strip()
87+
risk = (row.get("risk") or "").strip()
88+
recommended_action = (row.get("recommended_action") or "").strip()
89+
mapped_severity = self._map_severity(severity_raw)
90+
if issue_type and subject:
91+
title = f"{issue_type}: {subject}"
92+
elif issue_type:
93+
title = issue_type
94+
elif subject:
95+
title = subject
96+
else:
97+
title = "Cloudflare Insight"
98+
description_parts = []
99+
if issue_class:
100+
description_parts.append(f"**Issue class**: {issue_class}")
101+
if issue_type:
102+
description_parts.append(f"**Issue type**: {issue_type}")
103+
if status:
104+
description_parts.append(f"**Status**: {status}")
105+
if insight:
106+
description_parts.append(f"**Insight**: {insight}")
107+
if detection_method:
108+
description_parts.append(f"**Detection method**: {detection_method}")
109+
if risk:
110+
description_parts.append(f"**Risk**: {risk}")
111+
description = "\n\n".join(description_parts)
112+
finding = Finding(
113+
test=test,
114+
title=title,
115+
severity=mapped_severity,
116+
description=description,
117+
mitigation=recommended_action,
118+
references="Not provided!",
119+
static_finding=False,
120+
dynamic_finding=True,
121+
)
122+
finding.active = not self._is_inactive_status(status)
123+
host = self._extract_host_from_subject(subject)
124+
if host:
125+
finding.unsaved_endpoints = [Endpoint(host=host, port=None)]
126+
findings.append(finding)
127+
128+
return findings
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
severity,issue_class,subject,issue_type,scan_performed_on,status,insight,detection_method,risk,recommended_action
2+
Moderate,Unproxied 'A' Records,domain1.com,Exposed infrastructure,2024-07-05T05:30:57.976844Z,Active,,,,"Configure Cloudflare to proxy the DNS record. By setting up Cloudflare as your hostname's reverse proxy, Cloudflare protects origin servers from DDoS attacks by hiding their IP addresses. You can configure Cloudflare to proxy your hostname in your DNS settings."
3+
Moderate,Unproxied 'A' Records,domain2.com,Exposed infrastructure,2024-07-05T05:31:39.692808Z,Active,,,,"Configure Cloudflare to proxy the DNS record. By setting up Cloudflare as your hostname's reverse proxy, Cloudflare protects origin servers from DDoS attacks by hiding their IP addresses. You can configure Cloudflare to proxy your hostname in your DNS settings."
4+
Low,Security.txt not configured,domain3.com,Configuration suggestion,2024-12-01T05:43:45.712676Z,Active,Security.txt not configured. Configure and manage the Security.txt file to improve the website's vulnerability disclosure process,We evaluated the Security Settings configured for this domain and found that Security.txt is not enabled.,"The absence of Security.txt insights creates a lack of a clear, accessible method for researchers to report vulnerabilities. This can lead to security issues going unnoticed or under-reported, increasing the risk of exploitation.","Configure Security.txt file. "
5+
Low,Security.txt not configured,domain4.com,Configuration suggestion,2024-12-01T05:43:44.252529Z,Active,Security.txt not configured. Configure and manage the Security.txt file to improve the website's vulnerability disclosure process,We evaluated the Security Settings configured for this domain and found that Security.txt is not enabled.,"The absence of Security.txt insights creates a lack of a clear, accessible method for researchers to report vulnerabilities. This can lead to security issues going unnoticed or under-reported, increasing the risk of exploitation.","Configure Security.txt file. "
6+
Moderate,Unproxied CNAME Records,domain5.com,Exposed infrastructure,2024-07-08T03:37:16.031911Z,Active,,,,"Configure Cloudflare to proxy the DNS record. By setting up Cloudflare as your hostname's reverse proxy, Cloudflare protects origin servers from DDoS attacks by hiding their IP addresses. You can configure Cloudflare to proxy your hostname in your DNS settings."
7+
Moderate,Unproxied 'A' Records,domain6.com,Exposed infrastructure,2024-07-02T12:55:57.798974Z,Active,,,,"Configure Cloudflare to proxy the DNS record. By setting up Cloudflare as your hostname's reverse proxy, Cloudflare protects origin servers from DDoS attacks by hiding their IP addresses. You can configure Cloudflare to proxy your hostname in your DNS settings."
8+
Low,Security.txt not configured,domain7.com,Configuration suggestion,2025-03-25T17:33:40.070204Z,Active,Security.txt not configured. Configure and manage the Security.txt file to improve the website's vulnerability disclosure process,We evaluated the Security Settings configured for this domain and found that Security.txt is not enabled.,"The absence of Security.txt insights creates a lack of a clear, accessible method for researchers to report vulnerabilities. This can lead to security issues going unnoticed or under-reported, increasing the risk of exploitation.","Configure Security.txt file. "
9+
Low,Security.txt not configured,domain8.com,Configuration suggestion,2025-03-25T17:33:41.970652Z,Active,Security.txt not configured. Configure and manage the Security.txt file to improve the website's vulnerability disclosure process,We evaluated the Security Settings configured for this domain and found that Security.txt is not enabled.,"The absence of Security.txt insights creates a lack of a clear, accessible method for researchers to report vulnerabilities. This can lead to security issues going unnoticed or under-reported, increasing the risk of exploitation.","Configure Security.txt file. "
10+
Moderate,Unproxied 'A' Records,domain9.com,Exposed infrastructure,2024-07-05T05:30:46.435059Z,Active,,,,"Configure Cloudflare to proxy the DNS record. By setting up Cloudflare as your hostname's reverse proxy, Cloudflare protects origin servers from DDoS attacks by hiding their IP addresses. You can configure Cloudflare to proxy your hostname in your DNS settings."
11+
Low,Security.txt not configured,domain10.com,Configuration suggestion,2024-11-29T05:32:39.671608Z,Active,Security.txt not configured. Configure and manage the Security.txt file to improve the website's vulnerability disclosure process,We evaluated the Security Settings configured for this domain and found that Security.txt is not enabled.,"The absence of Security.txt insights creates a lack of a clear, accessible method for researchers to report vulnerabilities. This can lead to security issues going unnoticed or under-reported, increasing the risk of exploitation.","Configure Security.txt file. "
12+
Low,Security.txt not configured,domain11.com,Configuration suggestion,2025-03-06T15:16:53.931468Z,Active,Security.txt not configured. Configure and manage the Security.txt file to improve the website's vulnerability disclosure process,We evaluated the Security Settings configured for this domain and found that Security.txt is not enabled.,"The absence of Security.txt insights creates a lack of a clear, accessible method for researchers to report vulnerabilities. This can lead to security issues going unnoticed or under-reported, increasing the risk of exploitation.","Configure Security.txt file. "
13+
Moderate,Unproxied CNAME Records,domain12.com,Exposed infrastructure,2026-01-02T12:29:43.13416Z,Active,Unproxied CNAME Records. This DNS record is not proxied by Cloudflare. Your origin server is directly exposed and has a higher risk of a DDoS attack.,We reviewed your Cloudflare DNS settings and checked whether your hostname accepts connections on either port 80 or 443.,DDoS Attack,"Configure Cloudflare to proxy the DNS record. By setting up Cloudflare as your hostname's reverse proxy, Cloudflare protects origin servers from DDoS attacks by hiding their IP addresses. You can configure Cloudflare to proxy your hostname in your DNS settings."
14+
Moderate,Unproxied 'A' Records,domain13.com,Exposed infrastructure,2024-07-02T12:57:30.878124Z,Active,,,,"Configure Cloudflare to proxy the DNS record. By setting up Cloudflare as your hostname's reverse proxy, Cloudflare protects origin servers from DDoS attacks by hiding their IP addresses. You can configure Cloudflare to proxy your hostname in your DNS settings."
15+
Critical,Managed Rules not deployed,domain14.com,Configuration suggestion,2024-07-01T17:44:27.896818Z,Active,Managed Rules not deployed. We have detected that you have not enabled the Cloudflare Managed Rules feature on your zone.,We evaluated your websites and you have no Managed Rules deployed.,Insufficient protection for vulnerabilities targeting Web and API applications,Turn on Managed Rules. Deploy Cloudflare Managed Rules on your zone to protect your web application against common vulnerabilities in web applications.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
severity,issue_class,subject,issue_type,scan_performed_on,status,insight,detection_method,risk,recommended_action
2+
Moderate,Unproxied 'A' Records,domain.com,Exposed infrastructure,2024-07-06T13:50:15.536086Z,Active,,,,"Configure Cloudflare to proxy the DNS record. By setting up Cloudflare as your hostname's reverse proxy, Cloudflare protects origin servers from DDoS attacks by hiding their IP addresses. You can configure Cloudflare to proxy your hostname in your DNS settings."
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
from dojo.models import Test
2+
from dojo.tools.cloudflare_insights.parser import CloudflareInsightsParser
3+
from unittests.dojo_test_case import DojoTestCase, get_unit_tests_scans_path
4+
5+
6+
class TestCloudflareInsightsParser(DojoTestCase):
7+
8+
def test_cloudflare_insights_parser_with_one_finding(self):
9+
with (get_unit_tests_scans_path("cloudflare_insights") / "one_finding.csv").open(encoding="utf-8") as testfile:
10+
parser = CloudflareInsightsParser()
11+
findings = parser.get_findings(testfile, Test())
12+
self.assertEqual(1, len(findings))
13+
finding = findings[0]
14+
self.assertEqual("Exposed infrastructure: domain.com", finding.title)
15+
self.assertEqual("Medium", finding.severity)
16+
17+
def test_cloudflare_insights_parser_with_many_findings(self):
18+
with (get_unit_tests_scans_path("cloudflare_insights") / "many_findings.csv").open(encoding="utf-8") as testfile:
19+
parser = CloudflareInsightsParser()
20+
findings = parser.get_findings(testfile, Test())
21+
self.assertEqual(14, len(findings))
22+
finding = findings[0]
23+
self.assertEqual("Exposed infrastructure: domain1.com", finding.title)
24+
self.assertEqual("Medium", finding.severity)

0 commit comments

Comments
 (0)