Files
felhom.eu/documentation/audits/catalog-sweep-2026-10-24/tools/shot.py
T
admin 67053335d1
gates / gates (push) Successful in 13m17s
Catalog sweep 2026-10-24: all 65 catalog apps on a fresh box (52 work, 10 with a problem, 0 broken); register 208 -> 217 (R-1269..R-1277 opened; notes on R-739, R-1267); STATUS + D46; map B1 re-checked; evidence
Every catalog app installed, used, backed up, removed keeping data, reinstalled, restored and removed with data on a
fresh box from the public ISO (demo-hp VM 9360, Tester4), endpoint-level. Worst: a reinstall's capture rewrites the
kept unit so its restore breaks the app (R-1269, P2), the restore still reports success (R-1270, P2), and „use my kept
data" loaded another box's future-dated database (R-1271, P2). Drill catalog fixes for the morning list (n8n usage
data; OpenGist 256M, proven by a cgroup probe). VM 9360 and LXC 9361 destroyed; hub host Tester4-aa9466 left for the
operator. REPORT-catalog-sweep-2026-10-24.md.
2026-10-25 00:13:05 +02:00

68 lines
3.6 KiB
Python
Executable File

#!/opt/pw/bin/python
"""shot.py — open a page in real Chromium (Playwright) and screenshot it, phone and computer, HU and EN.
Rehearsal 2026-10-20, A4. Runs in the throwaway LXC rehearsal1020-browser on demo-hp.
Chromium is Debian's package (pinned by the image: `chromium --version` is in A4-browser/versions.txt);
Playwright 1.52.0 from PyPI drives it via executable_path.
usage: shot.py URL OUTPREFIX [--sizes phone,desktop] [--langs hu,en] [--state FILE] [--host-header H]
[--url-env NAME] [--wait MS] [--full]
--url-env NAME read the URL (or its secret tail) from env NAME and append it to URL — so a capability
link never appears on a command line or in this script's output.
--state FILE Playwright storage_state JSON (cookies) to load — a logged-in session.
Writes OUTPREFIX-<size>-<lang>.png and prints one line per shot: status, final path (secret tails masked), title.
"""
import argparse, os, re, sys
from playwright.sync_api import sync_playwright
SIZES = {"phone": dict(width=390, height=844, is_mobile=True, device_scale_factor=2, has_touch=True),
"desktop": dict(width=1366, height=768, is_mobile=False, device_scale_factor=1, has_touch=False)}
LANGS = {"hu": "hu-HU", "en": "en-US"}
UA_PHONE = ("Mozilla/5.0 (Linux; Android 14; Pixel 8) AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/155.0.0.0 Mobile Safari/537.36")
def mask(u: str) -> str:
return re.sub(r"[0-9a-f]{32,}", "<token>", u)
def main():
ap = argparse.ArgumentParser()
ap.add_argument("url"); ap.add_argument("out")
ap.add_argument("--sizes", default="phone,desktop"); ap.add_argument("--langs", default="hu,en")
ap.add_argument("--state"); ap.add_argument("--url-env"); ap.add_argument("--wait", type=int, default=1500)
ap.add_argument("--full", action="store_true"); ap.add_argument("--ignore-https-errors", action="store_true")
a = ap.parse_args()
url = a.url + (os.environ[a.url_env] if a.url_env else "")
with sync_playwright() as p:
b = p.chromium.launch(executable_path="/usr/bin/chromium", args=["--no-sandbox"])
for s in a.sizes.split(","):
for l in a.langs.split(","):
kw = dict(viewport={"width": SIZES[s]["width"], "height": SIZES[s]["height"]},
device_scale_factor=SIZES[s]["device_scale_factor"], is_mobile=SIZES[s]["is_mobile"],
has_touch=SIZES[s]["has_touch"], locale=LANGS[l],
extra_http_headers={"Accept-Language": LANGS[l] + "," + l + ";q=0.9"},
ignore_https_errors=a.ignore_https_errors, timezone_id="Europe/Budapest")
if s == "phone":
kw["user_agent"] = UA_PHONE
if a.state:
kw["storage_state"] = a.state
ctx = b.new_context(**kw)
pg = ctx.new_page()
try:
r = pg.goto(url, wait_until="networkidle", timeout=45000)
st = r.status if r else "-"
except Exception as e: # noqa: BLE001 — a failed load is a result, recorded
st = "ERR " + type(e).__name__
pg.wait_for_timeout(a.wait)
out = f"{a.out}-{s}-{l}.png"
pg.screenshot(path=out, full_page=a.full)
hw = pg.evaluate("document.documentElement.scrollWidth > window.innerWidth")
print(f"{st}\t{s}\t{l}\t{mask(pg.url)}\t{pg.title()!r}\thscroll={hw}\t{out}")
ctx.close()
b.close()
if __name__ == "__main__":
sys.exit(main())