Claude Code Tool Allowlists for CI/CD Automation
Claude Code tool allowlists let you define exactly which commands run automatically and which are blocked, using allow and deny rules stored in a settings.json file. For CI/CD pipelines with no human present, pair these rules with a non-interactive permission mode to eliminate approval prompts entirely. The result is a precise "safe corridor" where Claude can fix lint errors and run tests without stalling, while dangerous operations like dependency installs or credential reads are caught before execution.
What Are Claude Code Tool Allowlists?
Tool allowlists are lists of permitted (and blocked) tool invocations stored in a permissions object inside a settings.json file. Each entry is a string that names a tool and, optionally, an argument pattern — for example, Bash(npm run test) or Read(./.env). Rules live either at the project level (.claude/settings.json) or globally (~/.claude/settings.json), and project-level rules take precedence over global ones.
The system evaluates rules in a strict hierarchy: deny rules fire first and block unconditionally, then allow rules grant silent auto-approval, and anything not matched either prompts the user or is blocked depending on the active permission mode. This means you can safely allow a broad class of commands and then carve out specific exceptions in the deny list — the deny list always wins.
For a full reference on how rules are structured and merged, see the official Claude Code settings documentation.
What Permission Modes Are Available for Headless Pipelines?
Permission modes set the overall posture of a Claude Code session. There are four:
- default — asks before most tool uses. Fine for interactive development, unusable in a headless pipeline.
- acceptEdits — silently approves file read/write operations but still prompts for shell commands. Good for multi-file refactors where a human is watching.
- bypassPermissions — silently approves everything (deny rules still fire). Use this in fully isolated CI containers where no human is present.
- dontAsk — prevents all tool execution. Useful for read-only analysis jobs that should never modify files or run commands.
You can set the mode via the CLI flag --permission-mode, via the SDK's permissionMode option, or toggle it interactively during a session. For CI/CD, you will almost always set it at the CLI or SDK level so the pipeline never waits for input.
How Do You Configure Allowlists for a CI/CD Pipeline?
The steps below cover the most common headless automation pattern: allowing specific test and lint commands while blocking anything that touches secrets or modifies dependencies.
- Create
.claude/settings.jsonin your project root. This file travels with the repository, so every CI run and every developer gets the same rules automatically. - Add a
permissionsobject withallowanddenyarrays. Each entry is a tool-name string, optionally with a parenthesized argument pattern — for example,Bash(npm run test *). - Put anything that must never execute in the deny array. Deny rules take precedence over allow rules, so this is your hard safety net.
- Save the file. Claude Code reads it at session start and merges it with global settings.
- Pass a permission mode flag when invoking Claude Code in your pipeline. Use
--permission-mode bypassPermissionsfor full autonomous operation or--permission-mode dontAskfor read-only analysis. - Run
/permissionsin a local Claude Code terminal to view the active merged rule set and verify what is currently allowed or blocked before committing the config.
A concrete example for a Node.js project that auto-fixes lint errors and runs tests, but never installs packages or reads secrets:
{
"permissions": {
"allow": [
"Bash(npm run lint)",
"Bash(npm run test *)",
"Bash(npm run build)"
],
"deny": [
"Read(./.env)",
"Read(./.env.*)",
"Read(./secrets/**)",
"Bash(npm install)",
"Bash(npm install *)",
"Bash(npm uninstall *)"
]
}
}
With this config and --permission-mode bypassPermissions, Claude runs npm run test, reads the failure output, edits the source file, and re-runs — all without prompting. If it attempts npm install lodash, the deny rule blocks it and Claude reports that package installation requires manual approval.
For the SDK equivalent, pass permissionMode (TypeScript) or permission_mode (Python) in the query options. See the Claude Code SDK permissions reference for the exact parameter names.
What Is the Critical Pitfall That Freezes Headless Pipelines?
The most dangerous mistake in CI/CD setup is using --dangerously-skip-permissions in a headless environment. That flag triggers an interactive consent dialog on first run. A pipeline without a TTY will freeze indefinitely waiting for that confirmation — your job hangs silently until it times out.
Always use --permission-mode dontAsk or --permission-mode bypassPermissions in CI pipelines. These flags skip the consent dialog entirely and are the correct tools for non-interactive automation.
What Other Pitfalls Should You Know Before Going to Production?
Wildcard patterns don't match the way you expect
The asterisk in Bash(npm *) matches a single argument segment — it does not capture all npm subcommands universally. For maximum reliability, list each permitted command explicitly: Bash(npm run lint), Bash(npm run test *), Bash(npm run build). Also note that Bash(ls *) requires a space before an argument, so a command like lsof would not match — use Bash(ls*) (no space) to capture all commands starting with ls.
Broad allow rules are silently dropped in auto mode
Auto mode (available on Max plan) intentionally drops permission rules that grant arbitrary code execution — including blanket shell access like Bash(*), wildcarded script interpreters, and broad package-manager run rules. If you rely on these rules and then enable auto mode, your workflow will stall. Replace broad rules with specific ones before enabling auto mode.
Deny rules still fire in bypassPermissions mode
Deny rules are enforced even in bypassPermissions mode — if a deny rule matches, the tool is blocked. This is the behavior you want: your hard safety net remains intact regardless of the session posture. Reserve bypassPermissions for fully trusted, isolated environments such as locked-down CI containers.
Symlink bypass
The permission system evaluates both the symlink path and the physical target path. For an allow rule to approve an action, both paths must match the rule. For a deny rule, either path matching is sufficient to trigger a block. You cannot grant access to a symlink and assume the real file is also covered — you must explicitly allow the resolved target path as well.
When Should You Use Project-Level vs. Global Settings?
| Scenario | Use project-level .claude/settings.json |
Use global ~/.claude/settings.json |
|---|---|---|
| CI/CD pipeline rules | ✓ Checked into version control, consistent for all runners | Not suitable — not present on ephemeral CI containers |
| Team-wide repository policy | ✓ Everyone who clones the repo gets the same rules | Not suitable — each developer's global file differs |
| Personal safety defaults (block ~/.ssh, ~/.aws) | Not suitable — would need to repeat in every project | ✓ Applies across all projects on your machine |
| Exploratory local work | Optional — useful if the project has known risky paths | ✓ Global defaults cover you without per-project setup |
How Does This Compare to Just Using Interactive Approvals?
Interactive approvals (the default prompt behavior) are the right choice during exploratory sessions where you want full visibility and veto power over each action. They give you a chance to catch unexpected behavior before it happens. The tradeoff is that they are completely incompatible with headless automation — a CI job cannot click "approve."
Allowlists plus a non-interactive permission mode are the right choice when you need consistent, repeatable automation. The rules are version-controlled, auditable with /permissions, and enforced the same way on every run. The tradeoff is that you must think carefully upfront about what to allow and deny — a misconfigured allowlist can either block legitimate work or permit more than you intended.
For enterprise deployments, organization-wide ceilings can be configured so that individual role grants cannot exceed those ceilings, giving security teams a hard upper bound on what any Claude Code session can do. See the Claude Code security and permissions documentation for details on enterprise controls.
Is Setting Up Allowlists Worth the Effort for CI/CD?
Yes — and the source material makes the case plainly: without allowlists, every file write and shell command requires manual approval, which makes autonomous CI workflows impossible. With them, you define precise safe corridors — for example, allowing npm test but blocking npm install — so Claude can work quickly on permitted tasks while dangerous operations are caught automatically before execution.
The setup cost is low: create one JSON file, list your permitted commands, list your blocked paths, and choose a permission mode. The payoff is a Claude Code agent that can auto-fix lint errors, run tests, and report results inside an ephemeral container with no human intervention and no risk of touching credentials or modifying dependencies unexpectedly.
Frequently asked questions
What is the safest permission mode for a CI/CD pipeline with no human present?
Use bypassPermissions mode for full autonomous operation inside a locked-down, isolated container. Use dontAsk if the job only needs to read and analyze files without making any changes. Both modes suppress the interactive consent dialog that would otherwise freeze a headless pipeline.
Do deny rules still apply when bypassPermissions mode is active?
Yes. Deny rules are enforced even in bypassPermissions mode — if a deny rule matches a tool invocation, the tool is blocked regardless of the session posture. This means your hard safety net (blocking secrets reads, blocking package installs) remains intact in fully autonomous runs.
Why does my CI pipeline freeze when I use --dangerously-skip-permissions?
That flag triggers an interactive consent dialog on first run. A headless pipeline without a TTY waits indefinitely for that confirmation and never proceeds. Use --permission-mode bypassPermissions or --permission-mode dontAsk instead — these skip the consent dialog entirely.
Can I use a single wildcard rule like Bash(*) to allow all shell commands?
Technically you can write it, but broad wildcard shell rules are silently dropped when auto mode is active, and they grant more access than most pipelines need. Best practice is to list each permitted command explicitly for maximum reliability and auditability.
Where should CI/CD allowlist rules be stored — project or global settings?
Project-level .claude/settings.json is the right place for CI/CD rules. The file travels with the repository, so every CI runner and every developer who clones the repo gets the same rules automatically. Global settings live on individual machines and are not present on ephemeral CI containers.
How do I verify what rules are actually active during a session?
Run /permissions in the Claude Code terminal to view the active merged rule set. This shows you the final compiled rules after global and project settings have been merged, so you can confirm that project rules are not being overridden by globals or vice versa.
Permissions & tool allowlists is one of 85 features in Claude Master — the independent, continuously updated manual with worked examples, the pitfalls, and the workflows that put Claude to work.
Get Claude Master — founding price →Independent product. Not affiliated with or endorsed by Anthropic. "Claude" is a trademark of Anthropic, used here only to describe the subject of this guide.