Skip to content

fix: resolve 3 critical security findings (sandbox RCE, unauth console, Rust panic fail-safe)#3

Merged
New1Direction merged 3 commits into
rust-core-loadbearingfrom
fix/critical-security-3
Jun 1, 2026
Merged

fix: resolve 3 critical security findings (sandbox RCE, unauth console, Rust panic fail-safe)#3
New1Direction merged 3 commits into
rust-core-loadbearingfrom
fix/critical-security-3

Conversation

@New1Direction
Copy link
Copy Markdown
Owner

Fixes the three critical findings from the full audit (pi-platform-audit-report.html). Each was reproduced, fixed test-first (TDD), and verified.

What's fixed

🔴 Sandbox escape → RCE (demonstrated)

The extension "sandbox" ran untrusted code in-process via exec() with a restricted __builtins__ — not a security boundary; it was escapable to full RCE (read /etc/passwd, run shell). The static inspector was a name-blocklist that rated the escape 100/100 safe.

  • sandbox.py now fails closed: no in-process exec(). Execution is opt-in (PI_EXTENSION_ALLOW_CODE_EXECUTION=1 / allow_execution=True) and runs in an isolated subprocess (python -I) with a stripped env (no inherited secrets), RLIMIT_AS, and a hard parent-side wall-clock kill.
  • inspector.py _check_indirect_access rejects the reflective/dunder escape pivots; wired into the governor's Phase-1 scan.
  • ⚠️ The subprocess is a real improvement but not a full OS jail (no seccomp/network isolation) — run under gVisor/a locked-down container in prod.

🔴 Unauthenticated console

The ledger + transparency endpoints served every tenant's execution audit data with no auth by default.

  • New auth_guard.require_reader gates those routers fail-closed (401 unless a valid principal is present); explicit local-dev opt-out PI_CONSOLE_ALLOW_UNAUTHENTICATED=1.
  • ⚠️ Follow-up (not in this PR): per-row tenant scoping needs a schema migration (execution_trace has no tenant_id) + RBAC on these routes.

🔴 Rust panic defeats the "fail-safe" fallback

A Rust panic crossed PyO3 as PanicException (a BaseException), which the except Exception fallback couldn't catch — and the Rust core is default-on.

  • pi_agents::run_agent_safe catches panics → Err (→ a normal PyValueError); pi-py routes both entry points through it; consensus.py broadened to catch BaseException (re-raising KeyboardInterrupt/SystemExit).

Verification

  • Python (full CI suite — unit/integration/conformance/console/ledger/pipeline): 1256 passed, 0 failed
  • Rust: pi-agents 782 + pi-event-fabric 13 passed (incl. 3 new panic-safety tests)
  • ruff check + ruff format --check: clean on all changed files
  • 15 new security tests; each was RED before / GREEN after the fix

Notes

  • Base is rust-core-loadbearing (where this work was branched from).
  • Behaviour change: admitting an extension now requires the execution opt-in (intended — fail closed).

🤖 Generated with Claude Code

PI Platform and others added 3 commits June 1, 2026 12:13
…rect-access escapes

The extension sandbox ran untrusted code in-process via exec() with a restricted
__builtins__ — a non-boundary, escapable to full RCE (demonstrated: reads /etc/passwd,
runs shell). The static inspector was a name-blocklist that rated the escape safe.

- sandbox.py: remove in-process exec entirely. execute() now FAILS CLOSED (REJECTED)
  unless execution is explicitly enabled (PI_EXTENSION_ALLOW_CODE_EXECUTION=1 or
  allow_execution=True); when enabled it runs in an isolated subprocess (python -I)
  with a stripped env (no inherited secrets), RLIMIT_AS, SIGALRM + a hard parent-side
  wall-clock kill. (Not a full OS jail — still recommend seccomp/gVisor in prod.)
- inspector.py: add _check_indirect_access — reject reflective/dunder escape pivots
  (__subclasses__/__mro__/__globals__/__builtins__/_module/__import__/globals/vars/…).
- governor.py: wire the new check into the Phase-1 source scan.
- tests: new test_sandbox_security.py (escape rejected & not executed, env stripped);
  governor + catalog tests move execution behind the opt-in flag.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The ledger and transparency endpoints served every tenant's execution audit data
with NO authentication by default (JWT was opt-in; the shipped config left it off).

- auth_guard.py: new require_reader dependency — refuses access unless the request
  carries a valid principal (JWT validated by the existing middleware). Fails closed
  when auth is unconfigured, with an explicit local-dev opt-out
  (PI_CONSOLE_ALLOW_UNAUTHENTICATED=1).
- main.py: apply require_reader to the ledger + transparency routers.
- tests: new test_auth_gate.py (default 401 / valid-token 200 / opt-out); the
  transparency integration test uses the documented dev opt-out.

FOLLOW-UP (not in this commit): per-row tenant scoping needs a schema migration —
execution_trace has no tenant_id column — plus RBAC on these routes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…tches them

A Rust panic crossed the PyO3 boundary as PanicException (a BaseException subclass),
which the orchestrator's 'except Exception' fail-safe could not catch — aborting the
request instead of falling back to Python. The Rust core is default-on, so one crafted
input (e.g. a 20-digit Solidity version overflowing i64) could take down a request.

- pi-agents/registry.rs: add run_agent_safe — catch_unwind converts an escaped panic
  into Err (=> a normal PyValueError, an Exception). + panic_safety_tests.
- pi-py/lib.rs: run_agent and run_agents route through run_agent_safe.
- consensus.py: _try_rust_agent now catches BaseException (re-raising
  KeyboardInterrupt/SystemExit) — defence in depth for an unpatched cdylib.
- tests: new test_rust_fallback.py (panic-like BaseException falls back; interrupts
  still propagate).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@New1Direction New1Direction merged commit 01348bd into rust-core-loadbearing Jun 1, 2026
2 of 4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant