Bind Chromium denial receipts to executed recovery guard proof

The browser sandbox has no jq. Retain all browser control receipts and execute the actual harness guard in the offline source-proof VM with their explicit input binding, checking denied and permitted process/auth preparation. Browser context cleanup failure also remains a failed receipt.

Contract-Neutral: Existing refusal, bounded evidence and cleanup contract is unchanged; this connects its proof across the browser and source-proof runtimes.
Change-source: pulse-maintainer
This commit is contained in:
pulse-triage[bot]
2026-10-04 03:49:31 +01:00
parent 73b790b89b
commit d855d322e8
3 changed files with 71 additions and 33 deletions
@@ -477,5 +477,72 @@ class WorkflowContractTest(unittest.TestCase):
self.assertNotIn("release-lifecycle-rehearsal", path.read_text(encoding="utf-8"))
def verify_browser_recovery_controls(controls_path: Path) -> None:
"""Feed exact Chromium receipts into the real guard in a jq-equipped proof VM.
The guard's permitted process is a local sentinel, not native recovery.
Source proof binds both this source and the browser input's exact hash.
"""
controls = json.loads(controls_path.read_text(encoding="utf-8"))
assert controls["schema_version"] == 1
assert controls["scope"] == "offline adversarial observer controls only"
assert controls["client_version"] == "1.56.1"
fixed = {(kind, "upgrade") for kind in ("success", "anonymous-probes", "restart-gap",
"wrong-preview", "silent-stream", "stuck-modal")}
fixed.add(("success", "recovery"))
for endpoint in ("navigation", "authenticated-navigation", "login", "initial-version",
"authenticated-api", "check", "apply", "stream", "version", "health", "read"):
fixed.update((f"{endpoint}-{status}", "upgrade") for status in (401, 403))
results = controls["results"]
assert len(results) == len(fixed) == 29
assert {(row["fixture"], row["mode"]) for row in results} == fixed
source = HARNESS_PATH.read_text(encoding="utf-8")
function = "run_browser_journey() {" + source.split("run_browser_journey() {", 1)[1].split("\n}\n", 1)[0] + "\n}"
observed = []
for row in results:
kind, mode, receipt = row["fixture"], row["mode"], row["receipt"]
refused = re.search(r"-(401|403)$", kind) is not None
assert receipt["mode"] == mode and receipt["context_closed"] is True
assert receipt["access_refused"] is refused
assert row["requests_after_denial"] == 0
if refused:
assert receipt["status"] == "failed" and row["denials"] == 1
assert receipt["access_refusals"][0]["status"] == int(kind[-3:])
if mode != "upgrade":
continue
with tempfile.TemporaryDirectory() as tmp:
work = Path(tmp)
(work / "state").mkdir()
(work / "state/browser-upgrade.json").write_text(json.dumps(receipt), encoding="utf-8")
started = work / "process-started"
sentinel = work / "process-sentinel.sh"
sentinel.write_text("#!/bin/sh\nprintf started > " + json.dumps(str(started)) + "\n", encoding="utf-8")
sentinel.chmod(0o700)
result = subprocess.run(["bash", "-c", "set -euo pipefail\n" + function
+ "\nrun_browser_journey recovery v6.4.5\n"], capture_output=True, text=True, timeout=5,
env={"PATH": "/usr/local/bin:/usr/bin:/bin", "WORK_DIR": tmp, "ROOT_DIR": str(REPO_ROOT),
"SEED_ADMIN_USER": "synthetic", "ADMIN_PASSWORD": "synthetic", "PULSE_REHEARSAL_NODE": str(sentinel),
"PULSE_REHEARSAL_BROWSER_ORIGIN": "https://ephemeral.tawny-powan.ts.net",
"FROM_TAG": "v6.4.5", "TO_TAG": "v6.4.6-rc.1"})
assert result.returncode == (1 if refused else 0) and result.stderr == ""
assert started.exists() is (not refused)
auth_prepared = (work / "state/browser-auth.json").exists()
assert auth_prepared is (not refused)
if refused:
assert json.loads((work / "state/browser-recovery.json").read_text()) == {
"status": "not-executed", "reason": "stopped-access-no-reauthentication"}
observed.append({"fixture": kind, "guard_exit": result.returncode,
"browser_process_started": started.exists(), "auth_prepared": auth_prepared})
print(json.dumps({"scope": "actual shell guard with bound offline Chromium receipts, not native recovery",
"browser_controls": len(results), "executed_recovery_guards": len(observed), "results": observed}))
if __name__ == "__main__":
unittest.main()
if len(sys.argv) == 3 and sys.argv[1] == "--browser-controls":
try:
verify_browser_recovery_controls(Path(sys.argv[2]))
except Exception:
print("Bound browser receipts failed recovery controls; no installed proof.", file=sys.stderr)
sys.exit(1)
else:
unittest.main()
@@ -50,7 +50,7 @@ async function runJourney(browser, options) {
let closing;
const closeContext = () => closing ||= context.close().then(
() => { record.context_closed = true; },
() => { record.context_closed = false; },
() => { record.context_closed = false; record.status = 'failed'; record.failure_type = 'ContextCleanupError'; },
);
const requireAccess = () => assert.equal(record.access_refused, false,
'access refused; stop without another login, read or apply');
@@ -4,7 +4,6 @@ const assert = require('node:assert/strict');
const http = require('node:http');
const fs = require('node:fs');
const path = require('node:path');
const { spawnSync } = require('node:child_process');
const { chromium } = require('playwright');
const { validate, judge, runJourney } = require('./release-browser-journey.cjs');
const from = 'v6.4.5', to = 'v6.4.6-rc.1';
@@ -134,33 +133,6 @@ function fixture(kind) {
} };
}
function recoveryGuard(folder, refused) {
// Exercise the ACTUAL shell guard with this browser's receipt. The permitted
// branch's process is a fixed local sentinel, not installation or recovery.
const state = path.join(folder, 'state'); fs.mkdirSync(state);
fs.copyFileSync(path.join(folder, 'browser-upgrade.json'), path.join(state, 'browser-upgrade.json'));
const invocation = path.join(folder, 'guard-process-started');
const sentinel = path.join(folder, 'process-sentinel.sh');
fs.writeFileSync(sentinel, `#!/bin/sh\nprintf started > ${JSON.stringify(invocation)}\n`, { mode: 0o700 });
const harness = fs.readFileSync(path.resolve(__dirname, '../../../scripts/release_lifecycle_rehearsal.sh'), 'utf8');
const body = 'run_browser_journey() {' + harness.split('run_browser_journey() {')[1].split('\n}\n')[0] + '\n}';
const result = spawnSync('bash', ['-c', `set -euo pipefail\n${body}\nrun_browser_journey recovery v6.4.5\n`], {
env: { PATH: '/usr/local/bin:/usr/bin:/bin', WORK_DIR: folder, ROOT_DIR: path.resolve(__dirname, '../../..'),
SEED_ADMIN_USER: 'synthetic', ADMIN_PASSWORD: 'synthetic', PULSE_REHEARSAL_NODE: sentinel,
PULSE_REHEARSAL_BROWSER_ORIGIN: 'https://ephemeral.tawny-powan.ts.net', FROM_TAG: from, TO_TAG: to },
encoding: 'utf8', timeout: 5000,
});
assert.equal(result.error, undefined, 'shell guard must execute');
assert.equal(result.stderr, '', 'no raw error/receipt contents');
assert.equal(result.status, refused ? 1 : 0, 'actual recovery guard verdict');
assert.equal(fs.existsSync(invocation), !refused, 'no later browser process after refusal');
assert.equal(fs.existsSync(path.join(state, 'browser-auth.json')), !refused, 'no later auth preparation after refusal');
if (refused) assert.deepEqual(JSON.parse(fs.readFileSync(path.join(state, 'browser-recovery.json'))),
{ status: 'not-executed', reason: 'stopped-access-no-reauthentication' });
else fs.writeFileSync(path.join(state, 'browser-auth.json'), '');
return { executed_guard: true, exit: result.status, browser_process_started: !refused, auth_prepared: !refused };
}
async function runControls(outputDir) {
pureControls();
const browser = await chromium.launch({ headless: true, channel: 'chromium', args: ['--no-sandbox'] });
@@ -211,11 +183,10 @@ async function runControls(outputDir) {
if (kind.startsWith('read-')) assert.equal(receipt.protected_read_status, Number(kind.slice(-3)));
if (kind === 'anonymous-probes') assert.equal(counts.anonymous_probes, 2, 'normal pre-login denial controls ran');
if (kind === 'restart-gap') assert.equal(counts.transport_gaps, 1, 'restart transport control ran');
const recovery = mode === 'upgrade' ? recoveryGuard(folder, refusal) : null;
results.push({ fixture: kind, mode, expected_rejection: !expectedPass, observed_status: receipt.status,
active_step: receipt.active_step, ...counts, recovery_guard: recovery, receipt });
active_step: receipt.active_step, ...counts, receipt });
}
fs.writeFileSync(path.join(outputDir, 'observer-controls.json'), JSON.stringify({ scope: 'offline adversarial observer controls only',
fs.writeFileSync(path.join(outputDir, 'observer-controls.json'), JSON.stringify({ schema_version: 1, scope: 'offline adversarial observer controls only',
browser_version: browser.version(), client_version: require('playwright/package.json').version, results }, null, 2) + '\n');
console.log(JSON.stringify({ observer_controls: results.length, scope: 'fixtures, not native qualification', results }));
} finally { await browser.close(); }