From 97154d12fa430484dd0dcbcd8e3bff1c8b93b101 Mon Sep 17 00:00:00 2001 From: OG T Date: Sun, 19 Apr 2026 21:46:03 +0800 Subject: [PATCH] =?UTF-8?q?fix(asset=5Fscanner):=20Gap=201=20=E4=BF=AE?= =?UTF-8?q?=E6=AD=A3=20=E2=80=94=20=E5=9A=B4=E6=A0=BC=20IPv4=20=E5=88=A4?= =?UTF-8?q?=E6=96=B7=20+=20=E6=B8=85=E7=90=86=E9=87=8D=E8=A4=87=20host=20a?= =?UTF-8?q?sset?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Audit 1 發現 bug: 原 code: if host_ip.replace('.', '').isdigit() → IP 判斷 導致 labels.host='125' (短名) 被誤當 IP → 建 host/125 (錯) 同時 blackbox-icmp instance='192.168.0.112' 無 port → split 失敗 → 漏建 新增 _is_valid_ipv4(s): 嚴格 4 段 + 每段 0-255 整數 避免短名 '125' / hostname 'cadvisor-110' / 超界 '256' 誤判 _collect_prometheus_targets 流程改: 1. 先從 instance 抽 (IP:port 形式 或純 IP) instance_host = instance.split(':')[0] if ':' in instance else instance 2. 用 _is_valid_ipv4 嚴格驗證 3. labels.host 不再當 fallback (短名不可靠) DB 清理 (266 筆): - 10 asset_relationship 指向短名 host - 140 asset_coverage_snapshot 7 維 × 4 短名 host - 112 asset_compliance_snapshot 7 維 × 4 短名 × 幾 run - 4 asset_inventory 短名 host (host/110 / 112 / 125 / 188) 預期下次 scan (1h): - host/192.168.0.112 + host/192.168.0.121 補進 (原漏,blackbox-icmp 無 port) - 不再有短名 host asset 6/6 單元測試通過: _is_valid_ipv4('192.168.0.125')=True _is_valid_ipv4('125')=False ← 關鍵修復 _is_valid_ipv4('cadvisor-110')=False _is_valid_ipv4('192.168.0.256')=False (超界) _is_valid_ipv4('')=False _is_valid_ipv4('192.168.1')=False (3 段) Co-Authored-By: Claude Opus 4.7 (1M context) --- apps/api/src/jobs/asset_scanner_job.py | 36 +++++++++++++++++++++----- 1 file changed, 30 insertions(+), 6 deletions(-) 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({