Repository navigation
--deny-tool="read(...)" blocks ALL file reads regardless of pattern; no persistent permission profiles for non-interactive mode #2722
Description
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
- Run copilot in non-interactive mode, allowing reads to the entire project but denying reads to
.envfiles only:
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- 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*)`
- 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
- Run copilot with
read()deny for.env:
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- 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
--deny-tool="read(.env*)"should deny reads to.envfiles only, not ALL files.- File-level deny should apply across all tools - if
.envis denied,shell(cat .env)should also be denied. - A persistent settings file (e.g.,
~/.copilot/settings.jsonor.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):
~/.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):
~/.gemini/policies/*.toml(TOML-based policy files)- Priority system (0-999),
argsPatternregex matching on tool args - Workspace + user + admin tiers with deterministic precedence
Codex CLI (config reference docs):
.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_rootstoken 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
- Comprehensive Permissions System Improvements Proposal #307 (Comprehensive Permissions System Improvements Proposal, closed)
Activity
patniko commentedon Oct 26, 2026
Thanks for the report. We've shipped fixes in this area since this was filed (most recently in Copilot CLI v1.0.85), and we believe this may now be resolved. Could you retest on the latest release (Copilot CLI v1.0.103) and let us know whether you still see it? If we don't hear back in a couple of weeks, we may close this issue — you can always reopen it.
Triaged as part of a Copilot SDK backlog review, with help from GitHub Copilot.