Files
rengine/shared/definitions/dashboard.py
T
Yogesh OjhaandClaude Opus 5 8ffd8cc50a feat(dashboard): reachability replaces the funnel, counted as is:web
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
2026-09-16 08:45:38 +05:30

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)