Skip to content

fix(amazonq): require workspace containment before opening chat card files - #2906

Draft
dungdong-aws wants to merge 3 commits into
Amazon-Q-Developer:feature/mcp-security-enchancefrom
dungdong-aws:fix/chat-file-click-containment
Draft

dungdong-aws wants to merge 3 commits into
Amazon-Q-Developer:feature/mcp-security-enchancefrom
dungdong-aws:fix/chat-file-click-containment

Conversation

@dungdong-aws

@dungdong-aws dungdong-aws commented Oct 5, 2026 •

Copy link
Copy Markdown
Contributor

Summary

onFileClicked opened chat file-list card paths with no containment check. Three paths reached showDocument unchecked:

  • the fsRead branch opened params.filePath directly;
  • the default branch opened params.fullPath directly;
  • #resolveAbsolutePath joined a relative filePath onto the workspace root, so a traversing relative path such as ../../etc/passwd escaped as well.

Card paths originate in model tool results and service findings. A manipulated response plus one user click opened any file the server process could read, and opening it fed the file to completion context and the workspace-context upload.

All three paths now go through #openIfAllowed, which canonicalizes the path with resolveCanonicalPath and reuses requiresPathAcceptance. A file inside a workspace folder, or one the user already approved for the card's tool in the current session, opens directly. Any other path shows a showMessageRequest warning with Open and Cancel before opening. The dialog leads with the specific reason the check returned, such as the sensitive-file warning, and names the canonical path, so an injected path is legible rather than a reflexive click. Choosing Open records the path for the card's tool in the session's approved paths, so a repeat click or a later tool use on the same path does not prompt again. Cancel or dismissal aborts the open.

The user prompts directory (~/.aws/amazonq/prompts) is allowed for .prompt.md files. #resolveAbsolutePath deliberately resolves prompt files there, and requiresPathAcceptance would otherwise refuse them both as out-of-workspace and because /.aws/ matches the sensitive-path patterns.

Intentional behavior changes

  • Clicking a card for a file outside every workspace folder now shows a warning dialog instead of opening directly. An fsRead card for a path the user approved when the tool ran still opens without a dialog; an approval granted to fsWrite does not unlock an fsRead card for the same path, so that click prompts.
  • Choosing Open in the dialog is remembered for that path and tool for the rest of the session, consistent with how the filesystem tools record approvals.
  • Relative card paths are resolved and then containment-checked, so a path that resolves outside the workspace prompts rather than opening directly.
  • Prompt files in the user prompts directory continue to open. Other files placed in that directory are refused.
  • A declined or dismissed dialog is logged at info and produces no further UI.

Tests and verification

Twelve onFileClicked cases using a real temporary directory as the workspace folder, since the containment check canonicalizes folders through the filesystem. showMessageRequest is stubbed to Cancel by default, so each refusal case means refused when the user declines: an inside file via fullPath opens with no dialog; an outside fullPath is refused; a relative filePath that traverses out of the workspace is refused; an fsRead card for an unapproved outside path is refused; the same card after the user approved the path for fsRead opens with no dialog; the same card when the path was approved only for fsWrite prompts and is refused; choosing Open shows a Warning-typed dialog naming the exact path with Open and Cancel in that order and then opens the file; dismissing the dialog without a choice refuses; an Open choice is remembered so a second click does not prompt again; a sensitive out-of-workspace path surfaces the sensitive-file reason in the dialog; a .prompt.md in the prompts directory opens with no dialog; and a non-prompt file placed in the prompts directory is refused.

The gate decision was also exercised directly outside the Mocha harness with a real temporary workspace: inside file opens; outside file refused; traversing relative path refused; outside file approved for fsRead opens; outside file approved only for fsWrite refused.

Prettier passed on both changed files. ESLint reported no errors; remaining warnings are pre-existing import/no-nodejs-modules.

Verification limits

tsc --noEmit passes for the package, including both changed files. The Mocha suite was not run locally: the authoring host enforces memory-capped test invocation through a cgroup scope that the sandboxed session could not create, so it was refused rather than run uncapped. CI is the first execution of the added tests.

The prompt-directory tests stub os.homedir. getUserPromptsDirectory reads it at call time, so this works, but it is the most environment-sensitive part of the test and the most likely place for a CI-only failure.

Not verified in a running IDE.

Not changed

  • openFileDiff for fsWrite and fsReplace cards still takes toolUse.input.path unchecked. That path was already subject to the write tool's own approval when the tool ran, and the diff shows content the tool already produced rather than reading a new file, so it is a different exposure and is left for a separate change if wanted.

…files

onFileClicked passed card paths to showDocument with no containment
check. The fsRead branch opened params.filePath directly, the default
branch opened params.fullPath directly, and resolveAbsolutePath joined a
relative filePath onto the workspace root, so a traversing relative path
escaped as well. Card paths originate in model tool results and service
findings, so a manipulated response plus one click opened any readable
file and fed it to completion context.

All three open paths now go through openIfAllowed, which canonicalizes
the path and reuses requiresPathAcceptance: a file opens only when it is
inside a workspace folder or the user already approved it for the card's
tool in this session. The user prompts directory is allowed for
.prompt.md files, which resolveAbsolutePath deliberately resolves there.
Refused clicks are logged and otherwise ignored.

Tests cover an inside file, an outside fullPath, an escaping relative
path, an fsRead card for an unapproved and an approved outside path, an
fsWrite approval not unlocking an fsRead card, a prompt file in the
prompts directory, and a non-prompt file in that directory.
@codecov-commenter

codecov-commenter commented Oct 5, 2026 •

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

❌ Patch coverage is 94.64286% with 3 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
...nguage-server/agenticChat/agenticChatController.ts 96.15% 2 Missing ⚠️
...rc/language-server/agenticChat/tools/toolShared.ts 75.00% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

The previous commit refused such opens outright and only logged. A card
can legitimately point outside the workspace, for example a findings
path the user wants to inspect, so the open is now gated by a warning
dialog instead of dropped.

openIfAllowed shows a showMessageRequest warning with Open and Cancel
when requiresPathAcceptance requires acceptance. The message leads with
the specific reason the check returned, such as the sensitive-file
warning, and names the canonical path. Choosing Open records the path
for the card's tool in the session's approved paths, so a repeat click
or a later tool use on the same path does not prompt again. Cancel or
dismissal aborts the open. In-workspace, already-approved, and prompt
directory paths open without a dialog as before.

MessageType and MessageActionItem are imported from the protocol module,
which already supplied MessageType to this file; a second import from
server-interface produced a duplicate identifier.

Tests cover the dialog contents and action order, Open opening the file,
Cancel and dismissal refusing, the choice being remembered across
clicks, the sensitive-path reason surfacing in the dialog, and existing
approvals suppressing the dialog.
Match single backslash separators consistently with session approval storage.
Normalize Windows paths before sensitive-location pattern checks.
Add session round-trip tests for drive and UNC paths and use native separators
in the sensitive-path fixture.
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