agent_sidecar: confine the workspace a request may name - #2
Open
egeboy35 wants to merge 1 commit into
Open
Conversation
egeboy35
force-pushed
the
fix/sidecar-confines-workspace
branch
from
September 1, 2026 12:28
6b463b6 to
4748ca9
Compare
The sidecar's README documents AGENT_WORKSPACE as "Project dir the agent
reads/edits". The request decides otherwise:
root = os.path.abspath(body.get("root") or DEFAULT_ROOT) # :150
There is no allowlist and no check that `root` sits under DEFAULT_ROOT, so
whatever directory the body names becomes the agent's project root, and
`edge_agent.Tools` confines it faithfully to *that*. The service listens on
0.0.0.0 with no authentication, and `edgeLLM/nextjs-nemotron-app/app/api/
agent/route.js:75` forwards `body.root` verbatim from the browser.
Measured against the real app (httpx ASGI transport; only `_make_complete`
stubbed, because reaching the model needs a live endpoint and a key -- the
canned ReAct text it returns is what a prompt-injected model could return):
POST /run {"task": "...", "root": "<outside the workspace>"}
-> HTTP 200
[start] root accepted: <outside the workspace>
[observation] 1 PRIVATE-KEY-MATERIAL <- read_file
[observation] wrote 18 bytes to PWNED.txt <- write_file
the file now exists outside the workspace: True
So the read is not the whole of it: write_file and edit_file are in the same
tool set, which makes this write access to anything the sidecar's uid can
reach.
This adds AGENT_ALLOWED_ROOTS, os.pathsep-separated, defaulting to the
workspace alone -- which is the guarantee AGENT_WORKSPACE already implies. A
request may name an allowed root or anything beneath it; anything else gets
403 with a message naming the variable to widen. Comparison is on realpath,
so a link inside an allowed root cannot name a target outside it, and
/health reports the list.
The multi-project workflow the `root` field exists for is unchanged: point
AGENT_ALLOWED_ROOTS at the parent of your checkouts and every one of them
stays selectable.
What this does NOT change: the service still binds 0.0.0.0 and still has no
auth. `docs/curriculum/11b_nextjs_agent_lab.md:526` advertises
`http://<jetson>:8002/docs` as a way in, so the bind looks deliberate and I
did not want to break a documented path on my own judgement. Worth deciding
separately -- with the root confined, an unauthenticated caller can no longer
pick the target, which is the part that made the exposure serious.
Adds agent_sidecar/tests/test_workspace_confinement.py -- 13 tests, no
hardware, no network, no model. Against this branch: 13 passed. Against the
file as it stands on main: 10 failed, 3 passed, including
FAILED test_post_run_refuses_an_outside_root_with_403
FAILED test_post_run_does_not_touch_a_file_outside_the_root
The 3 that pass either way are "the default workspace still works", "a
subdirectory of it still works" and "a missing task is still a 400".
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
egeboy35
force-pushed
the
fix/sidecar-confines-workspace
branch
from
September 1, 2026 12:43
4748ca9 to
2a70281
Compare
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.
The sidecar's README documents
AGENT_WORKSPACEas "Project dir the agent reads/edits". The request decides otherwise —agent_sidecar.py:150:No allowlist, no check that
rootsits underDEFAULT_ROOT. Whatever directory the body names becomes the agent's project root, andedge_agent.Toolsthen confines it faithfully to that. The service listens on0.0.0.0with no authentication, andedgeLLM/nextjs-nemotron-app/app/api/agent/route.js:75forwardsbody.rootverbatim from the browser.Measured
Against the real app through httpx's ASGI transport — real routing, real body parsing, real
edge_agent.Tools. The only substitution is_make_complete, because reaching the model needs a live endpoint and a key; the canned ReAct text it returns is what a prompt-injected model could return anyway.write_fileandedit_fileare in the same tool set, so this is write access to anything the sidecar's uid can reach.The change
Adds
AGENT_ALLOWED_ROOTS,os.pathsep-separated, defaulting to the workspace alone — which is the guaranteeAGENT_WORKSPACEalready implies. A request may name an allowed root or anything beneath it; anything else gets 403 with a message naming the variable to widen. Comparison is on realpath, so a link inside an allowed root cannot name a target outside it./healthreports the list.The multi-project workflow the
rootfield exists for is unchanged: pointAGENT_ALLOWED_ROOTSat the parent of your checkouts and every one stays selectable.What this deliberately does not change
The service still binds
0.0.0.0and still has no auth.docs/curriculum/11b_nextjs_agent_lab.md:526advertiseshttp://<jetson>:8002/docsas a way in, so the bind looks deliberate and I did not want to break a documented path on my own judgement. Worth deciding separately — with the root confined, an unauthenticated caller can no longer choose the target, which is the part that made the exposure serious.Tests
Adds
agent_sidecar/tests/test_workspace_confinement.py— 13 tests, no hardware, no network, no model.Against this branch: 13 passed. Against the file as it stands on
main: 10 failed, 3 passed, includingThe 3 that pass either way are the default workspace still works, a subdirectory of it still works, and a missing task is still a 400.
pip install fastapi httpx pytest && pytest edgeLLM/nextjs-nemotron-app/agent_sidecar/tests