diff --git a/llm_team_ui.py b/llm_team_ui.py index ebeba8c..8e512ba 100644 --- a/llm_team_ui.py +++ b/llm_team_ui.py @@ -1012,6 +1012,60 @@ async function enrichIP(ip, card) { panel.appendChild(geoDiv); } + // Web-Check section (ports, blocklists) + if (d.webcheck) { + var wcDiv = document.createElement('div'); + wcDiv.style.cssText = 'margin-bottom:10px'; + var wcTitle = document.createElement('div'); + wcTitle.style.cssText = 'font-family:JetBrains Mono,monospace;font-size:8px;text-transform:uppercase;letter-spacing:2px;color:#d946ef;margin-bottom:6px;font-weight:700'; + wcTitle.textContent = 'Deep Scan (web-check)'; + wcDiv.appendChild(wcTitle); + var wcGrid = document.createElement('div'); + wcGrid.style.cssText = 'display:grid;grid-template-columns:repeat(auto-fit,minmax(140px,1fr));gap:6px'; + + // Open ports + if (d.webcheck.ports && d.webcheck.ports.openPorts) { + var ports = d.webcheck.ports.openPorts; + var pBox = document.createElement('div'); pBox.style.cssText = 'font-family:JetBrains Mono,monospace;font-size:10px'; + var pLabel = document.createElement('span'); pLabel.style.cssText = 'color:#7a7872;font-size:8px;text-transform:uppercase;letter-spacing:1px'; + pLabel.textContent = 'Open Ports: '; + var pVal = document.createElement('span'); pVal.style.cssText = 'color:#e05252;font-weight:700'; + pVal.textContent = ports.length ? ports.join(', ') : 'none found'; + pBox.appendChild(pLabel); pBox.appendChild(pVal); wcGrid.appendChild(pBox); + } + + // Blocklists + if (d.webcheck.block_lists && d.webcheck.block_lists.blocklists) { + var bls = d.webcheck.block_lists.blocklists; + var blocked = bls.filter(function(b){return b.isBlocked}); + var clean = bls.filter(function(b){return !b.isBlocked}); + var bBox = document.createElement('div'); bBox.style.cssText = 'font-family:JetBrains Mono,monospace;font-size:10px;grid-column:1/-1'; + var bLabel = document.createElement('span'); bLabel.style.cssText = 'color:#7a7872;font-size:8px;text-transform:uppercase;letter-spacing:1px'; + bLabel.textContent = 'Blocklists: '; + var bVal = document.createElement('span'); + bVal.style.cssText = 'color:' + (blocked.length ? '#e05252' : '#4ade80'); + bVal.textContent = blocked.length + ? blocked.length + '/' + bls.length + ' blocked (' + blocked.map(function(b){return b.server}).join(', ') + ')' + : 'Clean on all ' + bls.length + ' lists'; + bBox.appendChild(bLabel); bBox.appendChild(bVal); wcGrid.appendChild(bBox); + } + + // DNS + if (d.webcheck.dns) { + var dns = d.webcheck.dns; + var ptr = dns.PTR && dns.PTR.length ? dns.PTR.join(', ') : 'none'; + var dBox = document.createElement('div'); dBox.style.cssText = 'font-family:JetBrains Mono,monospace;font-size:10px;grid-column:1/-1'; + var dLabel = document.createElement('span'); dLabel.style.cssText = 'color:#7a7872;font-size:8px;text-transform:uppercase;letter-spacing:1px'; + dLabel.textContent = 'Reverse DNS: '; + var dVal = document.createElement('span'); dVal.style.color = '#e8e6e3'; + dVal.textContent = ptr; + dBox.appendChild(dLabel); dBox.appendChild(dVal); wcGrid.appendChild(dBox); + } + + wcDiv.appendChild(wcGrid); + panel.appendChild(wcDiv); + } + // AI Analysis section if (d.ai_analysis && !d.ai_analysis.error) { var ai = d.ai_analysis; @@ -4002,7 +4056,7 @@ def admin_enrich_ip(): except Exception: pass - # Step 3: AI threat analysis with full context + # Step 3: AI threat analysis with full context (including web-check data) try: geo_ctx = "" if result["geo"] and not result["geo"].get("error"): @@ -4011,11 +4065,22 @@ def admin_enrich_ip(): geo_ctx += f"ISP: {g.get('isp','?')} | Org: {g.get('org','?')} | AS: {g.get('as','?')}\n" geo_ctx += f"Proxy: {g.get('proxy',False)} | Hosting: {g.get('hosting',False)} | Mobile: {g.get('mobile',False)}\n" + # Add web-check data if available + wc_ctx = "" + if webcheck.get("ports"): + open_ports = webcheck["ports"].get("openPorts", []) + if open_ports: + wc_ctx += f"Open ports: {', '.join(str(p) for p in open_ports)}\n" + if webcheck.get("block_lists"): + blocked = [b["server"] for b in webcheck["block_lists"].get("blocklists", []) if b.get("isBlocked")] + if blocked: + wc_ctx += f"Blocked on: {', '.join(blocked[:5])}\n" + log_ctx = "\n".join(ip_logs[-20:]) if ip_logs else "No log entries found." prompt = ( f"You are a cybersecurity analyst. Provide a detailed threat assessment for IP {ip}.\n\n" - f"{geo_ctx}\n" + f"{geo_ctx}{wc_ctx}\n" f"Activity log ({len(ip_logs)} total entries, showing last 20):\n{log_ctx}\n\n" "Provide your analysis as JSON:\n" '{"threat_level": "none|low|medium|high|critical",\n' @@ -4052,6 +4117,17 @@ def admin_enrich_ip(): except Exception as e: result["ai_analysis"] = {"error": str(e)} + # Step 4: Web-Check deep scan (ports, DNS, blocklists) + WEB_CHECK_BASE = "http://localhost:3000/api" + webcheck = {} + for endpoint in ["ports", "dns", "block-lists"]: + try: + wc_resp = requests.get(f"{WEB_CHECK_BASE}/{endpoint}?url={ip}", timeout=15) + if wc_resp.status_code == 200: + webcheck[endpoint.replace("-", "_")] = wc_resp.json() + except Exception: + pass + result["webcheck"] = webcheck result["log_count"] = len(ip_logs) return jsonify(result)