Repository navigation
Conversation
❌ 1 Tests Failed:
View the top 3 failed test(s) by shortest run time
To view more test analytics, go to the Test Analytics Dashboard |
ebursztein
added a commit
that referenced
this pull request
Oct 7, 2026
Link assigned issue #342 and source PRs #338, #339 and #340. Keep completed parser components available while removing Inspect VM/workload and Dockerfile/Compose phases from this agent sprint. Shared 0.7 includes all carried code and latest main; complete integration and runtime/package qualification remain Pierre’s work.
tholop
added a commit
that referenced
this pull request
Oct 8, 2026
Add OCI container execution mode (`execution_mode="container"` / `execution_mode="auto"`, `image=...`, and `compose.yaml` / `docker-compose.yml` config parsing) on top of Capsem VM sandboxes: - `inspect_capsem/containers/compose.py` + `compose_fields.py` + `compose_inputs.py` + `compose_interpolation.py` + `compose_service.py` + `compose_values.py`: single bounded Compose parser built on `ComposeInputs`, `ComposeLimits` / `_BoundedSafeLoader`, and `InterpolationBudget` with redacted diagnostics, default-deny host environment interpolation (`SAMPLE_METADATA_*` allowlist with `.env` spoofing rejection), support for `image`, `command`/`entrypoint`, `environment`, `working_dir`, `user`, `volumes` (read-write and `:ro` bind mounts with symlink and project-root containment), `healthcheck`, `cpus`/`mem_limit`, and fail-closed validation on unsupported service keys, network isolation overrides, and multi-service topologies. - `inspect_capsem/containers/runtime.py` + `controller.py`: OCI container staging (`prepare_oci_workload_container`, `_stage_oci_bind_volumes`) and `ContainerController` protocol. - `inspect_capsem/_compose.py`, `_controller.py`, `_exec.py`, `_files.py`, `_lifecycle.py`, `config.py`, `sandbox.py`: thread container execution mode, non-root `user` execution (`su -m` / `setpriv`), `/workspace` staging, and `SdkCapsemController` `registry_ca_pem` / `Registry(ca_pem=...)` plumbing (moved from PR #340 into PR #339 so #339's hermetic loopback TLS OCI workload fixture authenticates without #340 while `CapsemSandboxConfig` continues to reject `registry_ca_pem` in untrusted task configs). - Unit tests (`tests/containers/*`, `tests/test_config.py`, `tests/test_sandbox*.py`) and hermetic loopback TLS OCI workload acceptance (`tests/oci_workload_fixture.py`, `tests/live_acceptance.py`). Proves #310 / #311 / #342 acceptance criteria: - [x] Compose config coercion (`compose.yaml` / `docker-compose.yml`) and `CapsemSandboxConfig(image=...)` select `execution_mode="container"` and route `exec` to `ExecTarget.WORKLOAD`. - [x] Hermetic OCI workload acceptance (`tests/oci_workload_fixture.py` + `tests/live_acceptance.py`) builds a digest-pinned OCI image from the guest initrd busybox, serves it over loopback TLS with a per-run CA passed via `SdkCapsemController(registry_ca_pem=...)`, admits its digest in `settings.toml`, runs `sample_init`, `exec` (`ExecTarget.WORKLOAD`), `write_file`/`read_file` (text and binary), and `eval_async` with `SandboxEnvironmentSpec("capsem", ...)` without pulling from Docker Hub, and verifies `history(layer=EXEC)` + `session.db` `exec_events` (`target="workload"`) and zero leaked VMs after `sample_cleanup`.
tholop
force-pushed
the
feat/inspect-capsem-containers
branch
from
October 8, 2026 13:07
99a088b to
3cb33cd
Compare
This was referenced Oct 8, 2026
…ck OCI registry Add evaluator-granted host-side Dockerfile and Compose `build:` image building and a read-only loopback HTTPS OCI v2 registry to `inspect-capsem-sandbox`: - Add `HostBuildGrant` (`containers/build_grant.py`) enforcing evaluator environment authority (`CAPSEM_INSPECT_HOST_BUILD=1`, `CAPSEM_INSPECT_ALLOWED_HOST_PATHS`, `CAPSEM_INSPECT_BUILD_NETWORK`, `CAPSEM_INSPECT_BUILD_DOCKER_CONFIG`) with task-level narrowing and strict refusal of `--network host`. - Add `containers/build_context.py` for canonical path containment, symlink escape rejection, `.dockerignore` context tree walking, deterministic cache key hashing, and unified Compose `build:` / `dockerfile:` normalization. - Add `containers/image_build.py`, `containers/oci_ingest.py`, and `containers/oci_registry.py` to build OCI archives via `docker build` / `buildx`, ingest OCI-layout and legacy `docker-save` tars into a content- addressed blob store (skipping BuildKit attestation manifests), and serve verified digests over a loopback TLS 1.2+ `/v2/` registry with 0.7 `[images]` `sources`, `ca_pem`, and `admit` profile wiring.
Stage .capsem-ca.crt and .capsem-ca-bundle.crt into a temporary build context when CAPSEM_INSPECT_BUILD_CA_PEM_FILE (and optional CAPSEM_INSPECT_BUILD_CA_BUNDLE_FILE) is set, seed NSS sql:$HOME/.pki/nssdb via certutil during builds and container startup, require BuildKit on the host (removing the legacy 0.6 classic-builder heredoc lowering module), and include the CA fingerprint in build cache keys.
Extend `run_live_vm_sandbox_acceptance` (invoked by the live VM gate `test_inspect_ai_live_vm_sandbox_acceptance`) with `verify_host_build_workload_mode` (`#342` bullets 2, 5, and 6): - Require the `docker` CLI and daemon on `PATH` and fail loudly if absent (no silent skip; `#342` bullet 5). - Load a hermetic busybox base image from the guest initrd into local Docker and build a multi-stage `Dockerfile` + `compose.yaml` (`build:` with `context`, `dockerfile`, `target`, and `args`) completely offline (`CAPSEM_INSPECT_BUILD_NETWORK=none`). - Verify fail-closed refusals when `CAPSEM_INSPECT_HOST_BUILD` is unset, when a symlink escapes the build context, and when `HostBuildGrant` attempts to widen `allowed_contexts` or `network` beyond the operator environment ceiling. - Verify multi-stage `FROM ... AS ...`, `FROM scratch`, `ARG`/`ENV`, `WORKDIR`, `USER 1000:1000`, `ENTRYPOINT`/`CMD`, `COPY --from` with `--chown`/`--chmod`, heredoc `RUN`/`COPY`, `ADD` local tar extraction, `.dockerignore` exclusion, real `inspect-sandbox-tools` host binary upload (`700:1000:1000`) and onedir archive extraction as UID `1000:1000` (`RUN rm -rf /var/tmp/sandbox-services` before `USER 1000:1000`), and `CAPSEM_INSPECT_BUILD_CA_PEM_FILE` CA injection (including byte-for-byte PEM equality inside the live guest container) on a real VM workload container (`#342` bullets 2 and 6). - Verify non-root `USER 1000:1000` container file round-trip (`write_file`, in-container append, `read_file`), `PermissionError` on `user="root"` privilege escalation under `no-new-privileges`, and `inspect_ai`'s portable `self_check` suite against the `USER 1000:1000` container. - Pin a stable loopback OCI registry port (preferring default `5055` via `CAPSEM_INSPECT_BUILD_REGISTRY_PORT` and falling back to an available loopback port if `5055` is busy) across `settings.toml` `[images]` source admission and verify second-sample digest cache reuse without a second `docker build` even after tightening `settings.toml` to digest-only admission. - Load sibling test modules in standalone `python -I` gateway consumer runs via `importlib.util.spec_from_file_location` without mutating `sys.modules["tests"]`, and use `unittest.mock.patch.object` for test seam overrides.
tholop
force-pushed
the
feat/inspect-capsem-containers
branch
from
October 9, 2026 15:49
3cb33cd to
6ea58de
Compare
tholop
force-pushed
the
feat/inspect-capsem-host-build
branch
from
October 9, 2026 15:49
da3aea1 to
0f6595b
Compare
tholop
marked this pull request as draft
October 9, 2026 15:52
This was referenced Oct 9, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds an opt-in, operator-granted host
docker buildpath (HostBuildGrant) toinspect-capsemso Inspect AI evaluations that ship aDockerfileor Composebuild:stanza can build images on the host with BuildKit, cache them by content hash, and serve them over a loopback HTTPS OCI v2 registry to Capsem0.7's existingHypervisor.create(image="...@sha256:...")puller (#342 step 4). This PR only touchesintegrations/inspect-ai,web/docs, and the hermetic live gate fixture (tests/ironbank/test_sdk_live.py,tests/helpers/sdk_packages.py); it adds no new routes or container-build machinery tocapsem-service, the guest runtime, or the SDKs. Stacked on #339.Changes
build:parsing (build_grant.py,compose_fields.py):docker buildis off by default and runs only when the operator setsCAPSEM_INSPECT_HOST_BUILD=1in the evaluator process environment (0is an unconditional hard deny). Task configs (CapsemSandboxConfig.host_build: HostBuildGrant | bool | None) can only narrow the operator's environment settings for a task or sample viaHostBuildGrant(enabled: bool = True, allowed_contexts: tuple[str, ...] = (), network: Literal["none", "default"] | None = None, ca_pem: bool = True), never widen them.dockerfile:/build:strings and rejects remote URLs viaprepare_compose_build_service, then delegates Composebuild:field extraction to0.7'scompose_service.extract_compose_fields(dockerfile,build_context,build_args,build_target), preserving its input bounds and documented refusals (dockerfile_inline,build.secrets,ssh,privileged,network,cache_from,cache_to,extra_hosts,additional_contexts, and unknownbuild:keys).build_context.py,image_build.py):os.path.realpath(context_dir)andos.path.realpath(dockerfile)underCAPSEM_INSPECT_ALLOWED_HOST_PATHS(the Compose file directory is not an implicit build root)..dockerignoreis honoured for symlink containment (refusing any non-ignored escaping symlink beforedocker build), whilecompute_build_cache_keyhashes all regular files incontext_dirso.dockerignorematcher differences cannot cause stale cache hits.docker buildwith--network noneby default (CAPSEM_INSPECT_BUILD_NETWORK=none|default;network="host"is always refused) and an isolated temporary--configdirectory ({"auths":{}}) unlessCAPSEM_INSPECT_BUILD_DOCKER_CONFIGexplicitly grants an operator directory underCAPSEM_INSPECT_ALLOWED_HOST_PATHS(ambient~/.docker/config.jsonis never read).docker.sock.build_ca.py,dockerfile_ca.py,oci_ingest.py,oci_registry.py):DOCKER_BUILDKIT=0is refused beforedocker build), passingRUN <<EOFandCOPY <<EOFheredocs straight through to BuildKit and removing the legacy0.6classic-builder heredoc module (dockerfile_heredoc.py).CAPSEM_INSPECT_BUILD_CA_PEM_FILEandCAPSEM_INSPECT_BUILD_CA_BUNDLE_FILE, required when combining CA injection withnetwork="default") into a temporary copy of the build context for non-scratchstages and includes the CA SHA-256 fingerprint in the build cache key.cache_key(threading.Lock()), writes cached OCI blobs via unique temporary files before atomic rename, mints loopback TLS certificates atomically under a cross-processfcntl.flock(.tls.lock) +threading.Lock(), verifies cached blob SHA-256 digests on every lookup, serves blobs over a read-only loopback HTTPS OCI v2 registry (127.0.0.1:5055) by@sha256:digest only, and relies directly on0.7's guest container launcher for runtimeUSER,WORKDIR, andENV.Authority Boundary (#342 Step 4)
Because
0.7does not run an in-guest image builder, building a task'sDockerfileruns on the host Docker daemon outside the micro-VM boundary. The operator enables builds and sets limits via environment variables on the evaluator process; tasks can only narrow those limits, andinspect-capsemnever reads ambient~/.dockercredentials or edits~/.capsem/settings.toml:CapsemSandboxConfig.host_build=HostBuildGrant(...))CAPSEM_INSPECT_HOST_BUILD=1(0= hard deny)host_build=FalseorHostBuildGrant(enabled=False)disables for this taskValueErrornamingCAPSEM_INSPECT_HOST_BUILDand the stanza that needs it)CAPSEM_INSPECT_ALLOWED_HOST_PATHSmust containrealpath(context_dir)andrealpath(dockerfile)allowed_host_paths/host_build.allowed_contextsnarrow (never widen); Compose dir is not implicit.dockerignore/ symlinks.dockerignorehonoured for symlink containment while cache key hashes all regular context files (Dockerfileand.dockerignorerealpaths validated directly); non-ignored escaping symlink refused beforedocker buildCAPSEM_INSPECT_BUILD_NETWORK=none|defaultsets the limithost_build.networkcan only narrow (default -> noneallowed;none -> defaultrefused)none(--network none);hostalways refusedCAPSEM_INSPECT_BUILD_DOCKER_CONFIG=<dir>underCAPSEM_INSPECT_ALLOWED_HOST_PATHS(operator-env-only)HostBuildGrantdocker --configwith{"auths":{}};~/.dockernever readprepare_compose_build_service+compose_service.extract_compose_fieldsdockerfile_inline,build.secrets,ssh,privileged,network,cache_from,cache_to,extra_hosts,additional_contexts→ documented refusalCAPSEM_INSPECT_BUILD_CA_PEM_FILE=<path>(+ requiredCAPSEM_INSPECT_BUILD_CA_BUNDLE_FILE=<path>whennetwork="default") underCAPSEM_INSPECT_ALLOWED_HOST_PATHS(operator-env-only paths)host_build.ca_pem: bool = True(ca_pem=Falseopts out of CA patching for the task)docker.sock→ refuse~/.capsem/settings.toml)0.7[images]selectors once:sources = ["127.0.0.1:5055"]admit = ["127.0.0.1:5055/inspect-capsem/build"](or exact@sha256:pins)Hypervisor.createpasses@sha256:<manifest>+Registry(ca_pem=...)[images]lines verbatim; integration never edits settingsQualification & Alternatives Considered (#342)
integrations/inspect-ai/tests):ruff check,ruff format --check, andty check --error-on-warning --python-platform allPASS (0findings);pytest integrations/inspect-ai/tests -q: 101 passed (95%coverage) acrosstest_build_grant.py,test_build_context.py,test_image_build.py(including 4-thread concurrent build singleflight + blob ingest regression),test_oci_registry.py,test_build_ca.py, andtest_dockerfile_ca.py.tests/ironbank/test_sdk_live.py::test_inspect_ai_live_vm_sandbox_acceptance,260.37s): runstests/host_build_acceptance.py+tests/host_build_fixture.pyfrom both offline-installed wheel and sdist underpython -I(INSPECT_CAPSEM_HOST_BUILD_ACCEPTANCE_OK), verifying default-off and widened-grant refusals, escaping-symlink refusal, offline multistagedocker build --network none(with.dockerignore,--build-arg,--target,FROM scratch,RUN <<EOF© <<EOFheredocs,COPY --chmod/--chown,ADDarchive unpack, non-rootUSER 1000:1000,WORKDIR,ENV,ENTRYPOINT/CMD, tool-bundle0700upload,exec(user="root") -> PermissionError, and34/34portableinspect_self_checkchecks), realopensslCA staging, second-run cache hit without rebuilding,session.dbexec_events(target="workload"), and0leaked VMs.inspect_evals/vimgolf_single_turn --limit 2(2/2samples,0errors),inspect_evals/gdm_intercode_ctf --limit 2(sample_ids=[0, 2], Composebuild: .withnetwork_mode: bridgeunderCAPSEM_INSPECT_HOST_BUILD=1,2/2samples,0errors), andinspect_harbor/terminal_bench_2(1/1sample,0errors), all with0leftover VMs.build:(max_sandboxes=4, 4 samples,400 MiBand1 GiBuncompressed layers): cold cache runsdocker buildonce while 3 threads wait on_lock_for_cache_key(0blob temp-file collisions); warm cache runs0docker buildcalls (4/4cache hits);0leaked VMs across all runs (see feat(inspect-ai): add OCI container execution mode and Compose parser #339 Known Limitations 1 & 2 forcapsem-service's30soci-cache-lockcontention when creating 4 VMs from the same large image concurrently).capsem-service(0.7: Refactor and land Inspect Compose/container support #311 / 0.7: Complete service image/cache and managed ephemeral-session contracts #301): aPOST /images/importroute (or local OCI-layout reference) that ingests adocker image savearchive straight intocapsem-assets's host blob cache would remove the loopback HTTPS registry, ephemeral TLS certificate generation, and127.0.0.1:5055[images]source configuration. We are happy to wait for or help build native OCI import if you prefer that over the loopback registry bridge; this PR usesHypervisor.create(image="...@sha256:...")so it requires zero changes tocapsem-service.