github / github/copilot-cli

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

未关闭
#2,722 0 条评论 1 个 reaction 已指派 0 人 在 GitHub 查看
area:non-interactive area:permissions area:tools
主要语言
Shell
星标
11.2k
派生
1.9k
平均合并
14 小时 16 分钟
30 天内合并 PR
6

描述

### 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)

贡献指南

打开贡献指南

调研方向

首先复现 issue 中的三个命令行案例,对比 read() 的模式匹配与通过 shell(cat/sed) 访问的行为,以及重复的权限标志。在确定范围之前,审查 #307 中相关的权限提案和现有的基于 wrapper-script 的变通方案。完成的标准是:路径模式能够有选择地拒绝匹配的文件,文件限制适用于所有工具,并且权限配置可以在非交互式调用中重复使用。

由索引模型根据 Issue 内容生成。

评估

技术栈
shell
领域
cli, security
Issue 类型
缺陷
难度
5/5
预计耗时
一周以上
活跃度
冷清
描述清晰度
基本清楚
新手友好度
30/100

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。