Skip to content

Quoted strings that happen to start with / are misinterpreted as paths the agent intends to read or write #3339

@diamond-lizard

Description

@diamond-lizard

Describe the bug

Description

The GitHub Copilot CLI's path-access scanner misclassifies quoted shell-command arguments that happen to start with / as filesystem paths the action intends to read or write, even when the receiving command is a no-op or treats the argument as literal data.

The scanner inspects bash-tool command text before execution. In the tested forms, a quoted argument whose first character is / triggers an interactive "Allow directory access" permission prompt, presenting the argument as a candidate directory the action may read or write.

The trigger appears to depend on the token's leading character (a forward slash) rather than on whether the receiving command opens the token as a filesystem path, and rather than on whether the token names an existing directory. Strings such as /)) (which does not name an existing directory and is not accessed by the receiving command) and /tmp is a sentence (whose /tmp prefix is allowlisted in the bare unquoted form but is not protected once the quoted argument is extended with non-path characters) both trigger the prompt.

The scanner appears to treat echo and the tested awk program-argument form differently from other tested commands: those pass, while most other tested commands trigger the prompt, including no-ops such as true and :, and data-only commands such as printf.

This is closely related to #774, which describes a similar lexical-scanning failure mode rejecting command-like words in text arguments passed to non-bash tools. The present report covers a complementary case within the bash-tool path: arguments are scanned by token shape rather than by whether the receiving command would actually open them.

Steps to Reproduce

  1. Invoke one of the tested non-exempt commands, such as true, :, or printf, with a quoted argument whose first character is a forward slash (/). In the tested cases, the remainder of the string varied widely — from an allowlisted-prefix extension like /tmp is a sentence, to a path-shaped sensitive substring like /etc/passwd, to a string that does not name any directory in the tested environment like /)) — and the prompt fired in every case.
  2. The string need not be a valid path; it need not exist on disk; the command need not access files.
  3. The scanner intercepts the call before bash runs with an "Allow directory access" permission prompt that defaults to "Yes, and add these directories to the allowed list".

Example

Quoted string starting with / passed to true

true '/))'

The CLI displays this interactive prompt before executing:

╭─ Allow directory access ─────────────────────────────────────────╮
│                                                                  │
│ This action may read or write the following path outside your    │
│ allowed directory list.                                          │
│                                                                  │
│ ╭──────────────────────────────────────────────────────────────╮ │
│ │ /))                                                          │ │
│ ╰──────────────────────────────────────────────────────────────╯ │
│                                                                  │
│ Do you want to allow this?                                       │
│                                                                  │
│   1. Yes                                                         │
│ ❯ 2. Yes, and add these directories to the allowed list          │
│   3. No (Esc)                                                    │
│                                                                  │
│ ↑↓ to navigate · Enter to select · Esc to cancel                 │
╰──────────────────────────────────────────────────────────────────╯

true is the universal POSIX no-op: it ignores every argument and returns success. The argument /)) does not name an existing directory in the tested environment, is not sensitive-looking, and is not accessed by true — it is simply a string that happens to start with /. Yet the prompt's title — "Allow directory access" — and the candidate text shown in its own bordered box indicate the CLI is treating /)) as a filesystem path the action may read or write. The default option ("Yes, and add these directories to the allowed list") would proceed with the call; based on that option's label, I would expect accepting it to also persist /)) in the allowed-directory list.

Quoted argument that begins with an allowlisted bare token (/tmp)

true '/tmp is a sentence'

Same prompt, with /tmp is a sentence shown as the candidate directory. This is striking because the unquoted variant true /tmp passes (see Contrast table below): the allowlist that protects /tmp does not carry through to a quoted argument whose first character is /, even when the quoted content starts with the same allowlisted prefix.

Quoted path-shaped string passed to the null builtin

: '/tmp is a noop arg'

Same prompt, with /tmp is a noop arg shown as the candidate directory. : is the shell null command and never inspects its arguments.

Argument to printf

printf '%s\n' '/tmp is a noop arg'

Same prompt, with the quoted argument shown as the candidate directory. printf treats this argument as data substituted into the format string, not as a path to read. A separately tested format-string case, printf '/tmp is a noop arg\n', also triggers the same prompt, so the observed scan is not limited to printf's data arguments; both tested positions are subject to it.

command builtin does not change the outcome

command true '/tmp is a sentence'

Same prompt, with the quoted argument shown as the candidate directory. Wrapping the command with the command builtin does not change the scanner's classification.

Contrast: tested forms that the scanner passes

Form Result
echo '/etc/passwd is a sentence' passes
echo /etc/passwd (unquoted) passes
echo '/var/log was the locus' passes
awk '/error/ {print}' (the awk regex starts with /) passes
true /foo passes
true /tmp passes
true /xyz passes

The scanner clearly recognizes single-quoting (it extracts the full quoted content as a single candidate path) and appears to treat echo and the tested awk form differently from other commands: echo arguments pass in both quoted and unquoted forms, and the tested awk program-argument form passes even when its first character is /. Comparable arguments to true, :, and printf trigger the permission prompt. Additionally, the tested forms true /foo, true /tmp, and true /xyz pass even though the receiving command is true — which does trigger the prompt for other tested arguments. What is not modeled in the tested forms is the receiving command's semantics: true, :, and printf never open files at the named path in the tested forms, yet their arguments are still subject to the path-access rule. Conversely, a quoted argument whose first character is not / does not trigger the prompt even when / appears later in the token: true '(/ 10 2)' passes, supporting the inference that the trigger condition is the token's leading character rather than the presence of / anywhere in the token.

