diff --git a/apps/api/src/jobs/asset_scanner_job.py b/apps/api/src/jobs/asset_scanner_job.py index 5868161a..32fb9918 100644 --- a/apps/api/src/jobs/asset_scanner_job.py +++ b/apps/api/src/jobs/asset_scanner_job.py @@ -491,6 +491,28 @@ async def _collect_all_k8s_assets() -> tuple[list[dict[str, Any]], list[dict[str return assets, relationships +def _is_valid_ipv4(s: str) -> bool: + """嚴格 IPv4 判斷: 4 段 + 每段 0-255 整數. + + 避免 '125' (短名) / 'cadvisor-110' (hostname) 被誤判為 IP. + """ + if not s or s.count(".") != 3: + return False + parts = s.split(".") + if len(parts) != 4: + return False + for p in parts: + if not p or not p.isdigit(): + return False + try: + n = int(p) + except ValueError: + return False + if n < 0 or n > 255: + return False + return True + + async def _collect_prometheus_targets() -> tuple[list[dict[str, Any]], list[dict[str, str]]]: """ 從 Prometheus /api/v1/targets 發現所有被監控的 host-install service + 主機. @@ -523,12 +545,14 @@ async def _collect_prometheus_targets() -> tuple[list[dict[str, Any]], list[dict if not instance or not job: continue - # 解析 host IP — 優先 labels.host,其次 instance 的 IP 前綴 - host_ip = labels.get("host") or "" - if not host_ip and ":" in instance: - host_ip = instance.split(":")[0] - # 只處理看起來是 IP 的 host (避免 'alertmanager' / 'argocd-server' 等 K8s DNS) - if not host_ip or not host_ip.replace(".", "").isdigit(): + # 2026-04-19 Audit 1 修: 嚴格 IPv4 判斷 + # 原 code bug: labels.host="125" (短名) 被 "125".replace(".","").isdigit()=True 誤判 IP + # 修: 優先從 instance 抽 IP (IP:port 形式或純 IP 無 port),嚴格 4 段 0-255 驗證 + # labels.host 可能是短名不可靠,只信 instance + instance_host = instance.split(":")[0] if ":" in instance else instance + host_ip = instance_host if _is_valid_ipv4(instance_host) else "" + + if not host_ip: # target instance 不是 IP 形式 → 建 third_party_service asset 但 host 留空 asset_key = f"prometheus_target/{job}/{instance}" assets.append({