Yves Gugger 3485668b5e
Some checks failed
CI / Frontend Lint & Type Check (push) Has been cancelled
CI / Frontend Build (push) Has been cancelled
CI / Backend Lint (push) Has been cancelled
CI / Backend Tests (push) Has been cancelled
CI / Docker Build (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
Deploy / Build & Push Images (push) Has been cancelled
Deploy / Deploy to Server (push) Has been cancelled
Deploy / Notify (push) Has been cancelled
feat: add Alpha Terminal HUNT/CFO modules and Analyze framework
Adds HUNT (Sniper/Trend/Forge), CFO dashboard (burn rate + kill list), and a plugin-based Analyze side panel with caching and SSRF hardening.
2025-12-15 16:15:58 +01:00

103 lines
4.2 KiB
Python

from __future__ import annotations
from app.schemas.analyze import AnalyzeItem
from app.services.analyze.base import AnalyzerContribution, AnalyzeContext
from app.services.domain_health import get_health_checker
class BasicRiskAnalyzer:
key = "basic_risk"
ttl_seconds = 60 * 10 # 10m (HTTP/SSL/DNS can change quickly)
async def analyze(self, ctx: AnalyzeContext) -> list[AnalyzerContribution]:
if ctx.fast:
return [
AnalyzerContribution(
quadrant="risk",
items=[
AnalyzeItem(
key="risk_skipped_fast_mode",
label="Risk Signals",
value=None,
status="na",
source="internal",
details={"reason": "Fast mode enabled: skip HTTP/SSL checks."},
)
],
)
]
health = ctx.health
if health is None:
health = await get_health_checker().check_domain(ctx.domain)
# health object has attributes; keep access defensive
score = int(getattr(health, "score", 0) or 0)
status = getattr(getattr(health, "status", None), "value", None) or str(getattr(health, "status", "unknown"))
signals = getattr(health, "signals", []) or []
http = getattr(health, "http", None)
ssl = getattr(health, "ssl", None)
dns = getattr(health, "dns", None)
http_status_code = getattr(http, "status_code", None) if http else None
http_reachable = bool(getattr(http, "is_reachable", False)) if http else False
http_parked = bool(getattr(http, "is_parked", False)) if http else False
redirect_url = getattr(http, "redirect_url", None) if http else None
parking_signals = getattr(http, "parking_signals", []) if http else []
http_error = getattr(http, "error", None) if http else None
ssl_has = bool(getattr(ssl, "has_ssl", False)) if ssl else False
ssl_valid = bool(getattr(ssl, "is_valid", False)) if ssl else False
ssl_days = getattr(ssl, "days_until_expiry", None) if ssl else None
ssl_issuer = getattr(ssl, "issuer", None) if ssl else None
ssl_error = getattr(ssl, "error", None) if ssl else None
dns_has_ns = bool(getattr(dns, "has_nameservers", False)) if dns else False
dns_has_a = bool(getattr(dns, "has_a_record", False)) if dns else False
dns_parking_ns = bool(getattr(dns, "is_parking_ns", False)) if dns else False
items = [
AnalyzeItem(
key="health_score",
label="Health Score",
value=score,
status="pass" if score >= 80 else "warn" if score >= 50 else "fail",
source="internal",
details={"status": status, "signals": signals},
),
AnalyzeItem(
key="dns_infra",
label="DNS Infra",
value={"has_ns": dns_has_ns, "has_a": dns_has_a},
status="pass" if (dns_has_ns and dns_has_a and not dns_parking_ns) else "warn",
source="dns",
details={"parking_ns": dns_parking_ns},
),
AnalyzeItem(
key="http",
label="HTTP",
value=http_status_code,
status="pass" if http_reachable and (http_status_code or 0) < 400 else "warn",
source="http",
details={
"reachable": http_reachable,
"is_parked": http_parked,
"redirect_url": redirect_url,
"parking_signals": parking_signals,
"error": http_error,
},
),
AnalyzeItem(
key="ssl",
label="SSL",
value=ssl_days if ssl_has else None,
status="pass" if ssl_has and ssl_valid else "warn",
source="ssl",
details={"has_certificate": ssl_has, "is_valid": ssl_valid, "issuer": ssl_issuer, "error": ssl_error},
),
]
return [AnalyzerContribution(quadrant="risk", items=items)]