mirror of
https://github.com/yogeshojha/rengine.git
synced 2026-10-20 23:31:31 +02:00
695c4803bb2ab93f2dee2be2fa9430dc3afa71f4
3
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
e3b310b5b1 |
feat(cve): CVEs become a surface of their own
One evidence axis runs across both findings dimensions
(shared/definitions/evidence.py): inferred < observed < corroborated <
proven. Software rows store inferred or corroborated, set in match_scan from
a host and CVE join to non-info, non-suppressed findings; vulnerabilities
store observed or proven, proven being an out-of-band interaction recorded at
write. Both grammars gain an evidence: field and the old is:proven becomes
is:recorded. GET /cves and GET /cves/{cve} answer "am I affected by X" across
both dimensions, with an MCP tool and a toolbox lookup beside them. Ladder
counts are count(*) FILTER over the same predicates the searches compile, so
a rung opens exactly the rows it counted; tests/grammar/test_evidence.py
asserts it. match_scan carries discovered_at forward through a
software_previous temp table and returns the newly exposed delta for the
nightly notification; nvd_corpus.load skips the download when the mirror
stamp is unchanged.
The Attack surface group had grown to eight items with three of them sharing
a word. Vulnerabilities is now one entry parenting three sibling routes
joined by a tab rail: Findings, Software and CVEs. The routes are unchanged,
so vuln_q and sw_q state, deep links and per-route caching survive, and
NavItem.match keeps the parent lit on the siblings. The Software dimension
label drops to "Software" while its rows keep the noun "software CVE".
The CVE index gets the layout its siblings have: coverage strip, a search bar
attached to the card, a severity rail, a filter row with sort and refresh,
ListHeader rows and pagination. components/search-bar.svelte is the
plain-text twin of QueryBar for surfaces with no grammar. GET /cves gained
page, size, sort, dir, severity, kev, ransomware and evidence and returns
severity_counts; severity and the top rung are resolved in SQL from
SEVERITY_RANK and EVIDENCE_RANK rather than in Python.
Counts verified against the rows they open: overview.cves 1,165 equals the
index total, the Software tab 91,039 equals coverage.findings, and the
severity rail 157/373/583/52 sums to 1,165. _INDEX_TOTAL now excludes the
triage-suppressed findings the listing already excluded, and the software
dimension is counted exactly rather than capped at COUNT_CAP, so the tab
matches the page. The distinct-CVE count is 10s with JIT on and 91ms under
SET LOCAL jit = off, leaving the overview at 137ms cold and 17ms warm.
Also fixes software-table, which passed a 1-based page to ResultsPagination
where every other table passes a 0-based one, so page one read
"Showing 51-100".
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MirAGiQ2fSJJtikWi6YRNj
|
||
|
|
8f341131fc |
refactor(ui): enterprise naming for the navigation, and Interest becomes Exposures
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 |
||
|
|
9be7c1d7cc |
feat(surface): the five result dimensions become project-wide, not scan-scoped
A pentester asked how to see vulnerabilities and the answer was "open a scan". Every result surface was scoped to one run, which is a scanner's model rather than an ASM platform's. The keystone is QueryScope: the set of scans a query reads — one on a scan page, the latest covering scan of every target on a project view (a producing stage succeeded, or rows exist; the rule target_summary already used, now shared as covering_stages()). scope.match() emits `== id` for a single scan so no scan-page plan changed, and the predicates accept a UUID, a QueryScope or a sequence so single-scan callers were untouched. Because the scope generalises rather than duplicates, the same table components render both views: the grammar, facets, leads, group-by, evidence and detail sheets all follow. No results table exists twice. Scan-level facts had to become per-row: _per_scan() ORs and_(col == sid, build(sid)) so *_has_baseline — and therefore is:new — stays exact across many scans, and baselined_targets() makes the row-level is_new flag ask per target, or a first-scanned target badges everything New. Addresses are the exception: the IPs view folds one row per address across every target that serves it, so newness is judged against target_ids with the row's own first_seen as cutoff. - target: is a field in all five grammars and a group dimension in all five - /surface/[dimension] renders from SURFACE_ORDER; routeLabels take their five labels from it so nav, breadcrumb and heading cannot drift - a scope strip states how many targets the reading covers, how old the oldest is, and which targets were never scanned for that dimension, with a launch action — absent is not empty - anything needing a parent run (rescan, recheck, the endpoints Gone lens) is off project-wide, and the coverage strips drop their "on this scan" copy - the scan page's tab strip links to the same dimension across all targets, carrying the typed query Verified: every group token and every counted lead in all five dimensions returns exactly its own count in both scopes, and project-scoped is:new equals the union of the per-scan answers. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EQgatRaA9fNsv2jXh4rZ47 |