fix(plane): sandbox-only fail-closed guard for Plane writes from test process (ORCH-117)
Close the root class of incident ORCH-114: a pytest/worktree process performed a REAL write (PATCH issues state=<Done> + comment) against the PRODUCTION Plane project, because test/staging processes inherit the live Plane token (PLANE_HEADERS/PROJECT_ID are captured at import — a post-hoc env/token swap is a no-op) and nothing forced them to write only to the sandbox. Symmetric to the existing _no_telegram autouse floor. - New pure never-raise leaf src/plane_write_guard.py (decide/audit_block/ audit_allow), wired into the 3 plane_sync write primitives (update_issue_state / add_comment / _set_issue_state_direct) via _guard_allows_write, AT CALL TIME, before any network step. Active ONLY in a test process (pytest in sys.modules / PYTEST_CURRENT_TEST); live + staging runtimes (uvicorn) are a strict no-op. - In a test process: default-deny. A write is allowed iff opt-in (plane_test_write_enabled) AND target project in the sandbox allowlist (plane_test_sandbox_projects, default = the one SANDBOX id). Prod is blocked even with opt-in (allowlist sandbox-only); unresolved project -> block (fail-closed). - Independent second layer: tests/conftest.py::_plane_sandbox_only autouse floor. Intentionally NO prod-block kill-switch (anti back-door, NFR-6). - Audit: block -> loud ERROR; sandbox-allow -> INFO. - Bypass fixtures for the 3 (+1) pre-existing tests that assert on the mocked write primitive's httpx call (header/URL/state logic), the guard is no Quality Gate: STAGE_TRANSITIONS / QG_CHECKS / check_* / machine-verdict / DB schema untouched. - Tests: tests/test_orch117_plane_write_isolation.py (TC-01 mandatory ORCH-114 regression + TC-02..TC-14). Docs: CLAUDE.md, architecture/README.md, operations/INFRA.md, .env.example, CHANGELOG.md. Refs: ORCH-117 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
13
.env.example
13
.env.example
@@ -24,6 +24,19 @@ ORCH_PLANE_BOT_REVIEWER=
|
||||
ORCH_PLANE_BOT_TESTER=
|
||||
ORCH_PLANE_BOT_DEPLOYER=
|
||||
ORCH_PLANE_BOT_STREAM=
|
||||
# ORCH-117: sandbox-only fail-closed guard for Plane WRITES from a test/worktree
|
||||
# process (regression of ORCH-114, where pytest mutated a live prod board issue).
|
||||
# In the live runtime (uvicorn, no pytest) the guard is a no-op; in a test process
|
||||
# it BLOCKS every Plane write unless BOTH the opt-in is true AND the target project
|
||||
# is in the sandbox allowlist. Defaults are SAFE (default-deny): leave both as-is.
|
||||
# ORCH_PLANE_TEST_WRITE_ENABLED -> opt-in for REAL Plane writes from a test process.
|
||||
# false (default) = no test may write to Plane. NOT a kill-switch for the prod
|
||||
# block: even true, only the sandbox allowlist below is writable (a prod write
|
||||
# from pytest stays impossible).
|
||||
# ORCH_PLANE_TEST_SANDBOX_PROJECTS -> CSV allowlist of sandbox project ids the
|
||||
# opt-in may write to. Default = the single SANDBOX project; empty = none.
|
||||
ORCH_PLANE_TEST_WRITE_ENABLED=false
|
||||
ORCH_PLANE_TEST_SANDBOX_PROJECTS=8c5a3025-4f9d-4190-b79f-fa06276bb27e
|
||||
# Telegram live-tracker / alerts (empty -> notifications are logged, not sent).
|
||||
ORCH_TELEGRAM_BOT_TOKEN=
|
||||
ORCH_TELEGRAM_CHAT_ID=
|
||||
|
||||
Reference in New Issue
Block a user