Assistant: codex Assistant-Model: gpt-6.1-sol Assistant-Session: 01a10391-6355-7b41-b0fa-3e14e874f091
163 lines
7.2 KiB
Python
163 lines
7.2 KiB
Python
"""Prove a real rein CLI starts in a pinned, read-only owner runtime.
|
|
|
|
No credential acquisition or model request is performed.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import json
|
|
import subprocess
|
|
import tempfile
|
|
from pathlib import Path
|
|
|
|
from sandboxer.extensions.bwrap import BwrapExtension
|
|
from sandboxer.models import Profile
|
|
|
|
ENTRYPOINT_PROBE = r'''
|
|
def entrypoint_started(runtime, name, timeout=15):
|
|
launcher = runtime / 'bin' / name
|
|
if launcher.is_symlink() or not launcher.is_file():
|
|
return False
|
|
try:
|
|
result = subprocess.run([str(launcher), '--help'], capture_output=True,
|
|
timeout=timeout)
|
|
except (OSError, subprocess.TimeoutExpired):
|
|
return False
|
|
return result.returncode == 0 and b'usage:' in result.stdout.lower()
|
|
|
|
|
|
def package_inside_runtime(module, runtime):
|
|
origin = getattr(module, '__file__', None)
|
|
return bool(origin) and Path(origin).resolve().is_relative_to(runtime.resolve())
|
|
'''
|
|
|
|
PROBE = ENTRYPOINT_PROBE + r'''
|
|
import json, os, subprocess, sys
|
|
from pathlib import Path
|
|
import rein_aharness.adapter
|
|
import llm_connect
|
|
source = Path(sys.argv[1])
|
|
runtime = Path('/opt/sandboxer/runtime')
|
|
home = Path(os.environ['HOME'])
|
|
private = home / 'private-state-proof'
|
|
private.write_text('non-secret private state\n')
|
|
assert not home.is_relative_to(Path.cwd())
|
|
assert (home.stat().st_mode & 0o777) == 0o700
|
|
assert private.is_file()
|
|
try:
|
|
(runtime / 'write-probe').write_text('must be refused')
|
|
except OSError:
|
|
readonly = True
|
|
else:
|
|
readonly = False
|
|
assert readonly
|
|
assert not source.exists()
|
|
assert entrypoint_started(runtime, 'rein-aharness'), 'rein entrypoint did not start'
|
|
owner_entrypoints_required = sys.argv[3] == 'true'
|
|
glas_cli_started = False
|
|
packages = [rein_aharness.adapter, llm_connect]
|
|
if owner_entrypoints_required:
|
|
import glas_harness
|
|
import sandboxer
|
|
packages.extend([glas_harness, sandboxer])
|
|
glas_cli_started = entrypoint_started(runtime, 'glas-harness')
|
|
assert glas_cli_started, 'glas entrypoint did not start'
|
|
packages_inside_runtime = all(package_inside_runtime(module, runtime) for module in packages)
|
|
assert packages_inside_runtime, 'package outside runtime'
|
|
assert json.loads(os.environ['SANDBOXER_CREDENTIAL_ROUTE_REFS']) == []
|
|
assert not any(name in os.environ for name in
|
|
('ANTHROPIC_API_KEY', 'ANTHROPIC_AUTH_TOKEN', 'OPENAI_API_KEY',
|
|
'OPENROUTER_API_KEY')), 'provider credential present'
|
|
claude_version = None
|
|
proxy_present = False
|
|
if sys.argv[2]:
|
|
assert 'ANTHROPIC_API_KEY' not in os.environ
|
|
proxy_present = os.environ['HTTPS_PROXY'].startswith('http://127.0.0.1:')
|
|
assert proxy_present
|
|
cli = subprocess.run([str(runtime / 'bin/claude'), '--version'],
|
|
capture_output=True, text=True, timeout=20)
|
|
assert cli.returncode == 0, cli.stderr
|
|
claude_version = cli.stdout.strip()
|
|
assert claude_version == sys.argv[2] + ' (Claude Code)', claude_version
|
|
assert subprocess.check_output(['git', 'status', '--porcelain', '--ignored=matching']) == b''
|
|
interfaces = [line.split(':', 1)[0].strip()
|
|
for line in Path('/proc/net/dev').read_text().splitlines()[2:]]
|
|
assert interfaces == ['lo']
|
|
print(json.dumps({'rein_cli_started': True, 'adapter_imported': True,
|
|
'runtime_readonly': readonly, 'source_absent': True,
|
|
'owner_entrypoints_required': owner_entrypoints_required,
|
|
'glas_cli_started': glas_cli_started, 'packages_inside_runtime': True,
|
|
'home_outside_workspace': True, 'home_mode': '0700',
|
|
'worktree_clean': True, 'interfaces': interfaces,
|
|
'python_prefix': sys.prefix,
|
|
'claude_version': claude_version, 'https_proxy_present': proxy_present,
|
|
'claude_provider_request_proven': False,
|
|
'credential_refs': json.loads(os.environ['SANDBOXER_CREDENTIAL_ROUTE_REFS'])}))
|
|
'''
|
|
|
|
|
|
def main() -> int:
|
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
parser.add_argument("--runtime-path", required=True)
|
|
parser.add_argument("--runtime-sha256", required=True)
|
|
parser.add_argument("--claude-version", default="")
|
|
parser.add_argument("--require-owner-entrypoints", action="store_true",
|
|
help="Require Glas and rein startup and all four owner packages")
|
|
args = parser.parse_args()
|
|
with tempfile.TemporaryDirectory(prefix="sandboxer-runtime-proof-") as temporary:
|
|
root = Path(temporary)
|
|
source = root / "source"
|
|
source.mkdir()
|
|
subprocess.run(["git", "init", "-q", str(source)], check=True)
|
|
egress = ["api.anthropic.com:443"] if args.claude_version else []
|
|
extension = BwrapExtension({
|
|
"allowed_egress": egress,
|
|
"base_dir": str(root / "sandboxes"),
|
|
"runtime": {"path": args.runtime_path, "sha256": args.runtime_sha256},
|
|
})
|
|
profile = Profile(id="profile.runtime-proof", version="1", extension="ext.bwrap",
|
|
network={"default": "deny", "egress": egress})
|
|
handle = extension.provision(profile, {"repo": str(source)}, "localhost")
|
|
try:
|
|
extension.wait_ready(handle)
|
|
result = extension.execute(
|
|
handle, ["python3", "-c", PROBE, str(source), args.claude_version,
|
|
str(args.require_owner_entrypoints).lower()],
|
|
credential_route_refs=[],
|
|
execution_context={"actor": "agt", "project": "sand-boxer-runtime-proof",
|
|
"run_id": "sand-wp-0015-proof"},
|
|
timeout_seconds=60, max_output_bytes=65536,
|
|
)
|
|
persistence = extension.execute(
|
|
handle, ["python3", "-c",
|
|
"import os; from pathlib import Path; "
|
|
"assert (Path(os.environ['HOME']) / 'private-state-proof').is_file()"],
|
|
credential_route_refs=[],
|
|
execution_context={"actor": "agt", "project": "sand-boxer-runtime-proof",
|
|
"run_id": "sand-wp-0015-proof"},
|
|
timeout_seconds=15, max_output_bytes=1024,
|
|
)
|
|
finally:
|
|
teardown = extension.teardown(handle)
|
|
passed = result["exit_code"] == 0 and not result["timed_out"]
|
|
facts = json.loads(result["stdout"]) if passed else {}
|
|
private_state_persisted = persistence["exit_code"] == 0
|
|
passed = passed and private_state_persisted and teardown["workspace_removed"] == "True"
|
|
proxy_removed = not egress or (
|
|
not Path(handle["egress_dir"]).exists()
|
|
and not extension._pid_alive(int(handle["egress_pid"]))
|
|
)
|
|
passed = passed and proxy_removed
|
|
print(json.dumps({
|
|
"ok": passed, "sandbox_id": handle["sandbox_id"],
|
|
"runtime_sha256": args.runtime_sha256, "proof": facts,
|
|
"workspace_removed": teardown["workspace_removed"] == "True",
|
|
"exit_code": result["exit_code"], "model_run_proven": False,
|
|
"private_state_persisted": private_state_persisted, "proxy_removed": proxy_removed,
|
|
}, indent=2))
|
|
return 0 if passed else 1
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|