confluence_web/agent/dev/isolation-probe.py
Artur Mukhamadiev 38a8ca67f7 agent: pi runtime track handoff (contract revision 1)
Pinned pi SDK 0.85.1 bridge, Python supervisor, artifact exporter,
scripted backend peer, image checks and boundary checks under agent/**.
Review findings F1-F3 are recorded in docs/implementation/PI_AGENT_REVIEW.md.
2026-09-14 21:57:54 +03:00

79 lines
3.0 KiB
Python

# Executed only by the fake model inside the target image; never on the host.
import ctypes
import os
import signal
import time
from pathlib import Path
assert os.getuid() == os.getgid() == 10001
assert Path.cwd() == Path('/work')
assert list(Path('/home/agent').iterdir()) == []
assert {p.name for p in Path('/work').iterdir()} == {'scratch.md', 'artifacts'}
assert set(os.environ) <= {'PATH', 'HOME', 'LANG', 'LC_CTYPE', 'PWD', 'SHLVL', '_'}
assert not Path('/home/vptyp').exists()
assert not Path('/opt/agent/.env').exists()
assert not Path('/opt/agent/node_modules/.env').exists()
assert Path('/sys/fs/cgroup/memory.max').read_text().strip() == '1073741824'
assert Path('/sys/fs/cgroup/pids.max').read_text().strip() == '128'
assert Path('/sys/fs/cgroup/cpu.max').read_text().split() == ['100000', '100000']
for line in Path('/proc/self/status').read_text().splitlines():
if line.startswith('CapEff:'):
assert int(line.split()[1], 16) == 0
if line.startswith('NoNewPrivs:'):
assert line.split()[1] == '1'
if line.startswith('Seccomp:'):
assert line.split()[1] == '2'
for name in ['/opt/agent/supervisor', '/opt/agent/bridge.ts', '/etc/probe-canary']:
try:
with open(name, 'w') as f:
f.write('tamper')
except OSError:
pass
else:
raise AssertionError('Root filesystem is writable')
for name in ['/proc/1/mem', '/proc/1/environ', '/proc/1/fd']:
try:
if name.endswith('/fd'):
os.listdir(name)
else:
open(name, 'rb').close()
except PermissionError:
pass
else:
raise AssertionError('Supervisor proc access is permitted')
assert ctypes.CDLL(None).ptrace(16, 1, 0, 0) == -1 # PTRACE_ATTACH
for sig in [signal.SIGSTOP, signal.SIGKILL, signal.SIGTERM, signal.SIGUSR1]:
os.kill(1, sig)
time.sleep(0.1)
assert Path('/proc/1/stat').read_text().rsplit(')', 1)[1].split()[0] not in ['T', 't', 'Z']
# Actually exhaust the PID cgroup with sleepers; reap them before continuing.
children = []
try:
for i in range(140):
try:
pid = os.fork()
except BlockingIOError:
break
if pid == 0:
time.sleep(10)
os._exit(0)
children.append(pid)
assert 1 < len(children) < 128, 'PID limit was not enforced'
finally:
for pid in children:
os.kill(pid, signal.SIGKILL)
for pid in children:
os.waitpid(pid, 0)
# Exercise memory enforcement rather than only reading the configured limit.
before = Path('/sys/fs/cgroup/memory.events').read_text()
pid = os.fork()
if pid == 0:
allocation = bytearray(1200 * 1024 * 1024)
os._exit(0)
_, status = os.waitpid(pid, 0)
assert os.WIFSIGNALED(status) and os.WTERMSIG(status) == signal.SIGKILL
after = Path('/sys/fs/cgroup/memory.events').read_text()
count = lambda text: int(dict(line.split() for line in text.splitlines())['oom_kill'])
assert count(after) > count(before), 'Memory limit did not produce a cgroup OOM kill'
Path('/home/agent/canary').write_text('must disappear on the next run')
print('isolation checks passed')