github / github/copilot-cli

`--deny-tool="read(...)"` blocks ALL file reads regardless of pattern; no persistent permission profiles for non-interactive mode

Abierto
#2,722 0 comentarios 1 reacción 0 asignados Ver en GitHub
area:non-interactive area:permissions area:tools
Lenguaje dominante
Shell
Estrellas
11.2k
Forks
1.9k
Merge medio
14 h 16 min
PR fusionados (30 d)
6

Descripción

### Describe the bug

Any `--deny-tool` pattern targeting `read()` blocks ALL `read()` tool calls, not just files matching the pattern. This makes it impossible to selectively deny reads to sensitive files (e.g., `.env`) while allowing reads to the rest of the project.

Even when the deny pattern is fixed, `read()` deny is bypassed trivially via `shell(cat .env)` or `shell(sed .env)` - there is no way to deny file access at the filesystem level.

Finally, without persistent permission profiles (#307), every non-interactive invocation requires 10+ CLI flags. All three competing AI coding CLIs (Claude Code, Gemini CLI, Codex CLI) provide file-based permission configuration.

### Affected version

GitHub Copilot CLI 1.0.27.

### Steps to reproduce the behavior

### Bug 1: `read()` deny blocks ALL reads

1. Run copilot in non-interactive mode, allowing reads to the entire project but denying reads to `.env` files only:

```bash
copilot -p "Read AGENTS.md and tell me the first heading" \
--available-tools="bash,view,rg,glob" \
--allow-tool="read($(pwd)/**)" \
--deny-tool="read($(pwd)/.env*)" \
--deny-tool=write
```

2. Observe that ALL `read()` calls are denied, not just `.env`:

```
Read AGENTS.md
Permission to run this tool was denied due the following rules:
`read(/home/user/my-project/.env*)`
```

3. Copilot falls back to `shell(sed ...)` to read files, wasting a turn.

We tested four patterns (both `$(pwd)` and absolute paths) - all produce the same result:

| Pattern | Result |
| ----------------------------------------------------- | ---------------- |
| `--deny-tool="read($(pwd)/.env*)"` | Blocks all reads |
| `--deny-tool="read($(pwd)/**/.env*)"` | Blocks all reads |
| `--deny-tool="read(.env*)"` | Blocks all reads |
| `--deny-tool="read(/absolute/path/to/project/.env*)"` | Blocks all reads |

### Bug 2: `read()` deny is trivially bypassed via shell

1. Run copilot with `read()` deny for `.env`:

```bash
copilot -p "What keys are in the .env file?" \
--available-tools="bash,view,rg,glob" \
--allow-tool="read($(pwd)/**)" \
--allow-tool="shell(git diff:*)" \
--deny-tool="read($(pwd)/.env*)" \
--deny-tool=write
```

2. Copilot's `read()` is denied, but it routes around via shell:

```
Read .env
Permission denied due to: read(.env*)

Read .env contents via shell
sed -n '1,20p' .env
1 line...

The .env file contains: [contents displayed]
```

The `read()` deny only blocks the `read()` tool. `shell(cat .env)`, `shell(sed .env)` etc. all work.

### Expected behavior

1. `--deny-tool="read(.env*)"` should deny reads to `.env` files only, not ALL files.
2. File-level deny should apply across all tools - if `.env` is denied, `shell(cat .env)` should also be denied.
3. A persistent settings file (e.g., `~/.copilot/settings.json` or `.copilot/config.toml`) should allow storing permission profiles so that 10+ flags per invocation are not needed.

### Additional context

### Competing CLI tools all solve this with settings files

**Claude Code** ([permissions docs](https://code.claude.com/docs/en/permissions)):

- `~/.claude/settings.json` + `.claude/settings.json` (project-level)
- JSON-based allow/deny/ask rules with glob patterns
- 4-level hierarchy: admin > user > project-local > project-shared
- deny > ask > allow evaluation order

**Gemini CLI** ([policy engine docs](https://geminicli.com/docs/reference/policy-engine/)):

- `~/.gemini/policies/*.toml` (TOML-based policy files)
- Priority system (0-999), `argsPattern` regex matching on tool args
- Workspace + user + admin tiers with deterministic precedence

**Codex CLI** ([config reference docs](https://developers.openai.com/codex/config-reference)):

- `.codex/config.toml` (TOML-based per-project config)
- Per-path filesystem permissions: `"/path" = "read"`, `"/path" = "none"`
- Named permission profiles, sandbox modes (`read-only`, `workspace-write`)
- `:project_roots` token for portable project-scoped access

### Host tool collision with deny-tool strings

When copilot is invoked from another AI coding tool (e.g., Claude Code), `--deny-tool="shell(rm:*)"` causes the host tool's own deny-list to block the entire command, because it pattern-matches the literal text `rm` in the Bash command string. This forces users to build wrapper scripts that hide the deny flags from the host's permission system.

### Workaround

We built a wrapper script that encapsulates all permission flags and is enforced by a validation hook. This works but is a band-aid.

### Related

- #307 (Comprehensive Permissions System Improvements Proposal, closed)

Guía de contribución

Abrir la guía de contribución

Evaluación

Este issue todavía no se ha evaluado.

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.