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
Adds HUNT (Sniper/Trend/Forge), CFO dashboard (burn rate + kill list), and a plugin-based Analyze side panel with caching and SSRF hardening.
103 lines
4.2 KiB
Python
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)]
|
|
|