fix(worker): scope the index worker to the request's repository, not the daemon's environment - #2076
Open
DeusData wants to merge 2 commits into
Open
fix(worker): scope the index worker to the request's repository, not the daemon's environment#2076DeusData wants to merge 2 commits into
DeusData wants to merge 2 commits into
Conversation
…the daemon's environment
The daemon-spawned index worker inherited the daemon process environment and
re-ran the allowed-root check without a session policy. The daemon's session
policy is installed only in application.c (the sole production caller of
cbm_mcp_server_set_session_context); after the daemon admits an
index_repository request, index_supervisor.c builds the worker argv
(`cli --index-worker --index-worker-build <fp> index_repository <args_json>
--response-out ...`, no root anywhere) and subprocess.c spawns it with the
daemon's environ. The worker then creates its own server with
cbm_mcp_server_new(NULL), so handle_index_repository runs with no session
policy and falls back to getenv("CBM_ALLOWED_ROOT") - the environment of
whoever STARTED the daemon (bootstrap.c spawns it with the starter's environ).
Observed: a daemon started with CBM_ALLOWED_ROOT=<rootA>; a client session for
<rootB>/tiny is admitted by the daemon (index.supervisor.reap outcome=clean
exit_code=0) and then refused by the worker with "<rootB>/tiny is outside the
allowed root. To allow it, run: codebase-memory-mcp allow-root ...". Any daemon
whose starter had CBM_ALLOWED_ROOT set broke every other session outside it.
Fix: in the worker arm of run_cli, right after the worker's server is created
and before any tool runs, read repo_path from the request args (both daemon
spawn paths rewrite the worker args with the canonical repo_path), canonicalize
it and install it explicitly as both session root and allowed root. The daemon
already authorized the request; the worker only executes it. A repository that
cannot be canonicalized does not exist and can only have been admitted by a
session with no declared boundary (containment needs a real path), so the
worker mirrors that with an explicit unrestricted policy - still never the
environment fallback - and the pipeline keeps reporting the missing repository
as the tool error it always was. A request without repo_path fails closed with
"request workspace scope invalid". No argv or IPC change, no process
environment mutation.
Regression test: tests/test_worker_session_scope.sh drives the real binary as a
supervised worker with CBM_ALLOWED_ROOT=<rootA> and repo_path=<rootB>/tiny and
requires an indexed response with no boundary refusal; a worker without
repo_path must exit nonzero on the scope check. Wired as Step 5f of
scripts/test.sh next to the other real-binary worker contracts. RED on main
(the exact "outside the allowed root" refusal above), GREEN with this change.
Distilled from #1925 with co-author credit.
Verification: test-runner suites index_supervisor, daemon_application, daemon,
mcp, cli green; tests/test_worker_error_response.sh, test_worker_watchdog.sh
and test_parent_watchdog.sh green against the fixed binary; make lint-ci and
scripts/check-no-test-skips.sh clean.
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Co-authored-by: Zhiyu <zhiyuzhang001@gmail.com>
This was referenced Sep 7, 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.
Defect
The daemon-spawned index worker inherited the daemon process environment and re-ran the allowed-root check without a session policy.
Trace on
main: the daemon's session policy is installed only insrc/daemon/application.c(the sole production caller ofcbm_mcp_server_set_session_context). After the daemon admits anindex_repositoryrequest,src/mcp/index_supervisor.cbuilds the worker argv (cli --index-worker --index-worker-build <fp> index_repository <args_json> --response-out ...- no root anywhere) andsrc/foundation/subprocess.cspawns it with the daemon'senviron. The worker creates its own server withcbm_mcp_server_new(NULL), sohandle_index_repositoryruns withallowed_root_policy_set=falseand falls back togetenv("CBM_ALLOWED_ROOT")- the environment of whoever started the daemon (bootstrap.cspawns it with the starter'senviron) - andworkspace.crefuses containment.Observed: daemon started with
CBM_ALLOWED_ROOT=<rootA>; a client session for<rootB>/tinyis admitted by the daemon (index.supervisor.reap outcome=clean exit_code=0) and then refused by the worker:Any daemon whose starter had
CBM_ALLOWED_ROOTset broke every other session outside it.Fix
In the worker arm of
run_cli(src/main.c), right after the worker's server is created and before any tool runs, readrepo_pathfrom the request args (both daemon spawn paths rewrite the worker args with the canonicalrepo_pathviaindex_args_with_repo_path), canonicalize it and install it explicitly as both session root and allowed root. The daemon already authorized the request; the worker only executes it.tests/test_worker_error_response.shgreen).repo_pathfails closed:error: request workspace scope invalid: request names no repo_path, non-zero exit, no response file.Distilled from #1925 with co-author credit to @zhiyuzhang001-a11y (only the defect fix; nothing from the feature). Refs #1925.
Regression test
tests/test_worker_session_scope.sh(Step 5f inscripts/test.sh, mirrored ontest_worker_error_response.sh) drives the real binary as a supervised worker withCBM_ALLOWED_ROOT=<rootA>andrepo_path=<rootB>/tinyand requires a"status":"indexed"response with no boundary refusal; a second worker withoutrepo_pathmust exit non-zero on the scope check.main(same build,main.creverted): the worker delivered the exact refusal above (isError:true).Verification (macOS, final tree)
build/c/test-runner index_supervisor daemon_application daemon- 72 passedbuild/c/test-runner mcp cli- 618 passed, 4SKIP_PLATFORM(Windows-only, pre-existing)tests/test_worker_error_response.sh,tests/test_worker_watchdog.sh,tests/test_parent_watchdog.sh- green against the fixed binarymake -f Makefile.cbm lint-ci- passed;scripts/check-no-test-skips.sh- OK;tests/test_script_exec_bit_contract.sh- PASSfopen(, ASCII-only additions, noSKIP()/NOLINTNote for the queue: this touches
scripts/test.sh(CI script) only to add the Step 5f invocation.