Expand web-check enrichment: traceroute, headers, status, full rendering
Now queries 6 web-check endpoints per IP: - ports — open port scan - dns — reverse DNS / PTR records - block-lists — DNS blocklist check (AdGuard, CloudFlare, etc.) - trace-route — full network path with per-hop latency - headers — HTTP response headers (server, powered-by, etc.) - status — HTTP status code and response time Frontend rendering: - Traceroute displayed as hop chips with latency: IP (45ms) → IP (56ms) - HTTP status with response time - Server headers inline - Errors silently skipped (many endpoints fail on raw IPs) AI analysis now includes: - Blocklist count and names in prompt - Traceroute hops in prompt for network path analysis Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
51ffd2b82c
commit
3c4846d52c
@ -1062,6 +1062,53 @@ async function enrichIP(ip, card) {
|
|||||||
dBox.appendChild(dLabel); dBox.appendChild(dVal); wcGrid.appendChild(dBox);
|
dBox.appendChild(dLabel); dBox.appendChild(dVal); wcGrid.appendChild(dBox);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Traceroute
|
||||||
|
if (d.webcheck.trace_route && d.webcheck.trace_route.result) {
|
||||||
|
var hops = d.webcheck.trace_route.result.filter(function(h){return typeof h === 'object' && h !== null});
|
||||||
|
if (hops.length) {
|
||||||
|
var trBox = document.createElement('div'); trBox.style.cssText = 'font-family:JetBrains Mono,monospace;font-size:10px;grid-column:1/-1;margin-top:4px';
|
||||||
|
var trLabel = document.createElement('span'); trLabel.style.cssText = 'color:#7a7872;font-size:8px;text-transform:uppercase;letter-spacing:1px';
|
||||||
|
trLabel.textContent = 'Traceroute (' + hops.length + ' hops): ';
|
||||||
|
trBox.appendChild(trLabel);
|
||||||
|
var trVal = document.createElement('div');
|
||||||
|
trVal.style.cssText = 'color:#e8e6e3;margin-top:4px;display:flex;flex-wrap:wrap;gap:2px;align-items:center';
|
||||||
|
hops.forEach(function(h, i) {
|
||||||
|
var hopIp = Object.keys(h)[0];
|
||||||
|
var latency = h[hopIp] ? h[hopIp][0] : '?';
|
||||||
|
var chip = document.createElement('span');
|
||||||
|
chip.style.cssText = 'background:rgba(0,0,0,0.3);border:1px solid #2a2d35;border-radius:2px;padding:2px 6px;font-size:9px;white-space:nowrap';
|
||||||
|
chip.textContent = hopIp + ' (' + (typeof latency === 'number' ? latency.toFixed(0) + 'ms' : '?') + ')';
|
||||||
|
trVal.appendChild(chip);
|
||||||
|
if (i < hops.length - 1) {
|
||||||
|
var arrow = document.createElement('span'); arrow.style.cssText = 'color:#7a7872;font-size:8px';
|
||||||
|
arrow.textContent = '→'; trVal.appendChild(arrow);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
trBox.appendChild(trVal);
|
||||||
|
wcGrid.appendChild(trBox);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// HTTP Status/Headers if available
|
||||||
|
if (d.webcheck.status && !d.webcheck.status.error) {
|
||||||
|
var st = d.webcheck.status;
|
||||||
|
var stBox = document.createElement('div'); stBox.style.cssText = 'font-family:JetBrains Mono,monospace;font-size:10px';
|
||||||
|
var stLabel = document.createElement('span'); stLabel.style.cssText = 'color:#7a7872;font-size:8px;text-transform:uppercase;letter-spacing:1px';
|
||||||
|
stLabel.textContent = 'HTTP Status: ';
|
||||||
|
var stVal = document.createElement('span'); stVal.style.color = '#e8e6e3';
|
||||||
|
stVal.textContent = st.statusCode ? st.statusCode + ' (' + (st.responseTime||'?') + 'ms)' : 'No HTTP';
|
||||||
|
stBox.appendChild(stLabel); stBox.appendChild(stVal); wcGrid.appendChild(stBox);
|
||||||
|
}
|
||||||
|
if (d.webcheck.headers && !d.webcheck.headers.error) {
|
||||||
|
var hdrs = d.webcheck.headers;
|
||||||
|
var hdrKeys = Object.keys(hdrs).filter(function(k){return typeof hdrs[k] === 'string'}).slice(0,6);
|
||||||
|
if (hdrKeys.length) {
|
||||||
|
var hBox = document.createElement('div'); hBox.style.cssText = 'font-family:JetBrains Mono,monospace;font-size:9px;grid-column:1/-1;color:#7a7872;margin-top:2px';
|
||||||
|
hBox.textContent = 'Headers: ' + hdrKeys.map(function(k){return k+': '+hdrs[k].substring(0,40)}).join(' | ');
|
||||||
|
wcGrid.appendChild(hBox);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
wcDiv.appendChild(wcGrid);
|
wcDiv.appendChild(wcGrid);
|
||||||
panel.appendChild(wcDiv);
|
panel.appendChild(wcDiv);
|
||||||
}
|
}
|
||||||
@ -4056,14 +4103,16 @@ def admin_enrich_ip():
|
|||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# Step 3: Web-Check deep scan (ports, DNS, blocklists)
|
# Step 3: Web-Check deep scan (ports, DNS, blocklists, traceroute)
|
||||||
WEB_CHECK_BASE = "http://localhost:3000/api"
|
WEB_CHECK_BASE = "http://localhost:3000/api"
|
||||||
webcheck = {}
|
webcheck = {}
|
||||||
for endpoint in ["ports", "dns", "block-lists"]:
|
for endpoint in ["ports", "dns", "block-lists", "trace-route", "headers", "status"]:
|
||||||
try:
|
try:
|
||||||
wc_resp = requests.get(f"{WEB_CHECK_BASE}/{endpoint}?url={ip}", timeout=15)
|
wc_resp = requests.get(f"{WEB_CHECK_BASE}/{endpoint}?url={ip}", timeout=20)
|
||||||
if wc_resp.status_code == 200:
|
if wc_resp.status_code == 200:
|
||||||
webcheck[endpoint.replace("-", "_")] = wc_resp.json()
|
data = wc_resp.json()
|
||||||
|
if not isinstance(data, dict) or not data.get("error"):
|
||||||
|
webcheck[endpoint.replace("-", "_")] = data
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
result["webcheck"] = webcheck
|
result["webcheck"] = webcheck
|
||||||
@ -4086,7 +4135,11 @@ def admin_enrich_ip():
|
|||||||
if webcheck.get("block_lists"):
|
if webcheck.get("block_lists"):
|
||||||
blocked = [b["server"] for b in webcheck["block_lists"].get("blocklists", []) if b.get("isBlocked")]
|
blocked = [b["server"] for b in webcheck["block_lists"].get("blocklists", []) if b.get("isBlocked")]
|
||||||
if blocked:
|
if blocked:
|
||||||
wc_ctx += f"Blocked on: {', '.join(blocked[:5])}\n"
|
wc_ctx += f"Blocked on {len(blocked)} DNS blocklists: {', '.join(blocked[:5])}\n"
|
||||||
|
if webcheck.get("trace_route") and webcheck["trace_route"].get("result"):
|
||||||
|
hops = [list(h.keys())[0] for h in webcheck["trace_route"]["result"] if isinstance(h, dict)]
|
||||||
|
if hops:
|
||||||
|
wc_ctx += f"Traceroute ({len(hops)} hops): {' → '.join(hops[:8])}\n"
|
||||||
|
|
||||||
log_ctx = "\n".join(ip_logs[-20:]) if ip_logs else "No log entries found."
|
log_ctx = "\n".join(ip_logs[-20:]) if ip_logs else "No log entries found."
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user