fix(a2a): allow launcher workspace outside home - #28888
fix(a2a): allow launcher workspace outside home#28888sylvesterkaczmarek wants to merge 2 commits into
Conversation
Summary of ChangesHello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request addresses an issue where the A2A server incorrectly restricted workspace access to the user's home directory, even when a specific workspace path was provided by the launcher. By adjusting the default confinement root logic, the server now correctly supports workspaces located on different drives or outside the standard user profile, while preserving existing security controls for per-task paths. Highlights
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize the Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counterproductive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here. Footnotes
|
|
📊 PR Size: size/M
|
There was a problem hiding this comment.
Code Review
This pull request updates the workspace confinement logic in 'packages/a2a-server' to use a launcher-pinned workspace path as the default confinement root when configured, and introduces a new test suite to verify this behavior. The review feedback correctly identifies a confinement issue where 'defaultAllowedRoot' should resolve to the real path of 'configuredWorkspacePath' rather than 'resolvedPath'. Additionally, the feedback points out several violations of the repository style guide in the test file, recommending the use of Vitest's 'vi.stubEnv' and 'vi.unstubAllEnvs()' instead of directly modifying 'process.env'.
| const defaultAllowedRoot = configuredWorkspacePath | ||
| ? resolvedPath | ||
| : isTestEnv | ||
| ? path.parse(resolvedPath).root | ||
| : homedir(); |
There was a problem hiding this comment.
If targetDir is allowed to be the client-provided agentSettings.workspacePath, resolvedPath will point to that subdirectory. Therefore, defaultAllowedRoot should be resolved to the real path of configuredWorkspacePath rather than resolvedPath to correctly confine the client to the pinned workspace root.
| const defaultAllowedRoot = configuredWorkspacePath | |
| ? resolvedPath | |
| : isTestEnv | |
| ? path.parse(resolvedPath).root | |
| : homedir(); | |
| const defaultAllowedRoot = configuredWorkspacePath | |
| ? resolveToRealPath(configuredWorkspacePath) | |
| : isTestEnv | |
| ? path.parse(resolvedPath).root | |
| : homedir(); |
References
- Ensure consistent path resolution by using a single, robust function (e.g.,
resolveToRealPath) for all related path validations, including internal validations in components likeWorkspaceContext.
| let originalArgv: string[]; | ||
| let originalWorkspacePath: string | undefined; | ||
| let originalAllowedRoot: string | undefined; |
There was a problem hiding this comment.
These variables are no longer needed once we refactor the test environment setup to use vi.stubEnv and vi.unstubAllEnvs() as per the repository style guide.
| let originalArgv: string[]; | |
| let originalWorkspacePath: string | undefined; | |
| let originalAllowedRoot: string | undefined; | |
| let originalArgv: string[]; |
| originalWorkspacePath = process.env['CODER_AGENT_WORKSPACE_PATH']; | ||
| originalAllowedRoot = process.env['CODER_AGENT_ALLOWED_ROOT']; | ||
| delete process.env['CODER_AGENT_WORKSPACE_PATH']; | ||
| delete process.env['CODER_AGENT_ALLOWED_ROOT']; | ||
| }); | ||
|
|
||
| afterEach(() => { | ||
| vi.unstubAllEnvs(); | ||
| process.argv = originalArgv; | ||
|
|
||
| if (originalWorkspacePath === undefined) { | ||
| delete process.env['CODER_AGENT_WORKSPACE_PATH']; | ||
| } else { | ||
| process.env['CODER_AGENT_WORKSPACE_PATH'] = originalWorkspacePath; | ||
| } | ||
| if (originalAllowedRoot === undefined) { | ||
| delete process.env['CODER_AGENT_ALLOWED_ROOT']; | ||
| } else { | ||
| process.env['CODER_AGENT_ALLOWED_ROOT'] = originalAllowedRoot; | ||
| } | ||
|
|
||
| fs.rmSync(workspaceDir, { recursive: true, force: true }); | ||
| fs.rmSync(homeDir, { recursive: true, force: true }); | ||
| }); |
There was a problem hiding this comment.
Directly modifying and deleting properties on process.env violates the repository style guide. Use vi.stubEnv and vi.unstubAllEnvs() instead to prevent test leakage and ensure reliable test environment setup.
vi.stubEnv('CODER_AGENT_WORKSPACE_PATH', '');
vi.stubEnv('CODER_AGENT_ALLOWED_ROOT', '');
});
afterEach(() => {
vi.unstubAllEnvs();
process.argv = originalArgv;
fs.rmSync(workspaceDir, { recursive: true, force: true });
fs.rmSync(homeDir, { recursive: true, force: true });
});References
- When testing code that depends on environment variables, use
vi.stubEnv('NAME', 'value')inbeforeEachandvi.unstubAllEnvs()inafterEach. Avoid modifyingprocess.envdirectly as it can lead to test leakage and is less reliable. To "unset" a variable, use an empty stringvi.stubEnv('NAME', ''). (link)
| }); | ||
|
|
||
| it('allows a launcher-provided workspace outside the home directory', async () => { | ||
| process.env['CODER_AGENT_WORKSPACE_PATH'] = workspaceDir; |
There was a problem hiding this comment.
Use vi.stubEnv instead of directly modifying process.env to adhere to the repository style guide.
| process.env['CODER_AGENT_WORKSPACE_PATH'] = workspaceDir; | |
| vi.stubEnv('CODER_AGENT_WORKSPACE_PATH', workspaceDir); |
References
- When testing code that depends on environment variables, use
vi.stubEnv('NAME', 'value')inbeforeEachandvi.unstubAllEnvs()inafterEach. Avoid modifyingprocess.envdirectly as it can lead to test leakage and is less reliable. To "unset" a variable, use an empty stringvi.stubEnv('NAME', ''). (link)
| process.env['CODER_AGENT_WORKSPACE_PATH'] = workspaceDir; | ||
| process.env['CODER_AGENT_ALLOWED_ROOT'] = homeDir; |
There was a problem hiding this comment.
Use vi.stubEnv instead of directly modifying process.env to adhere to the repository style guide.
| process.env['CODER_AGENT_WORKSPACE_PATH'] = workspaceDir; | |
| process.env['CODER_AGENT_ALLOWED_ROOT'] = homeDir; | |
| vi.stubEnv('CODER_AGENT_WORKSPACE_PATH', workspaceDir); | |
| vi.stubEnv('CODER_AGENT_ALLOWED_ROOT', homeDir); |
References
- When testing code that depends on environment variables, use
vi.stubEnv('NAME', 'value')inbeforeEachandvi.unstubAllEnvs()inafterEach. Avoid modifyingprocess.envdirectly as it can lead to test leakage and is less reliable. To "unset" a variable, use an empty stringvi.stubEnv('NAME', ''). (link)
Fixes #28782
Summary
CODER_AGENT_WORKSPACE_PATHas the default confinement rootCODER_AGENT_ALLOWED_ROOTWhy
The A2A server currently defaults its allowed root to the user's home directory even when the launcher has already pinned the server to a specific workspace. That makes trusted IDE workspaces outside
%USERPROFILE%fail during startup.Using the launcher-provided workspace as the default confinement root fixes workspaces on other drives or outside the user profile without broadening the fallback boundary for request-supplied per-task paths.
Validation
Added focused tests in
packages/a2a-server/src/config/workspace-root.test.ts.Opening as a draft because #28782 is still awaiting maintainer triage.