ops-warden/docs/development/safe-unit-tests.md
tegwick b593c87983
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
test: isolate ordinary unit runs from ambient credentials and provider I/O
Assistant: codex
Assistant-Model: gpt-6.1-sol
Assistant-Session: 01a1468b-63fa-7710-85dc-8f2e16b3ebfa
2026-10-17 21:30:12 +02:00

64 lines
4.2 KiB
Markdown

# Safe unit-test entry point
Use `make test`, or `.venv/bin/python -I scripts/run_unit_tests.py`. Dependencies
must already be installed; this path does not run uv, synchronize or download.
`UNIT_PYTHON` may select another existing interpreter. `PHASE_MEMORY_REPO` remains
an explicit local source option. Pass pytest options with `TEST_ARGS`, for example
`make test TEST_ARGS='-q tests/test_safe_unit_runner.py'`.
The launcher creates an empty temporary HOME and temporary fixture root and starts
an isolated (`-I`) Python interpreter with a constructed environment. Caller token,
proxy, Python, pytest, XDG and Warden configuration variables are never copied or
inventoried. Third-party pytest plugin autoload is disabled. Tests can explicitly
introduce synthetic variables with monkeypatch, but allowed children always get
the original temporary HOME and a fresh environment, never those variables.
Before pytest import/collection, a Python audit hook and subprocess wrapper:
- deny file opens outside the selected source roots, installed interpreter/library
roots and fixture root; writes through open are limited to the fixture root;
- deny sockets, DNS and process creation except the closed fixture inventory;
- refuse shell, executable, preexec and inherited-FD overrides outside that inventory;
- install the same file/socket/process denial before executing every allowed
Python child, including BrowserInputs' exact validator code and reverified copied
module hashes. Validator children cannot start further processes.
The process inventory in `scripts/run_unit_tests.py` is intentionally small:
BrowserInputs validation, four metadata-checker scripts with fixed argument shapes,
the exact original-intent restart probe, the synthetic containment print, and
closed guard-regression probes. Metadata checkers receive only fixture output
paths. Only the generated-artifact checker child can run the exact read-only Git
log/status queries for this repository's catalog, with global/system config and
optional locks disabled, hooks disabled and fsmonitor disabled. Those local Git
commands are not provider operations.
Two reviewed non-Python cases remain: exact-byte, temporary contract-double scripts
with a closed argv list, and five exact fixed `printf` commands used by existing
transport tests. They run Bash without profile/rc startup and without inherited
environment; no arbitrary shell text, provider binary, SSH, kubectl or OpenBao
command is permitted. Default unit selection excludes four integration-marked
SSH-keygen tests. Selecting them explicitly does not grant their native commands.
Only `test_desk.py::test_approve_flow_http_dry` receives a network lease. It may
bind `127.0.0.1:0` and connect to the endpoint actually allocated to that test's
socket. DNS is replaced by numeric fixture resolution. Another loopback service
(e.g. the real Hub on port 8000), Unix sockets and remote endpoints stay denied.
The lease and registered endpoints are cleared after the test.
Coverage tests now use the explicit public declaration projection snapshot in
`tests/fixtures/workload-coverage`, with provenance hashes. They compare the
published source snapshot rather than silently consulting the workstation HOME.
Run the existing coverage report separately with an explicit estate root when
measuring current estate state. Tests of the acknowledged NetKingdom source use
`--netkingdom-root` (default: sibling source checkout), never a HOME symlink. The
optional Phase Memory library's one existing public coordination-profile fixture
is explicitly readable alongside its source; no runtime memory store is inherited.
This is an accident guard for trusted unit tests, not a security sandbox for
hostile Python, native extensions or hostile installed dependencies. It does not
revoke already-open descriptors, prevent every filesystem mutation syscall, or
provide OS process/network isolation. Fixed Bash/Git fixture processes rely on
the reviewed executable and source/argument shapes; Python audit hooks do not run
inside those binaries. Do not run untrusted test code under a privileged runtime.
Direct `pytest` bypasses this launcher and is not equivalent validation. No
production Warden boundary, grant, catalog, source pin or owner credential changes.