From 1a3e11294685df63ae57c75c15f0e98e12601c66 Mon Sep 17 00:00:00 2001 From: waffle2k Date: Sat, 5 Sep 2026 23:24:25 -0700 Subject: [PATCH] Fix ipapi.is response parsing: handle flat string format ipapi.is free tier returns company/asn as flat strings ("Google LLC", "AS15169 Google LLC") not nested dicts. The old code assumed nested objects and crashed with AttributeError when trying to call .get() on strings. This broke IP-based signup scrutiny for every new signup. Now handles both formats (string and dict) for backward compat. Route field handling also simplified since free tier doesn't nest asn data. Fixes signup accounts falling through without IP classification. --- app/main.py | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/app/main.py b/app/main.py index b84936c..a73eb66 100644 --- a/app/main.py +++ b/app/main.py @@ -937,16 +937,29 @@ def classify_signup_ip(ip: str) -> tuple[str, str, bool, str]: log.warning("ipapi.is lookup failed for ip=%s: %s", ip, exc) bounds = _ip_range_bounds(ip) return "unknown", "", False, bounds[3] if bounds else f"{ip}/32" + # ipapi.is returns company/asn as flat strings, not nested dicts + company = data.get("company") + org_name = company if isinstance(company, str) else (company or {}).get("name", "") + + if not org_name: + asn_str = data.get("asn", "") + if isinstance(asn_str, str) and " " in asn_str: + org_name = asn_str.split(" ", 1)[1] # "AS15169 Google LLC" → "Google LLC" + + asn_obj = data.get("asn") + if isinstance(asn_obj, dict): + route = asn_obj.get("route", "") + else: + route = "" # ipapi.is free tier returns asn as string, not nested object + intel = { "is_datacenter": bool(data.get("is_datacenter")), "is_vpn": bool(data.get("is_vpn")), "is_proxy": bool(data.get("is_proxy")), "is_tor": bool(data.get("is_tor")), "is_abuser": bool(data.get("is_abuser")), - "org": ((data.get("company") or {}).get("name") - or (data.get("asn") or {}).get("org") or ""), + "org": org_name, } - route = (data.get("asn") or {}).get("route") or "" bounds = _ip_range_bounds(ip, route) intel["cidr"] = bounds[3] if bounds else f"{ip}/32" cache_ip_intel(ip, intel, route)