From d9cc61f198dbd08342681a220549e204c7d13ec2 Mon Sep 17 00:00:00 2001 From: ScottW514 Date: Tue, 22 Sep 2026 20:00:38 -0400 Subject: [PATCH] forgectrl.panel-serves: the page lets no frame navigate The panel page now carries Content-Security-Policy: frame-src 'none' (forgectrl 95970cf), the one thing that keeps a package's page from navigating its own frame to another address with what it was shown in the URL. What the header does in each browser is the frame-isolation harness's to prove (forgectrl tools/frame_isolation.py); this test holds the machine's own served page to carrying it, read with the headers from the loopback listener. The edit is inside the test's own function, so no other test's fingerprint moves. Proof: on the bench reference, image 20260922225653, PASS with forgectrl 95970cf bind-mounted ("GET / Content-Security-Policy: frame-src 'none'"), and FAIL against the image's own daemon: "the panel page lets a frame navigate: 200 None". --- forgetest/forgetest/suite/forgectrl.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/forgetest/forgetest/suite/forgectrl.py b/forgetest/forgetest/suite/forgectrl.py index d6fa2ee..ff89879 100644 --- a/forgetest/forgetest/suite/forgectrl.py +++ b/forgetest/forgetest/suite/forgectrl.py @@ -322,7 +322,12 @@ def settings_bounds(ctx): ("forgectrl", "src/cam.c"), ("forgectrl", "src/main.c"), ("forgectrl", "src/super.c"), ("grblhal-glowforge", "src/glowforge_status.c"), ("grblhal-glowforge", "src/serial.c"), ("forgectrl", "src/curverec.*")], - description="The panel page is served, /status carries the machine telemetry the panel and " + description="The panel page is served, with a policy that lets no frame navigate anywhere " + "(frame-src 'none': a package's page renders in a frame of it, and a frame that " + "could go to another address could carry what it was shown in the URL; what the " + "header does in each browser is the frame-isolation harness's to prove, and this " + "holds the machine's own page to carrying it), /status carries the machine " + "telemetry the panel and " "the acceptance tool read (including the sys block: CPU busy percent over the " "interval since the previous read, memory used percent; and homed_axes, " "the axes that carry a reference, Z alone once the lens has taken its " @@ -350,6 +355,12 @@ def panel_serves(ctx): # the head script sets and the one save bar every settings tab shares. ctx.check("data-bs-theme" in text, "the panel lacks the theme attribute (inflate failed?)") ctx.check('id="savebar"' in text, "the panel lacks the save bar") + from .setup import request + st_h, _page, hdrs = request(fc.base, "GET", "/", headers={"Host": fc.host_header()}) + ev["page_policy"] = hdrs.get("content-security-policy") + ctx.log("GET / Content-Security-Policy: %s", ev["page_policy"]) + ctx.check(st_h == 200 and "frame-src 'none'" in (ev["page_policy"] or ""), + "the panel page lets a frame navigate: %s %r", st_h, ev["page_policy"]) s = fc.status() for key in ("state", "switches", "coolant", "fans"):