Files
felhom.eu/documentation/audits/rehearsal-2026-10-20/tools/bind.py
T
admin f84f94d987 Rehearsal 2026-10-20 (Tester4) + data-loss hunt: baselines, B1 install evidence, A6 drawn order, tools, first two reviewers' candidates
No release, deploy or publish. Off-site is OFF for Tester4 and the owner passphrase is needed from the operator (bind blocked after 2 of 5 tries).
2026-10-20 11:57:55 +02:00

46 lines
2.4 KiB
Python

#!/opt/pw/bin/python
"""bind.py — the household's self-bind page, in real Chromium (rehearsal 2026-10-20, B2).
Env (handed in by brun.sh, never on a command line): BIND_TOKEN, PAIRING, PASS.
--typo : change ONE character of PASS before sending (a household's typo; the value is never printed)
Screenshots are taken BEFORE the fields are filled and AFTER the answer page loads — the passphrase field
is type=text, so a filled-form screenshot would show it. Prints the answer page's visible text.
usage: bind.py OUTPREFIX [--typo] [--size phone|desktop] [--lang hu|en] [--get-only]
"""
import os, re, sys
from playwright.sync_api import sync_playwright
out = sys.argv[1]; args = sys.argv[2:]
size = args[args.index("--size") + 1] if "--size" in args else "phone"
lang = args[args.index("--lang") + 1] if "--lang" in args else "hu"
vp = {"phone": dict(viewport={"width": 390, "height": 844}, device_scale_factor=2, is_mobile=True, has_touch=True),
"desktop": dict(viewport={"width": 1366, "height": 768})}[size]
loc = {"hu": "hu-HU", "en": "en-US"}[lang]
url = "https://hub.felhom.eu/bind/" + os.environ["BIND_TOKEN"]
with sync_playwright() as p:
b = p.chromium.launch(executable_path="/usr/bin/chromium", args=["--no-sandbox"])
ctx = b.new_context(locale=loc, extra_http_headers={"Accept-Language": loc}, timezone_id="Europe/Budapest", **vp)
pg = ctx.new_page()
r = pg.goto(url, wait_until="networkidle")
pg.screenshot(path=f"{out}-1-open-{size}-{lang}.png", full_page=True)
print("GET", r.status, pg.title())
body = pg.inner_text("body")
print(re.sub(r"\s+", " ", body)[:600])
if "--get-only" in args or not pg.locator("#pairing_code").count():
b.close(); sys.exit(0)
pw = os.environ["PASS"]
if "--typo" in args: # swap one inner letter for its neighbour in the alphabet
i = len(pw) // 2
while not pw[i].isalpha():
i += 1
pw = pw[:i] + ("b" if pw[i] != "b" else "c") + pw[i + 1:]
pg.fill("#pairing_code", os.environ["PAIRING"])
pg.fill("#passphrase", pw)
with pg.expect_navigation(wait_until="networkidle"):
pg.click("button[type=submit]")
# blank both inputs before the screenshot, in case a re-rendered form echoes a value
pg.evaluate("document.querySelectorAll('input').forEach(e => e.value = '')")
pg.screenshot(path=f"{out}-2-answer-{size}-{lang}.png", full_page=True)
print("POST answer:", re.sub(r"\s+", " ", pg.inner_text("body"))[:600])
b.close()