mirror of
https://github.com/yogeshojha/rengine.git
synced 2026-10-20 15:21:30 +02:00
The menu read as hacker cosplay rather than security software: "Worth a look" as a product feature, swords for the tool library, a sparkle for a risk surface, a crosshair for the asset list. "Worth a look" is now Exposures. Studying what the layer does settles the name: it scores an asset for what it IS — admin interface, developer tooling, remote access, non-production, legacy, no authentication, exposed content, takeover risk — and for how far it stands out from the estate. That is an exposure, the Gartner CTEM term, and it pairs with Vulnerabilities, which is what a scanner found. Bands are Critical/High/Notable, not "Look first"/"Worth a look". Only the user-visible layer is renamed. The interest/ package, the interest_signals table and InterestKind keep their names because renaming them is a migration with no user benefit. The host grammar's canonical tokens become is:exposed, exposure:<kind> and exposure_band:<band>, with the old spellings kept as aliases so no saved rule breaks — and the endpoint grammar's separate interest: (open_redirect, traversal, admin) keeps its name, so the rename removes a collision rather than creating one. Exposures also stops being a settings page. The nav item opened Rules and Dismissed while its name promised findings; it now opens the project-wide ranked list over the same QueryScope as the five dimensions, with Rules and Dismissed as tabs. Judging and rule suggestions are per-run and stay hidden there, and a project view reports stale=false so it never queues a re-label. Naming and icons elsewhere: - IPs -> IP addresses; the other four dimensions keep their names - Reconnaissance -> Discovery - the Services "Exposure" panel -> "Open services", freeing the word - swords -> library, sparkle -> scan-eye, crosshair -> target, plug-zap -> cable, network -> bot for MCP, layers -> sliders for scan contexts, and no two items in a group share a glyph Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EQgatRaA9fNsv2jXh4rZ47
43 lines
1.1 KiB
Python
43 lines
1.1 KiB
Python
import uuid
|
|
from datetime import datetime
|
|
|
|
from pydantic import BaseModel, Field
|
|
|
|
|
|
class SurfaceTargetRead(BaseModel):
|
|
target_id: uuid.UUID
|
|
target_value: str
|
|
target_type: str
|
|
scan_id: uuid.UUID | None = None
|
|
scan_status: str | None = None
|
|
observed_at: datetime | None = None
|
|
stale: bool = False
|
|
|
|
|
|
class SurfaceCoverage(BaseModel):
|
|
dimension: str
|
|
label: str
|
|
noun: str
|
|
noun_plural: str
|
|
total: int = 0
|
|
total_capped: bool = False
|
|
targets_total: int = 0
|
|
targets_covered: int = 0
|
|
observed_from: datetime | None = None
|
|
observed_to: datetime | None = None
|
|
covered: list[SurfaceTargetRead] = Field(default_factory=list)
|
|
uncovered: list[SurfaceTargetRead] = Field(default_factory=list)
|
|
|
|
@property
|
|
def stale_targets(self) -> int:
|
|
return sum(1 for row in self.covered if row.stale)
|
|
|
|
|
|
class SurfaceOverview(BaseModel):
|
|
project_id: uuid.UUID
|
|
targets_total: int = 0
|
|
live_scans: int = 0
|
|
exposures: int = 0
|
|
dimensions: list[SurfaceCoverage] = Field(default_factory=list)
|
|
generated_at: datetime
|