mirror of
https://github.com/yogeshojha/rengine.git
synced 2026-10-20 15:21:30 +02:00
Repo-wide sweep of every user-facing string: frontend components, routes, config and stores; API details and route docs; shared definitions and models; stage config help; report prose; MCP, toolbox, interest and connector text; notification and worker messages. Descriptions that explained, reassured, justified or restated their title are deleted. Confirm dialogs name what is removed and stop. Failed actions read "<Noun> not <verbed>", successes "<Noun> <verbed>". No reader address, no product-as-subject, no em-dashes or parentheticals in prose. Mirrors updated on both sides: EPSS band help, compare refusal reasons, interest labels. Four tests asserting on old wording follow the new text. The rules are condensed into CLAUDE.md under Writing. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SK5o8xR96cbSnAfjYSTPSe
63 lines
2.5 KiB
Python
63 lines
2.5 KiB
Python
from __future__ import annotations
|
|
|
|
from reports.base import RenderContext, Section
|
|
from reports.config import SectionConfig, flag, limit
|
|
from shared.definitions.ports import SERVICE_CLASS_LABELS
|
|
from shared.definitions.reports import SectionGroup
|
|
from shared.definitions.surface import SurfaceDimension
|
|
|
|
|
|
class ServicesConfig(SectionConfig):
|
|
max_rows: int = limit(60, title="Rows shown", minimum=5, maximum=2000)
|
|
sensitive_first: bool = flag(True, title="List sensitive services first")
|
|
hide_web: bool = flag(False, title="Hide services that answer HTTP")
|
|
show_composition: bool = flag(True, title="Show class composition")
|
|
show_banner: bool = flag(True, title="Show software")
|
|
show_coverage: bool = flag(True, title="Show scan coverage")
|
|
|
|
|
|
class ServicesSection(Section):
|
|
name = "services"
|
|
title = "Network services"
|
|
description = "Services listening on each address."
|
|
group = SectionGroup.SURFACE.value
|
|
order = 20
|
|
launch_fields = frozenset({"max_rows"})
|
|
requires = frozenset({SurfaceDimension.SERVICES.value})
|
|
config_model = ServicesConfig
|
|
|
|
def build(self, ctx: RenderContext, cfg: ServicesConfig) -> dict | None:
|
|
rows = ctx.data.service_rows
|
|
if cfg.hide_web:
|
|
rows = [s for s in rows if not s.is_http]
|
|
if not rows:
|
|
return None
|
|
if cfg.sensitive_first:
|
|
rows = sorted(rows, key=lambda s: (not s.sensitive, s.ip, s.port))
|
|
total = len(rows)
|
|
policies: dict[str, int] = {}
|
|
for address in ctx.data.address_rows:
|
|
if address.scan_policy:
|
|
policies[address.scan_policy] = policies.get(address.scan_policy, 0) + 1
|
|
return {
|
|
"rows": rows[: cfg.max_rows],
|
|
"total": total,
|
|
"hidden": max(0, total - cfg.max_rows),
|
|
"classes": [
|
|
{
|
|
"name": f.name,
|
|
"label": SERVICE_CLASS_LABELS.get(f.name, f.name.title()),
|
|
"count": f.count,
|
|
}
|
|
for f in ctx.data.service_classes
|
|
]
|
|
if cfg.show_composition
|
|
else [],
|
|
"sensitive": len(ctx.data.sensitive_services),
|
|
"internal": ctx.data.internal_estate,
|
|
"web": sum(1 for s in ctx.data.service_rows if s.is_http),
|
|
"addresses": len({s.ip for s in ctx.data.service_rows}),
|
|
"show_banner": cfg.show_banner,
|
|
"policies": policies if cfg.show_coverage else {},
|
|
}
|