- shots.py crops to a region (clip) or to what one or more elements cover (element, pad), per-shot viewport; capture.py writes the published sizes to src/data/screenshots.json so the page reserves the right space. - anonymize.py no longer empties secrets that netOrk compares with each other (Wi-Fi keys on an SSID against the key read from the access point). Emptying them invented passphrase "drift" that never existed; a keyed hash keeps equal equal, reverses nothing, and its key lives for one run. - scripts/check/site.py checks the built site in both languages at four widths: sideways overflow, one h1, images with alt and size, console errors, requests to other origins, links to unknown routes, old-URL redirects, language detection, and word counts against the budgets. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
63 lines
3.1 KiB
Python
63 lines
3.1 KiB
Python
"""The screenshots the website uses, as data.
|
|
|
|
Each shot is one page of the netOrk UI. `path` may contain `{placeholders}`
|
|
that are filled from `--var name=value` on the command line (device IDs
|
|
differ per instance, so they are never hard-coded here). Device detail
|
|
sections are addressed through the URL hash the UI itself writes
|
|
(`#security/assessment`, `#config`, ...), so no clicking is needed.
|
|
|
|
`mask` lists extra CSS selectors to cover on top of the automatic masks
|
|
(public IPv4 addresses, e-mail addresses, and the terms from the mask file).
|
|
"""
|
|
|
|
from dataclasses import dataclass, field
|
|
|
|
|
|
@dataclass
|
|
class Shot:
|
|
name: str
|
|
path: str
|
|
# Selector that must be visible before the shot is taken.
|
|
wait_for: str = "main"
|
|
mask: list[str] = field(default_factory=list)
|
|
full_page: bool = False
|
|
# Crop to a region (x, y, width, height in CSS px of the viewport) ...
|
|
clip: tuple[int, int, int, int] | None = None
|
|
# ... or to one element, plus `pad` px around it. Cropping shows less of a
|
|
# real screen; it never changes what is on it.
|
|
element: str | list[str] | None = None # several: crop to what they cover together
|
|
pad: int = 16
|
|
# Viewport for this shot, (width, height) in CSS px; default in capture.py.
|
|
viewport: tuple[int, int] | None = None
|
|
# Width of the published WebP in pixels (captures are taken at 2x).
|
|
width: int = 1600
|
|
# Selectors clicked in order before the shot, first match each. Only for
|
|
# controls that change the view (filters, tabs); the API guard in
|
|
# capture.py aborts anything that would write.
|
|
clicks: list[str] = field(default_factory=list)
|
|
|
|
|
|
# The drift comparison on a device page: the summary line ("… (42 compared)") down to
|
|
# the end of the table. The card around it stretches to the window height.
|
|
DRIFT_CARD = ["xpath=//*[contains(text(), 'compared)')]", "xpath=//table[.//th[contains(., 'Expected')]]"]
|
|
|
|
SHOTS: list[Shot] = [
|
|
# Home hero: an access point checked against its profile, wide and short.
|
|
Shot("drift", "/devices/{ap}#drift", wait_for="main table", element=DRIFT_CARD,
|
|
pad=28, viewport=(1440, 900), width=2400),
|
|
# Phones: the same finding from "Parameter" to the status badge, readable at 390px.
|
|
Shot("drift-narrow", "/devices/{ap}#drift", wait_for="main table",
|
|
element=["xpath=//th[contains(., 'Parameter')]", "xpath=//th[contains(., 'Actual')]",
|
|
"xpath=//tbody//td[contains(., 'Remote Syslog')]",
|
|
"xpath=//tbody//span[contains(., 'Incomplete')]",
|
|
"xpath=//tbody//*[starts-with(normalize-space(text()), 'Set this field')]"],
|
|
pad=16, viewport=(1180, 900), width=1200),
|
|
Shot("vulnerabilities", "/vulnerabilities", clip=(256, 40, 1344, 620), width=2400),
|
|
# Background polls drown out what people did: filter the scheduler out,
|
|
# the way a reader would (click a source badge, then flip it to exclude).
|
|
Shot("audit-log", "/audit-log", clip=(256, 40, 1344, 560), width=2400, clicks=[
|
|
"tbody td >> text=scheduler",
|
|
"button[title='Click to toggle include/exclude']",
|
|
]),
|
|
]
|