mirror of
https://github.com/yogeshojha/rengine.git
synced 2026-10-21 07:41:28 +02:00
The attack surface funnel is gone. The target page draws Reachability instead: one composition bar over the four states a name can be in — unresolved, resolved without an HTTP answer, answering, answering with a finding — and the dashboard's Inventory row carries the count that answered. That count is is:web, not is:live. is:live compiles to 2xx and 3xx, so a 401, 403 or 5xx responder answered and was still reported as having dropped out; the funnel's "Answer HTTP" step had been reading the wrong one, and the Inventory sub-line inherited it. Measured on this instance: 3,418 answer where 2,814 are live, so 604 web assets — the denied and error surface — were counted as not answering. DashboardOverview.answering_hosts carries it and the predicate has one definition, predicates.answered(), which the is:web flag compiles to. subdomains insights re-derived live, web and resolved inline beside the predicates module it already imports. It reads them from there. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DafWxfoWZXVDgr9KroJBDZ
85 lines
2.0 KiB
Python
85 lines
2.0 KiB
Python
"""Windows and caps for the project dashboard."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from datetime import timedelta
|
|
from enum import StrEnum
|
|
|
|
|
|
class DashboardWindow(StrEnum):
|
|
WEEK = "7d"
|
|
FORTNIGHT = "14d"
|
|
MONTH = "30d"
|
|
|
|
|
|
WINDOW_DELTAS: dict[str, timedelta] = {
|
|
DashboardWindow.WEEK.value: timedelta(days=7),
|
|
DashboardWindow.FORTNIGHT.value: timedelta(days=14),
|
|
DashboardWindow.MONTH.value: timedelta(days=30),
|
|
}
|
|
WINDOW_DAYS: dict[str, int] = {k: v.days for k, v in WINDOW_DELTAS.items()}
|
|
DEFAULT_WINDOW = DashboardWindow.WEEK.value
|
|
|
|
STALE_DAYS = 30
|
|
EXPIRING_DAYS = 30
|
|
SERIES_DAYS = 30
|
|
RUNS_PER_TARGET = 25
|
|
QUEUE_LIMIT = 40
|
|
CHANGES_LIMIT = 40
|
|
EXPOSURE_TOP = 8
|
|
DISCOVERY_LIMIT = 40
|
|
ITEMS_CAP = 100
|
|
SURFACE_RISK_ROWS = 6
|
|
|
|
EXPIRED_CERT_QUERY = "cert:expired and is:live"
|
|
EXPIRING_CERT_QUERY = "cert:expiring"
|
|
|
|
|
|
ACTIVITY_LIMIT = 40
|
|
|
|
|
|
class ActivityKind(StrEnum):
|
|
RUN = "run"
|
|
WATCH = "watch"
|
|
PROGRAM = "program"
|
|
FEEDS = "feeds"
|
|
INTEL = "intel"
|
|
CONNECTOR = "connector"
|
|
|
|
|
|
class QueueTier(StrEnum):
|
|
ACT = "act"
|
|
ATTEND = "attend"
|
|
TRACK = "track"
|
|
|
|
|
|
TIER_ORDER: tuple[str, ...] = tuple(t.value for t in QueueTier)
|
|
TIER_LABELS: dict[str, str] = {
|
|
QueueTier.ACT.value: "Act",
|
|
QueueTier.ATTEND.value: "Attend",
|
|
QueueTier.TRACK.value: "Track",
|
|
}
|
|
TIER_ACT_EPSS = 0.088
|
|
TIER_ATTEND_EPSS = 0.01
|
|
|
|
|
|
# (key, label, lower bound in days, upper bound in days); None is open
|
|
CERT_BUCKETS: tuple[tuple[str, str, int | None, int | None], ...] = (
|
|
("expired", "Expired", None, 0),
|
|
("week", "Under 7 days", 0, 7),
|
|
("month", "Under 30 days", 7, 30),
|
|
("quarter", "Under 90 days", 30, 90),
|
|
("later", "Later", 90, None),
|
|
)
|
|
|
|
|
|
def cert_bucket_query(lower: int | None, upper: int | None) -> str:
|
|
if upper == 0:
|
|
return EXPIRED_CERT_QUERY
|
|
parts = ["is:live", "not cert:expired"]
|
|
if lower:
|
|
parts.append(f"cert.expires:>={lower}d")
|
|
if upper is not None:
|
|
parts.append(f"cert.expires:<{upper}d")
|
|
return " and ".join(parts)
|