66 lines
2.5 KiB
Python
66 lines
2.5 KiB
Python
"""Expected provider domains (cloud, CDN, OS, PKI).
|
|
|
|
Used to lower severity, never to hide an observation. Extend with the
|
|
ANALYZER_BENIGN_DOMAINS environment variable (comma-separated suffixes).
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
from typing import Optional
|
|
|
|
BENIGN_SUFFIXES: set[str] = {
|
|
# Apple
|
|
"apple.com", "aaplimg.com", "icloud.com", "apple-dns.net", "cdn-apple.com",
|
|
"mzstatic.com", "itunes-apple.com", "apple.map.fastly.net", "push.apple.com",
|
|
"ls-apple.com.akadns.net", "edge.apple", "apple-cloudkit.com",
|
|
# Akamai
|
|
"akadns.net", "akamai.net", "akamaiedge.net", "akamai.com", "akamaized.net",
|
|
"akamaihd.net",
|
|
# AWS
|
|
"amazonaws.com", "cloudfront.net", "awsstatic.com", "aws.dev", "amazon.com",
|
|
# Google / Firebase
|
|
"googleapis.com", "google.com", "gstatic.com", "ggpht.com", "youtube.com",
|
|
"crashlytics.com", "firebaseio.com", "firebase.com", "app-measurement.com",
|
|
"googleusercontent.com", "google-analytics.com", "doubleclick.net", "1e100.net",
|
|
"googlevideo.com", "gvt1.com", "gvt2.com",
|
|
# Apple analytics endpoint used by many apps
|
|
"app-analytics-services.com",
|
|
# CDNs
|
|
"fastly.net", "fbcdn.net", "cloudflare.com", "cloudflare.net",
|
|
"cloudflare-dns.com", "edgekey.net", "edgesuite.net", "llnwd.net", "cdn77.org",
|
|
"azureedge.net", "aliyuncs.com", "alicdn.com",
|
|
# Microsoft
|
|
"microsoft.com", "windows.net", "azure.com", "windowsupdate.com", "office.com",
|
|
"msftncsi.com", "live.com", "msftconnecttest.com", "office365.com",
|
|
# PKI / time
|
|
"digicert.com", "sectigo.com", "letsencrypt.org", "globalsign.com", "godaddy.com",
|
|
"ntp.org", "pool.ntp.org", "time.apple.com", "time.google.com", "time.windows.com",
|
|
# Reverse-DNS / special-use
|
|
"in-addr.arpa", "ip6.arpa", "resolver.arpa", "arpa", "local", "home.arpa",
|
|
}
|
|
|
|
|
|
def _load_extra() -> set[str]:
|
|
raw = os.environ.get("ANALYZER_BENIGN_DOMAINS", "")
|
|
return {s.strip().lower().lstrip(".") for s in raw.split(",") if s.strip()}
|
|
|
|
|
|
_EXTRA = _load_extra()
|
|
|
|
|
|
def is_benign(name: Optional[str]) -> bool:
|
|
"""Whether a host name belongs to an expected provider."""
|
|
if not name:
|
|
return False
|
|
n = str(name).rstrip(".").lower()
|
|
return any(n == s or n.endswith("." + s) for s in BENIGN_SUFFIXES | _EXTRA)
|
|
|
|
|
|
def benign_name_for_ip(ctx, ip: Optional[str]) -> Optional[str]:
|
|
"""Return a benign provider name that resolved to the address, if any."""
|
|
for name in sorted(ctx.names_for_ip(ip)):
|
|
if is_benign(name):
|
|
return name
|
|
return None
|