Asymmetry: commands that pass vs. true

echo '/etc/passwd is a sentence' passes but true '/etc/passwd is a sentence' triggers the permission prompt. Similarly, awk '/error/ {print}' passes — the awk regex argument's leading / is not flagged — but the same content passed to true (true '/error/ {print}') does trigger the permission prompt. The tested commands have the same filesystem-access semantics with respect to these arguments: none of them opens the argument as a file. The asymmetry suggests command-specific treatment for echo and the tested awk form rather than a general rule about whether the argument can be opened as a path.

I have not exhaustively tested every command, every leading-/ argument shape, every allowlisted path, or every argument position. The claims here are limited to the tested contrast cases above and to the reproducible permission prompts.

Expected Behavior

A semantics-aware path-access guard would:

  • Distinguish between commands that use arguments as filesystem paths (e.g., cat, mv, cp, chmod) and commands that do not open the tested argument positions as paths (e.g., true, :, printf, and echo).
  • Apply the path-access rule only to arguments at positions where the receiving command will treat the argument as a filesystem path.
  • Treat literal text data passed to data-only commands as data, not as path-access claims.

Actual Behavior

The observed behavior is consistent with the scanner inspecting quoted leading-/ arguments for the tested non-exempt commands and treating them as candidate directory-access strings without distinguishing:

  • Whether the receiving command opens arguments as filesystem paths.
  • Whether any argument position of the receiving command can open filesystem paths; in the tested printf cases, both format-string and data positions are scanned even though neither is opened as a path.
  • Whether the bytes name an existing directory or just happen to have a leading /.

Within the tested forms, the scanner appears to recognize quoting (it extracts the entire quoted content as a single candidate path) and to pass arguments to echo and the tested awk form, but it does not pass comparable arguments to other tested commands or argument positions.

Impact

  • Requires interactive approval for string operations that do not access files: print operations such as printf '%s' '/tmp is a noop arg' trigger the permission prompt; no-op operations such as true '/tmp was the locus' trigger the permission prompt.
  • Interrupts legitimate agent actions before execution: the agent can choose a command whose runtime behavior would not open the named path, but the pre-execution scanner still intercepts the action with a permission prompt based on the argument text alone.
  • May pollute the allowed-directory list if the default option is accepted: the prompt's default option ("Yes, and add these directories to the allowed list") is labeled as adding the candidate string to the allowed list; based on that label, I would expect accepting it to persist non-path strings such as /tmp is a sentence as registered allowed directories. A user who casually accepts the default could then accumulate arbitrary English-prose fragments in their allowed-list configuration.
  • Can interrupt OS-development conversations involving examples or prose that begin with /, similar to the impact described in Bug: CLI intercepts command names in text responses and attempts to execute them #774 for the session tool.
  • Requires indirect workarounds: routing all data through echo or the tested awk program-argument form, splitting strings to avoid a leading /, paraphrasing prose, or piping through unrelated transformations to obscure the leading slash.

Environment

  • GitHub Copilot CLI version: 1.0.48
  • OS: Linux
  • Use case: passing literal strings (English prose, JSON data, command examples) that incidentally begin with system-path-like substrings.

Suggested Fix

One narrow fix would be to make the scanner command-semantics-aware:

  1. Maintain a per-command path-argument-position table for common commands.
  2. Apply the same observed behavior of passing echo arguments and the tested awk program-argument form to other known data-only / no-op commands such as true, :, and printf.
  3. For commands that do have path-bearing argument positions, such as cat, cp, mv, or chmod, inspect only the positions that the command can actually use as filesystem paths.

A runtime filesystem-access signal, where available, could complement these lexical checks. A narrower available fix is to avoid applying path-access rules to known data-only argument positions.

Note: the scanner already passes arguments to echo and the tested awk form, and it passes the tested forms true /foo, true /tmp, and true /xyz. The fix is to extend comparable behavior to other data-only commands (true, :, printf) and to no-op builtins generally.

Workaround

  • Route data through echo — or through the tested awk program-argument form — instead of other commands when the data starts with /.
  • Avoid any quoted argument whose first character is /, where possible. (Often impossible when the data is determined by the user's request.)
  • Use heredoc redirection to a data-only command rather than quoted arguments. Note that heredoc-based workarounds may be affected by separate heredoc-body scanning behavior (Sentences containing the word kill in a heredoc body are misinterpreted as kill commands #3334), so this workaround is not always reliable.

Related


Note: Together with #774 and #3334, this report describes interception behavior across multiple contexts that follow a similar lexical-scanning failure mode. The scanner already passes echo arguments, the tested awk form, and the tested unquoted paths /foo, /tmp, and /xyz for true, so a change to extend command-semantics awareness to other commands could be scoped specifically to a per-command path-argument-position table.

Affected version

GitHub Copilot CLI 1.0.48.

Steps to reproduce the behavior

No response

Expected behavior

No response

Additional context

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:permissionsTool approval, security boundaries, sandbox mode, and directory restrictionsarea:toolsBuilt-in tools: file editing, shell, search, LSP, git, and tool call behavior

    Type

    No fields configured for Bug.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions