add ip rep
This commit is contained in:
@@ -6,6 +6,7 @@ import os
|
||||
import time
|
||||
from pathlib import Path
|
||||
from urllib import error, request
|
||||
from urllib.parse import urlencode
|
||||
|
||||
|
||||
def is_public_ip(value: str | None) -> bool:
|
||||
@@ -20,7 +21,10 @@ def is_public_ip(value: str | None) -> bool:
|
||||
class ThreatIntelClient:
|
||||
def __init__(self, *, cache_file: str = "state/threat-intel-cache.json", ttl_seconds: int = 86400) -> None:
|
||||
self.enabled = os.getenv("FGAI_THREAT_INTEL", "").lower() in {"1", "true", "yes", "on"}
|
||||
self.abuseipdb_key = os.getenv("ABUSEIPDB_API_KEY")
|
||||
self.virustotal_key = os.getenv("VIRUSTOTAL_API_KEY")
|
||||
self.provider = os.getenv("FGAI_THREAT_INTEL_PROVIDER", "auto").lower()
|
||||
self.max_age_days = int(os.getenv("ABUSEIPDB_MAX_AGE_DAYS", "90"))
|
||||
self.cache_path = Path(cache_file)
|
||||
self.ttl_seconds = ttl_seconds
|
||||
self.cache = self._read_cache()
|
||||
@@ -40,21 +44,70 @@ class ThreatIntelClient:
|
||||
def lookup_ip(self, ip: str) -> dict[str, object]:
|
||||
if not is_public_ip(ip):
|
||||
return {"ip": ip, "provider": "local", "status": "skipped", "reason": "not_public_ip", "score": 0}
|
||||
cached = self.cache.get(ip)
|
||||
provider = self._select_provider()
|
||||
cache_key = f"{provider}:{ip}"
|
||||
cached = self.cache.get(cache_key)
|
||||
now = int(time.time())
|
||||
if cached and now - int(cached.get("cached_at", 0)) < self.ttl_seconds:
|
||||
return cached
|
||||
if not self.enabled:
|
||||
return {"ip": ip, "provider": "none", "status": "disabled", "score": 0}
|
||||
if not self.virustotal_key:
|
||||
return {"ip": ip, "provider": "virustotal", "status": "missing_api_key", "score": 0}
|
||||
if provider == "abuseipdb":
|
||||
if not self.abuseipdb_key:
|
||||
return {"ip": ip, "provider": "abuseipdb", "status": "missing_api_key", "score": 0}
|
||||
result = self._lookup_abuseipdb_ip(ip)
|
||||
elif provider == "virustotal":
|
||||
if not self.virustotal_key:
|
||||
return {"ip": ip, "provider": "virustotal", "status": "missing_api_key", "score": 0}
|
||||
result = self._lookup_virustotal_ip(ip)
|
||||
else:
|
||||
return {"ip": ip, "provider": provider, "status": "unsupported_provider", "score": 0}
|
||||
|
||||
result = self._lookup_virustotal_ip(ip)
|
||||
result["cached_at"] = now
|
||||
self.cache[ip] = result
|
||||
self.cache[cache_key] = result
|
||||
self._write_cache()
|
||||
return result
|
||||
|
||||
def _select_provider(self) -> str:
|
||||
if self.provider in {"abuseipdb", "virustotal"}:
|
||||
return self.provider
|
||||
if self.abuseipdb_key:
|
||||
return "abuseipdb"
|
||||
if self.virustotal_key:
|
||||
return "virustotal"
|
||||
return "abuseipdb"
|
||||
|
||||
def _lookup_abuseipdb_ip(self, ip: str) -> dict[str, object]:
|
||||
query = urlencode({"ipAddress": ip, "maxAgeInDays": str(self.max_age_days)})
|
||||
req = request.Request(
|
||||
f"https://api.abuseipdb.com/api/v2/check?{query}",
|
||||
headers={"Key": self.abuseipdb_key or "", "Accept": "application/json"},
|
||||
)
|
||||
try:
|
||||
with request.urlopen(req, timeout=20) as response:
|
||||
payload = json.loads(response.read().decode("utf-8"))
|
||||
except error.HTTPError as exc:
|
||||
return {"ip": ip, "provider": "abuseipdb", "status": f"http_{exc.code}", "score": 0}
|
||||
except Exception as exc:
|
||||
return {"ip": ip, "provider": "abuseipdb", "status": "error", "error": str(exc), "score": 0}
|
||||
|
||||
data = payload.get("data", {})
|
||||
score = int(data.get("abuseConfidenceScore", 0) or 0)
|
||||
return {
|
||||
"ip": ip,
|
||||
"provider": "abuseipdb",
|
||||
"status": "ok",
|
||||
"score": score,
|
||||
"abuse_confidence_score": score,
|
||||
"total_reports": int(data.get("totalReports", 0) or 0),
|
||||
"country_code": data.get("countryCode"),
|
||||
"usage_type": data.get("usageType"),
|
||||
"isp": data.get("isp"),
|
||||
"domain": data.get("domain"),
|
||||
"is_tor": bool(data.get("isTor", False)),
|
||||
"last_reported_at": data.get("lastReportedAt"),
|
||||
}
|
||||
|
||||
def _lookup_virustotal_ip(self, ip: str) -> dict[str, object]:
|
||||
req = request.Request(
|
||||
f"https://www.virustotal.com/api/v3/ip_addresses/{ip}",
|
||||
|
||||
Reference in New Issue
Block a user