scidex/senate/cgroup_isolation.py plus the bwrap launcher
(scidex/senate/sandbox_audit.py is the in-process detector but does
not actually block) currently rely on a denylist posture for outbound
traffic — sandbox_audit logs net_connect events and the watchdog kills
runaways post-hoc. That is too late for the prompt-injection / token-
exfiltration threat model: a malicious notebook need only one
successful POST to leak an OAuth token. This task replaces the denylist
with a hard allowlist nftables ruleset bound to the sandbox UID/cgroup
and verifies it from inside the sandbox at runtime.
Effort: thorough
scidex/senate/sandbox_egress.py:build_allowlist(profile: str) -> list[str] returns the FQDNscidex/senate/sandbox_egress_profiles.yaml:default — pubmed.ncbi.nlm.nih.gov,eutils.ncbi.nlm.nih.gov, api.semanticscholar.org,api.openalex.org, clinicaltrials.gov, rest.uniprot.org,ebi.ac.uk, pypi.org (read-only), files.pythonhosted.org,github.com (read-only via 443), raw.githubusercontent.com,api.github.com.notebook — default ∪ huggingface.co, cdn-lfs.huggingface.co.none — empty list, used for pure-CPU statistical analyses.apply_nft_ruleset(profile, sandbox_uid, sandbox_cgroup) -> strnft script that drops all egress for the cgroup/etc/nftables.d/scidex-sandbox-<id>.nftnft -fs it.tear_down(sandbox_id) removes the ruleset on completion.
SCIDEX_EGRESS_REPIN_INTERVAL) for long-lived sessions.
cgroup_isolation.isolated_run:egress_profile='default' (default) / 'notebook' / 'none'apply_nft_ruleset before the bwrap exec andtear_down in the finally block.
api.evil.example); the launcher fails the run if the deny testmigrations/20260428_sandbox_egress_event.sql — newsandbox_egress_event(id BIGSERIAL, analysis_id TEXT, ts
TIMESTAMPTZ, action TEXT CHECK (action IN ('allow','deny','panic')),
remote_ip INET, remote_port INT, fqdn_pinned TEXT, raw_pkts INT).nft monitor trace background reader (best-effort —failed_safety_setup rather than running with noscidex/senate/sandbox_audit.py:emit_review_task).
tests/test_sandbox_egress.py: ruleset rendering snapshotcgroup_isolation.py and sandbox_audit.py end-to-end; locatepubmed-searchopenalex-works, etc.).
isolated_run; add the in-sandbox self-test as a tinytear_down; verify in a teardown unit test that thenft list ruleset no longer matches.
curl https://api.evil.examplesandbox_egress_event row withaction='deny'.scidex/senate/sandbox_audit.py (already shipped) — provides theq-safety-runaway-circuit-breaker — uses egress denial counts as onescidex/senate/sandbox_egress_profiles.yaml with default, notebook, and none profilesscidex/senate/sandbox_egress.py with build_allowlist, apply_nft_ruleset, apply_egress_ruleset, tear_down, egress_self_test, record_egress_event, get_recent_egress_denies, emit_egress_review_task, check_and_emit_egress_review_tasksmigrations/20260428_sandbox_egress_event.sql with sandbox_egress_event tableegress_profile kwarg into isolated_run, isolated_analysis_run, run_analysis_isolated in cgroup_isolation.py_egress_self_test_command, _build_run_command) that runs inside sandbox_build_senate_page) showing egress denies by IPtests/test_sandbox_egress.py with 30 passing tests
ip[6] cgroupv2-path to match sandbox cgroupEgressSetupError raised if nft apply fails, aborting launch&& before actual command inside sandbox/etc/nftables.d/scidex-sandbox-<id>.nft