mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-10-24 20:52:51 +00:00
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:
@@ -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(); }
|
||||
|
||||
Reference in New Issue
Block a user