mirror of
https://github.com/yogeshojha/rengine.git
synced 2026-10-20 07:11:28 +02:00
The three-column sankey's first column was always a single node, so two thirds of it was a stacked bar drawn as ribbons. On badssl.com — 15,477 web assets, one ASN — it degenerated to a 250px slab of one colour carrying one sentence, and 486 lines of layout, a linearGradient per link, an IntersectionObserver draw-in and a recursive hover walk paid for three numbers. GET /subdomains/hosting returns a HostingComposition: the fronting split, then the top MAX_NETWORKS AS orgs plus "Other networks" as ranked rows, each carrying its own fronting mix. hosting-body.svelte draws a stacked hairline with an inline legend and a ranked list whose meter is segmented by that mix, so "19 hosts in Salesforce AS14340, all direct to origin" is one glance. One body, used by hosting-panel.svelte on the scan Overview and hosting-section.svelte on the target Overview; the host passes its own padding. Rows reuse ranked-list.svelte, which gains segments, hint and a rendered meta, and the strip reuses composition-bar.svelte, which gains inline. Two count-is-a-promise breaks went with it. The service grouped on coalesce(Subdomain.asn, HttpAsset.asn) where the grammar's asn reads Subdomain.asn alone, and it read cloud off a DISTINCT ON (host) latest row where is:cloud is an EXISTS over every asset of the host, now bool_or. All 23 numbers on affirm.com equal their search/counts, checked across five scans. Unattributed hosts are a row, not a column: they appear as "Network not attributed" when some networks resolve, and when none do the list is dropped and the panel head says it instead of the network count. The dashboard widget showed two segments both reading "10,000 100%" — COUNT_CAP, with the denominator capped too. It now reads 10,000+ and drops the percentages when any count is capped. Its fronting colours come from config/hosting.ts, which the panel shares. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01B6DocJiHaZQdMhgQcWddsb
27 lines
600 B
Python
27 lines
600 B
Python
from pydantic import BaseModel, Field
|
|
|
|
|
|
class HostingSlice(BaseModel):
|
|
kind: str
|
|
label: str
|
|
count: int
|
|
query: str | None = None
|
|
|
|
|
|
class HostingNetwork(BaseModel):
|
|
id: str
|
|
label: str
|
|
detail: str | None = None
|
|
count: int
|
|
query: str | None = None
|
|
fronting: list[HostingSlice] = Field(default_factory=list)
|
|
|
|
|
|
class HostingComposition(BaseModel):
|
|
hosts: int = 0
|
|
resolving: int = 0
|
|
attributed: int = 0
|
|
networks: int = 0
|
|
fronting: list[HostingSlice] = Field(default_factory=list)
|
|
by_network: list[HostingNetwork] = Field(default_factory=list)
|