Skip to content

fix(a2a): allow launcher workspace outside home - #28888

Open
sylvesterkaczmarek wants to merge 2 commits into
google-gemini:mainfrom
sylvesterkaczmarek:fix/a2a-workspace-root-28782
Open

fix(a2a): allow launcher workspace outside home#28888
sylvesterkaczmarek wants to merge 2 commits into
google-gemini:mainfrom
sylvesterkaczmarek:fix/a2a-workspace-root-28782

Conversation

@sylvesterkaczmarek

Copy link
Copy Markdown

Fixes #28782

Summary

  • use a launcher-provided CODER_AGENT_WORKSPACE_PATH as the default confinement root
  • preserve an explicit CODER_AGENT_ALLOWED_ROOT
  • keep per-task workspace paths confined to the existing home-directory boundary when no launcher path is configured
  • add regression coverage for all three cases

Why

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.

@sylvesterkaczmarek
sylvesterkaczmarek requested a review from a team as a code owner August 18, 2026 16:42
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, 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

  • Launcher Workspace Confinement: Updated the A2A server to use the launcher-provided CODER_AGENT_WORKSPACE_PATH as the default confinement root, enabling support for workspaces located outside the user's home directory.
  • Explicit Configuration Preservation: Ensured that the CODER_AGENT_ALLOWED_ROOT environment variable continues to take precedence, maintaining existing security boundaries when explicitly configured.
  • Regression Testing: Added comprehensive test coverage in a new file to validate workspace confinement behavior across default, launcher-provided, and explicitly restricted scenarios.
Using Gemini Code Assist

The 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 /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

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 .gemini/ folder in the base of the repository. Detailed instructions can be found here.

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

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@github-actions github-actions Bot added the size/m A medium sized PR label Aug 18, 2026
@github-actions

Copy link
Copy Markdown

📊 PR Size: size/M

  • Lines changed: 116
  • Additions: +113
  • Deletions: -3
  • Files changed: 2

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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'.

Comment on lines +481 to +485
const defaultAllowedRoot = configuredWorkspacePath
? resolvedPath
: isTestEnv
? path.parse(resolvedPath).root
: homedir();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

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.

Suggested change
const defaultAllowedRoot = configuredWorkspacePath
? resolvedPath
: isTestEnv
? path.parse(resolvedPath).root
: homedir();
const defaultAllowedRoot = configuredWorkspacePath
? resolveToRealPath(configuredWorkspacePath)
: isTestEnv
? path.parse(resolvedPath).root
: homedir();
References
  1. Ensure consistent path resolution by using a single, robust function (e.g., resolveToRealPath) for all related path validations, including internal validations in components like WorkspaceContext.

Comment on lines +33 to +35
let originalArgv: string[];
let originalWorkspacePath: string | undefined;
let originalAllowedRoot: string | undefined;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

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.

Suggested change
let originalArgv: string[];
let originalWorkspacePath: string | undefined;
let originalAllowedRoot: string | undefined;
let originalArgv: string[];

Comment on lines +52 to +75
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 });
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

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
  1. When testing code that depends on environment variables, use vi.stubEnv('NAME', 'value') in beforeEach and vi.unstubAllEnvs() in afterEach. Avoid modifying process.env directly as it can lead to test leakage and is less reliable. To "unset" a variable, use an empty string vi.stubEnv('NAME', ''). (link)

});

it('allows a launcher-provided workspace outside the home directory', async () => {
process.env['CODER_AGENT_WORKSPACE_PATH'] = workspaceDir;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Use vi.stubEnv instead of directly modifying process.env to adhere to the repository style guide.

Suggested change
process.env['CODER_AGENT_WORKSPACE_PATH'] = workspaceDir;
vi.stubEnv('CODER_AGENT_WORKSPACE_PATH', workspaceDir);
References
  1. When testing code that depends on environment variables, use vi.stubEnv('NAME', 'value') in beforeEach and vi.unstubAllEnvs() in afterEach. Avoid modifying process.env directly as it can lead to test leakage and is less reliable. To "unset" a variable, use an empty string vi.stubEnv('NAME', ''). (link)

Comment on lines +86 to +87
process.env['CODER_AGENT_WORKSPACE_PATH'] = workspaceDir;
process.env['CODER_AGENT_ALLOWED_ROOT'] = homeDir;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Use vi.stubEnv instead of directly modifying process.env to adhere to the repository style guide.

Suggested change
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
  1. When testing code that depends on environment variables, use vi.stubEnv('NAME', 'value') in beforeEach and vi.unstubAllEnvs() in afterEach. Avoid modifying process.env directly as it can lead to test leakage and is less reliable. To "unset" a variable, use an empty string vi.stubEnv('NAME', ''). (link)

@gemini-cli gemini-cli Bot added the area/security Issues related to security label Aug 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/security Issues related to security size/m A medium sized PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Agent Mode fails with "Workspace path is outside the allowed root directory" for any workspace outside %USERPROFILE%

1 participant