Uh oh!
There was an error while loading. Please reload this page.
Add protected mock service and plugin bundle projection - #95
Open
dmorosanu wants to merge 14 commits into
Open
Conversation
…isolation wrapper execs it
…served-path assertion
…e isolation default
…dir and skip POSIX-only relative symlinks on Windows
feat(protected-mock): add subset fixture matching and harden mockd startup
dmorosanu
requested review from
akshaylive, bai-uipath, tmatup and uipreliga
as code ownersAugust 10, 2026 08:33
Claude finished @dmorosanu's task in 1m 58s —— View job Code Review in Progress |
dmorosanuforce-pushed
the
codex/uid-gid-agent-isolation
branch
from
August 10, 2026 14:16
1dd19ec to
7a2c59aCompare
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 freeto 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.

This PR carries the protected mock service and the plugin bundle projection, split out of #87 so that PR stays scoped to the UID/GID architecture; it stacks on
codex/uid-gid-agent-isolationbecause both features build on the agent/grader identity boundary (mockd authorizes callers by the agent UID and needs the image users and groups).Fixture matching behavior is unchanged from the previously merged sub-PR #93.
Protected mock service (mockd)
How it works. Fixtures live in
/opt/coder-eval/mock/fixtures(mode0500, owned by themockduser), so they are not readable by the agent identity. The agent instead gets thin client wrappers named as the mocked CLI (for exampleuip) onPATH, generated bySandbox._generate_protected_mock_clients. The wrappers talk to mockd over a Unix domain socket, so fixture bytes never enter the agent-readable filesystem.How it starts.
docker_runnerstages fixtures and writesmock-config.jsoninto the private grader tree.run_task_internalwraps the turn inrunning_mock_server(...).coder_eval_mockd.sh, whichexecssetpriv --reuid=mockd --regid=mockd --groups=uip-rpc(UID/GID 2100) with all capabilities dropped and--no-new-privs./run/coder-eval/uip.sock, chowns it to theuip-rpcgroup and chmods it0660.The agent user is a member of
uip-rpconly whenprotected_mocksis configured (CODER_EVAL_AGENT_ALLOW_RPC). Startup failures abort loudly, carrying the child's exit code and a tail of its stderr, rather than binding a socket over a partially loaded fixture set.Algorithm (one request).
argv(size-capped atMAX_REQUEST_BYTES; oversized requests are refused before any connection is attempted).SO_PEERCRED- root or the agent UID only.match_mode: subsetrules in fixture-file order (unchanged from feat(protected-mock): add subset fixture matching and harden mockd startup #93).[docsai, ask]), run the real command with no shell, bounded execution time and output size, and an in-memory response cache.Every call is appended to
calls.jsonl.Plugin bundle projection
How it works. Local plugins are projected into sanitized read-only copies at
/opt/coder-eval/agent-skills/plugin-Ninstead of raw host mounts.agent.plugins[].pathin the task payload is rewritten to point at the projection.How it starts. During
_prepare_isolated_sources, each local plugin source is staged viastage_bundlebefore the container starts. The bundle directory is mounted read-only, and the manifest is kept beside the bundle rather than inside it.Algorithm.
build_manifestwalks only the allowed top-level subtrees (skills,commands,agents,.claude-plugin,hooks), sha256-hashes every file, and validates that symlinks resolve inside the allowed subtrees (absolute links are rejected, as are broken or looping ones).resolution.md,check_*.py) appear inside an agent-visible subtree.stage_bundlerequires an empty destination, copies exactly the manifest-listed files, and re-verifies the digest after copying.Validation
Ran the full
make verifysequence locally on Windows (invoking the underlying commands directly):ruff format --check src/ tests/ .github/scripts/ruff check src/ tests/ .github/scripts/pyrightantigravity_agent.py:394)pytest tests/test_custom_lint.py(CE001+)pytest tests/ -n auto -m "not live and not lint" --cov-fail-under=80Known-acceptable results:
tests/test_sandbox.py(test_build_run_command_env_preserves_external_plugin_tools_dir,test_capture_to_copies_and_tolerates_dangling_symlink) fail withOSError: [WinError 1314] A required privilege is not held by the client. These are pre-existing environmental failures - Windows symlink creation needs admin or Developer Mode - and are unrelated to this change.TestCE028DocIndexParity::test_every_published_doc_is_in_the_nav, is caused entirely by five untracked local scratch files in my working tree that are not part of this branch. The onlydocs/pages this commit touches -DOCKER_ISOLATION.mdandTASK_DEFINITION_GUIDE.md- are both already in the mkdocs nav, so CE028 is clean for this change and will pass in CI.The diff of this branch against the pre-split base (
dcc73456) is exactly the added test coverage intests/test_protected_mock.pyand nothing else, confirming the split restored the stripped content byte for byte.