Files
felhom.eu/documentation/audits/design-build-2026-10-06/tools/r717live.py
T

94 lines
4.5 KiB
Python

"""r717live.py — R-717 live proof on scratch 9202 (test controller 0.301.0, drill catalog with wishlist's after_setup).
1 wishlist installed fresh; the household's first account (the fixture, through the setup gate);
2 the household opens the app (POST /apps/<slug>/setup-gate/open) → after_setup's close command runs;
the DB value read (enableSignup=false) + the controller's own lines;
3 a STRANGER straight at the app (the container's address — past the address block, the strictest case):
sign-up refused; the user count before/after;
4 the household's 15-minute window (POST /apps/<slug>/signup-window) → the open command; DB = true; a family member
signs up straight at the app → success; the user count +1;
5 after the window ends (polled; ~15 min): the close command ran again; DB = false; a stranger refused; count unchanged.
Evidence: ../E/live.txt."""
import json, os, secrets, sys, time
sys.path.insert(0, "/mnt/5_hdd/felhom.eu/git/app-catalog-felhom.eu/scripts")
import box_walk as w
import upgrade_fixtures_box as fixtures
APP, SUB = "wishlist", "wishlist"
HERE = os.path.dirname(os.path.abspath(__file__))
log = open(os.path.join(HERE, "..", "E", "live.txt"), "a", buffering=1)
def say(*a):
w.say(*a); log.write(" ".join(map(str, a)) + "\n")
DBJS = r'''const {DatabaseSync}=require('node:sqlite');const db=new DatabaseSync('/usr/src/app/data/prod.db',{readOnly:true});
const v=db.prepare("select value from system_config where key='enableSignup' and groupId='global'").get();
const n=db.prepare("select count(*) as n from user").get();console.log('DB enableSignup='+(v?v.value:'<no row: default open>')+' users='+n.n);'''
def db():
return w.guest("docker exec -i wishlist node - <<'JS'\n" + DBJS + "\nJS\n").strip().splitlines()[-1:]
def stranger(tag):
"""A sign-up straight at the app's own address inside the box — past the box's address block."""
u = f"{tag}{secrets.token_hex(3)}"
body = f"name={tag}&username={u}&email={u}%40example.invalid&password=Drill-{secrets.token_hex(8)}&tokenId="
out = w.guest(f"""ip=$(docker inspect -f '{{{{range .NetworkSettings.Networks}}}}{{{{.IPAddress}}}} {{{{end}}}}' wishlist | awk '{{print $1}}')
curl -s -m 30 -H 'Host: {SUB}.{w.DOMAIN}' -H 'Origin: https://{SUB}.{w.DOMAIN}' -H 'x-sveltekit-action: true' \\
-H 'Content-Type: application/x-www-form-urlencoded' -w '\\nHTTP %{{http_code}}' --data '{body}' http://$ip:3000/signup""")
return " ".join(out.split())[-220:]
def ctl_lines(since):
return w.guest(f"docker logs --since {since} felhom-controller 2>&1 | grep -v DEBUG | grep -i -E 'after_setup|native|signup|sign-up|window' | cut -c1-300")
w.login()
if w.stack(APP).get("deployed"):
say("wishlist installed from the earlier probe — removing it first (scratch box)")
w.remove(APP)
w.sync_rescan()
time.sleep(5)
w.sync_rescan()
say("9202 controller:", w.guest("docker inspect -f '{{.Config.Image}}' felhom-controller").strip())
if not w.deploy(APP, SUB):
sys.exit(say("RESULT install failed") or 1)
fx = fixtures.FIXTURES[APP]
tok = fx.seed(w, SUB, say)
say("[1] the household's first account:", tok is not None, "| DB:", db())
t0 = w.guest("date -u +%Y-%m-%dT%H:%M:%SZ").strip()
code, d = w.ctl("POST", f"/apps/{APP}/setup-gate/open", {})
say("[2] the household opens the app (setup done) ->", code, str(d)[:160])
time.sleep(25)
say("[2] DB after the close command:", db())
log.write(ctl_lines(t0) + "\n")
say("[3] stranger, straight at the app:", stranger("stranger"))
say("[3] DB:", db())
t1 = w.guest("date -u +%Y-%m-%dT%H:%M:%SZ").strip()
code, d = w.ctl("POST", f"/apps/{APP}/signup-window", {})
say("[4] the household opens the 15-minute window ->", code, str(d)[:160])
time.sleep(25)
say("[4] DB inside the window:", db())
say("[4] a family member, straight at the app:", stranger("family"))
say("[4] DB:", db())
log.write(ctl_lines(t1) + "\n")
t2 = time.time()
while time.time() - t2 < 1500:
time.sleep(30)
v = db()
if v and "enableSignup=false" in v[0]:
say(f"[5] the switch reads CLOSED again {round(time.time() - t2)} s after the check began: {v}")
break
else:
say("[5] the switch did NOT close within 25 minutes:", db())
say("[5] stranger after the window, straight at the app:", stranger("late"))
say("[5] DB:", db())
log.write(ctl_lines(t1) + "\n")
st = w.stack(APP)
say("[5] app page record:", json.dumps({k: (st.get("app_config") or {}).get(k) for k in ("after_setup", "setup_gate")})[:600])
if not os.environ.get("KEEP"):
say("remove ->", w.remove(APP))