Skip to content

fix(docker): mount plugins at /work/plugins/<i> and point the staged task there - #206

Merged
akshaylive merged 4 commits into
mainfrom
akshaya/plugin_mount_fix
Sep 29, 2026
Merged

akshaylive merged 4 commits into
mainfrom
akshaya/plugin_mount_fix

Conversation

@akshaylive

@akshaylive akshaylive commented Sep 29, 2026 •

Copy link
Copy Markdown
Collaborator

Problem

Under driver: docker, the runner resolved each agent.plugins[].path on the host and mounted it at the same host path. But the task.yaml staged into the container kept the path as authored, and the in-container agent resolved it again against the container's cwd. As a result, a relative or $VAR plugin path silently loaded no skill:

  • Relative path, such as plugin or ./plugin: the host mounted it correctly, but inside the container the path pointed at a directory that doesn't exist. Codex logged Plugin skills path did not resolve: ... (path does not exist), and Claude loaded nothing.
  • ${VAR}/plugin: it works only if VAR is in the real host environment (not just .env) and is forwarded into the container. Otherwise it isn't expanded, so nothing is mounted and nothing loads.

Only absolute host paths worked end to end. The failure isn't obvious, because the run still grades normally, just without the skill.

Fix

  • Mount point: plugin i is mounted :ro at /work/plugins/<i>, a new CONTAINER_PLUGINS_DIR constant, which is also added to RESERVED_CONTAINER_DIRS. It sits under /work, so the existing extra_mounts check (which refuses destinations under /work/) stops a task from shadowing a plugin mount.
  • Staged task: _stage_inputs rewrites plugin i's path in the staged task.yaml to /work/plugins/<i>. The host-side task isn't modified.
  • One shared mapping: both steps use one helper, _plugin_mounts(), which gives each plugin's host directory and container path, so the mount and the rewrite can't disagree. Host-side resolution is unchanged (_resolve_mount_path: a relative path resolves against the task YAML's directory, and $VAR and ~ are expanded).
  • Masks: the tmpfs masks that hide non-skill folders now sit under the plugin's container path, and a nested plugin root still keeps its own bind.
  • Unresolved entries: a plugin that doesn't resolve to a host directory is neither mounted nor rewritten, same as before.
  • Docs: docs/DOCKER_ISOLATION.md is updated.

Testing

  • Unit tests: the three TestAutoMountAllowlistMask tests now assert container-path binds and masks. The new test_the_staged_task_yaml_points_plugins_at_their_container_mounts covers the task.yaml rewrite.
  • Full suite: uv run pytest gives 6727 passed and 1 failed, test_codex_golden[g_items_rebuild], which fails on main too.
  • Pyright: make verify still reports one error, harbor/agent.py:65 (SUPPORTS_ATIF), which is also on main.
  • End to end: a real driver: docker task with plugins: [{type: local, path: plugin}], run on claude-code (claude-sonnet-5) and codex (gpt-5.6-luna). Both got the bind …/plugin:/work/plugins/0:ro, loaded the skill, and passed:
    • Claude's Skill call succeeded, and it ran the skill's scripts from /work/plugins/0/skills/….
    • Codex logged Linked skill: copilot-usage-metrics.
    • Without this change, the same task loaded no skill on either harness.

🤖 Generated with Claude Code

…taged task there

The runner resolved each agent.plugins[].path on the host and mounted it at
its host path, but the task.yaml staged into the container kept the
authored string. The in-container agent then re-resolved it against the
container's cwd: a relative path, or a $VAR the container did not have,
pointed at nothing and the skill silently never loaded (only a warning).

Now plugin i mounts :ro at /coder_eval/plugins/<i>, and the staged
task.yaml is rewritten to that path, via one shared _plugin_mounts()
mapping so the bind and the rewrite cannot disagree. The anti-cheat tmpfs
masks move under the container path. The host-side task is untouched.
Entries that do not resolve to a host directory are neither mounted nor
rewritten. /coder_eval/plugins joins RESERVED_CONTAINER_DIRS.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…plugins

Every other framework-owned container path lives under /work, and
_validate_extra_mount already refuses any extra_mounts destination under
/work/, so a task can no longer shadow a plugin mount with its own.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@akshaylive akshaylive changed the title fix(docker): mount plugins at /coder_eval/plugins/<i> and point the staged task there fix(docker): mount plugins at /work/plugins/<i> and point the staged task there Sep 29, 2026
akshaylive and others added 2 commits September 29, 2026 13:59
pip-audit started failing on two new advisories:

- pyjwt 2.13.0, CVE-2026-102274 (a malformed RSA JWK aborts parsing of
  the whole JWK set): floor raised to >=2.14.0; the lock resolves 2.15.1,
  the newest release past the safe-chain minimum package age.
- oauthlib 3.3.1, CVE-2026-49265 (PKCE timing side channel): the flaw is
  in the server-side code_challenge check, which coder-eval never runs;
  oauthlib is only transitive (azure-monitor-opentelemetry-exporter ->
  msrest -> requests-oauthlib). The fix, 4.0.0, is a major bump published
  inside the minimum-age window, so ignore it in pip-audit and
  osv-scanner for now and revisit next month.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ignore

osv-scanner flagged a second oauthlib 3.3.1 advisory, GHSA-hj66-6f7g-4r5v
(CVE-2026-49264, RevocationEndpoint JSONP callback injection with
enable_jsonp=True), published 2026-09-29 and not yet in pip-audit's DB.
Like CVE-2026-49265 it is a server-side OAuth endpoint coder-eval never
runs, and the fix is the same too-new 4.0.0, so ignore it in both
scanners.

PYSEC-2025-183 (pyjwt 2.12.1) no longer applies now that pyjwt is 2.15.1;
osv-scanner reported it as an unused ignore. Removed from both lists.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@akshaylive
akshaylive merged commit 03808c0 into main Sep 29, 2026
17 checks passed
@akshaylive
akshaylive deleted the akshaya/plugin_mount_fix branch September 29, 2026 21:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants