added cloudflare detection / badge on results page

This commit is contained in:
2025-09-03 10:11:47 -05:00
parent b59bf67329
commit d5cc9df699
3 changed files with 12 additions and 21 deletions

View File

@@ -2,18 +2,6 @@
updated: "2025-08-22" updated: "2025-08-22"
roadmap: roadmap:
- id: "p1-analysis-cloudflare"
priority: 1
title: "Cloudflare Detection"
goal: "Detect Cloudflare usage and badge it, with explanation of dual-use (security vs. abuse)."
tags: ["analysis"]
milestone: null
details:
- "Detection signals: DNS (CNAME to Cloudflare, AS13335), HTTP headers (cf-ray, cf-cache-status), IP ranges, and challenge pages."
- "UI: add badge + tooltip with a short explainer about legitimate protection vs. abuse evasion."
- "Edge cases: 'grey-clouded' DNS entries, partial proxy (only some records), and CDN in front of non-HTTP services."
- "Acceptance: correctly identifies Cloudflare on known test hosts and avoids false positives on non-CF CDNs."
- id: "p1-analysis-total-score" - id: "p1-analysis-total-score"
priority: 1 priority: 1
title: "Total Score" title: "Total Score"
@@ -26,15 +14,6 @@ roadmap:
- "Explainability: always show a breakdown and contribution per component; include a 'Why?' link in the UI." - "Explainability: always show a breakdown and contribution per component; include a 'Why?' link in the UI."
- "Calibration: start with heuristic weights, then calibrate on a test set; store weights in settings.yaml." - "Calibration: start with heuristic weights, then calibrate on a test set; store weights in settings.yaml."
- id: "p1-modal-close-fast"
priority: 1
title: "Analyze modal closes too fast"
goal: "have it wait until page reload"
tags: ["ui"]
milestone: null
details:
- "UX: user sees modal removed too quickly and thinks something broke"
- id: "p2-ui-rules-lab" - id: "p2-ui-rules-lab"
priority: 2 priority: 2
title: "Rules Lab" title: "Rules Lab"

View File

@@ -37,6 +37,9 @@
{{ ip }} - {{ ip }} -
{% if info.country %} {{ info.country }} {% endif %} - {% if info.country %} {{ info.country }} {% endif %} -
{% if info.isp %} {{ info.isp }} {% endif %} {% if info.isp %} {{ info.isp }} {% endif %}
{% if info.cloudflare %}
<span class="badge badge-warn">Cloudflare </span>
{% endif %}
</summary> </summary>
<div class="px-3 pb-3 overflow-x-auto"> <div class="px-3 pb-3 overflow-x-auto">

View File

@@ -336,6 +336,9 @@ def enrich_whois(hostname: str) -> dict:
def enrich_geoip(hostname: str) -> dict: def enrich_geoip(hostname: str) -> dict:
"""Resolve hostname to IPs and fetch info from ip-api.com.""" """Resolve hostname to IPs and fetch info from ip-api.com."""
CLOUDFLARE_ASN = "AS13335 Cloudflare"
geo_info = {} geo_info = {}
ips = extract_ips_from_url(hostname) ips = extract_ips_from_url(hostname)
for ip in ips: for ip in ips:
@@ -352,6 +355,12 @@ def enrich_geoip(hostname: str) -> dict:
resp = requests.get(f"http://ip-api.com/json/{ip_str}?fields=24313855", timeout=5) resp = requests.get(f"http://ip-api.com/json/{ip_str}?fields=24313855", timeout=5)
if resp.status_code == 200: if resp.status_code == 200:
geo_info[ip_str] = resp.json() geo_info[ip_str] = resp.json()
asname = geo_info[ip_str].get("as")
# if behind cloudflare
if CLOUDFLARE_ASN in asname:
geo_info[ip_str].update({"cloudflare":True})
else: else:
geo_info[ip_str] = {"error": f"HTTP {resp.status_code}"} geo_info[ip_str] = {"error": f"HTTP {resp.status_code}"}
except Exception as e: except Exception as e: