Files
rengine/shared/definitions/report_theme.py
T
Yogesh OjhaandClaude Opus 5 0b853e04b6 chore: production-readiness review across the tree
A full static review of the 3.0 tree, run as two fan-out passes with every
finding verified against the running stack rather than taken on inspection.

Correctness and security
- instance settings encrypted the mask over the stored AI key: `!= MASK` never
  matched `••••••••abcd`, so saving settings destroyed the key and `test_ai`
  posted the mask as a bearer token
- the MFA token was replayable for its full five-minute life; the gate now
  checks revocation and consumes the token after a successful exchange
- logout revoked nothing for a bearer client while answering "Logged out"
- `POST /api-keys/{id}/test` returned the plaintext key inside httpx's error
  string, which embeds the whole URL
- an interest rule could be edited or deleted through another project's id
- redis ran the celery broker under `allkeys-lru`, free to evict a queued task
  or a chord counter; now `noeviction`
- nuclei's stored command carried the OAST token and every auth header
- a finalize step could poison the session and leave a scan RUNNING for ever
- `is:issue` raised TypeError on every search
- offset paging repeated and skipped rows in four dimensions
- a cancelled run kept a stale `scans.*_found` rollup
- every PDF report raised `KeyError: 'software'`
- `version_key` truncated a wide numeric run and inverted release ordering
- psycopg raises a NUL as a bare ValueError, above StatementError, so every
  savepoint guard was blind to the case it was written for

Performance
- endpoints.host had no trigram index: 35.9ms seq scan -> 0.389ms
- free text compiled to one OR, not the prescribed UNION ALL: 132ms -> 41ms
- `/http-assets/summary` and `/ips/summary` materialised whole tables
- the cross-link `ip` kind ran an estate query per page and drew nothing

Frontend
- `$effect` loaders that read the state they write, an unbounded request loop
  on a failing endpoint
- error states that rendered as empty states
- 2FA backup codes were unusable while the copy said they worked
- a "Scan history" dialog that always reported none
- ~400 duplicated lines across five filter bars, now one component

Build and tooling
- every scanner installed at @latest; all 13 pinned to the measured versions
- CI built `web/`, gone since 2.x, with tags that cannot be valid
- the prettier pre-commit hook discarded its filenames and always passed
- the test suite destroyed itself when run twice

Also: subdomains from public code, via a pinned github-subdomains source and a
GitHub API key that subfinder receives too; sublist3r and oneforall removed
after measuring one unique host in 89 across four targets.

Verified: 853 tests, ruff clean over eleven packages, svelte-check 0 errors,
the count-is-a-promise gate at 131 checks and 0 mismatches, and 143 GET routes
with no 5xx. Four migrations, each with an exercised downgrade.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DVkdx5ynZ6aof5kLFczmhX
2026-09-13 18:03:29 +05:30

174 lines
4.6 KiB
Python

"""A report theme is data: tokens a user can write, upload and share."""
from __future__ import annotations
from enum import StrEnum
from pydantic import BaseModel, ConfigDict, Field
MAX_THEME_BYTES = 200_000
MAX_THEME_CSS = 60_000
THEME_SLUG_LENGTH = 48
class ThemeOrigin(StrEnum):
BUILTIN = "builtin"
CUSTOM = "custom"
class CoverLayout(StrEnum):
BAND = "band"
RULE = "rule"
FULL = "full"
SPLIT = "split"
MINIMAL = "minimal"
COVER_LAYOUT_LABELS: dict[str, str] = {
CoverLayout.BAND.value: "Colour band",
CoverLayout.RULE.value: "Rule and title",
CoverLayout.FULL.value: "Full bleed",
CoverLayout.SPLIT.value: "Split panel",
CoverLayout.MINIMAL.value: "Minimal",
}
class CoverArt(StrEnum):
NONE = "none"
GRID = "grid"
TOPO = "topo"
MESH = "mesh"
SCAN = "scan"
RINGS = "rings"
COVER_ART_LABELS: dict[str, str] = {
CoverArt.NONE.value: "None",
CoverArt.GRID.value: "Grid",
CoverArt.TOPO.value: "Contours",
CoverArt.MESH.value: "Mesh",
CoverArt.SCAN.value: "Scan lines",
CoverArt.RINGS.value: "Rings",
}
class TableStyle(StrEnum):
HAIRLINE = "hairline"
ZEBRA = "zebra"
BOXED = "boxed"
OPEN = "open"
TABLE_STYLE_LABELS: dict[str, str] = {
TableStyle.HAIRLINE.value: "Hairline",
TableStyle.ZEBRA.value: "Zebra",
TableStyle.BOXED.value: "Boxed",
TableStyle.OPEN.value: "Open",
}
class FindingStyle(StrEnum):
RAIL = "rail"
CARD = "card"
PLAIN = "plain"
BANNER = "banner"
FINDING_STYLE_LABELS: dict[str, str] = {
FindingStyle.RAIL.value: "Severity rail",
FindingStyle.CARD.value: "Card",
FindingStyle.PLAIN.value: "Plain",
FindingStyle.BANNER.value: "Severity banner",
}
class HeadingStyle(StrEnum):
NUMBERED = "numbered"
RULE = "rule"
PLAIN = "plain"
KICKER = "kicker"
HEADING_STYLE_LABELS: dict[str, str] = {
HeadingStyle.NUMBERED.value: "Numbered",
HeadingStyle.RULE.value: "Rule above",
HeadingStyle.PLAIN.value: "Plain",
HeadingStyle.KICKER.value: "Kicker label",
}
class ColorTokens(BaseModel):
model_config = ConfigDict(extra="ignore")
page: str = "#ffffff"
ink: str = "#16181d"
ink_soft: str = "#4a4f5a"
ink_faint: str = "#82889a"
rule: str = "#e3e5ea"
rule_strong: str = "#c8ccd4"
surface: str = "#f6f7f9"
surface_soft: str = "#fafbfc"
accent: str = "#4f46e5"
accent_soft: str = "#eef0fe"
accent_ink: str = "#ffffff"
link: str = ""
severity: dict[str, str] = Field(default_factory=dict)
chart: list[str] = Field(default_factory=list, max_length=8)
class TypeTokens(BaseModel):
model_config = ConfigDict(extra="ignore")
heading: str = "inter"
body: str = "inter"
mono: str = "jetbrains-mono"
base_size: float = Field(default=9.5, ge=6, le=16)
scale: float = Field(default=1.22, ge=1.05, le=1.5)
line_height: float = Field(default=1.55, ge=1.0, le=2.4)
heading_weight: int = Field(default=650, ge=300, le=900)
heading_tracking: float = Field(default=-0.01, ge=-0.08, le=0.2)
label_tracking: float = Field(default=0.08, ge=-0.05, le=0.4)
uppercase_labels: bool = True
numeric_tabular: bool = True
class LayoutTokens(BaseModel):
model_config = ConfigDict(extra="ignore")
rule_width: float = Field(default=0.5, ge=0.2, le=2.0)
radius: float = Field(default=3.0, ge=0, le=12)
pill: bool = True
block_gap: float = Field(default=1.0, ge=0.4, le=2.5)
table: str = TableStyle.HAIRLINE.value
finding: str = FindingStyle.RAIL.value
heading: str = HeadingStyle.NUMBERED.value
chart_stroke: float = Field(default=1.0, ge=0.4, le=3.0)
class CoverTokens(BaseModel):
model_config = ConfigDict(extra="ignore")
layout: str = CoverLayout.BAND.value
art: str = CoverArt.NONE.value
ink: str = "light"
background: str = ""
accent_bar: bool = True
class ThemeTokens(BaseModel):
"""The whole look of a document, as a file a person can write."""
model_config = ConfigDict(extra="ignore")
key: str = Field(default="", max_length=THEME_SLUG_LENGTH)
name: str = Field(default="", max_length=120)
description: str = Field(default="", max_length=400)
author: str = Field(default="", max_length=120)
version: str = Field(default="1", max_length=20)
color: ColorTokens = Field(default_factory=ColorTokens)
dark: ColorTokens | None = None
type: TypeTokens = Field(default_factory=TypeTokens)
layout: LayoutTokens = Field(default_factory=LayoutTokens)
cover: CoverTokens = Field(default_factory=CoverTokens)
css: str = Field(default="", max_length=MAX_THEME_CSS)