Keeping Your Claude Local Agent Secure
Claude Code: Security Hardening
Why should you care about hardening your Claude? Because you don’t want to be the developer whose Claude caused a catastrophic outage if your machine got compromised. 🙀
The following config won’t prevent every attack, but it significantly reduces the blast radius of a compromised session, malicious prompt injection, or a rogue dependency. Security is an ongoing practice and as AI advances so will recommendations. It is extremely important now to add layers of defense.
By setting up an extra layer of protection your agent won't be able to accomplish tasks that can cause massive financial consequences. That extra layer of protection means your agent physically can't reach for operations with real financial consequences: deleted data, leaked credentials, blown-away infrastructure.
Pre-req: Sandbox Setup (Quick Start)
If you’re not familiar with sandboxes or permissions in Claude Code, it’s recommended to read the official docs first: Sandboxing — the default environment unless you opt into a more advanced setup — and Permissions. If you haven’t set up your sandbox environment and don’t want to read the full docs, use the quick steps below. This will turn on a sandbox setting.
Check if you already have a settings file:
cat ~/.claude/settings.jsonIf it exists, the contents will print. If not, you’ll see
No such file or directory.Create it with sandbox enabled. Skip this step if step 1 printed the file’s contents — you already have one.
mkdir -p ~/.claude \ && [ ! -f ~/.claude/settings.json ] \ && echo '{"sandbox": {"enabled": true}}' > ~/.claude/settings.json \ || echo "settings.json already exists, skipping"If you already had a settings file and step 2 skipped, add the sandbox key to it manually:
{ "sandbox": {"enabled": true} }Verify sandbox is enabled. Run inside Claude Code:
/sandboxImportant: if you use the
/sandboxmenu to try a different mode, that choice gets saved to settings.local.json and will silently overridesettings.jsonfrom then on — even after you editsettings.jsonagain. If you're just testing, remember to flip it back through the menu (or clear the override file) once you're done.
Troubleshooting
- If you still see No Sandbox after adding your settings, look in your settings.local.json to ensure you do not have local overrides:
~/.claude/settings.local.json - Earlier versions of this snippet wrote
{"sandbox": true}, which Claude Code rejects as invalid and skips. Ensure you have the sandbox value set correctly.
Hardening Your Settings
This sample config allows basic GitHub CLI commands (this doesn’t add settings for any other CLI tools, so it’s important for you to understand it enough to be able to modify it yourself — see the sections below if you have questions).
This config has explicit deny rules for the really bad things. The others are nice-to-haves, but if you do nothing else, just add the deny rule section.
Copy this, or sections of it, to ~/.claude/settings.json.
{
"sandbox": {
"enabled": true,
"autoAllowBashIfSandboxed": true,
"allowUnsandboxedCommands": false,
"enableWeakerNetworkIsolation": false,
"network": {
"allowedDomains": ["github.com"]
},
"excludedCommands": [
"gh pr view*",
"gh pr list*",
"gh pr comment*",
"gh pr create*",
"gh issue view*",
"gh issue list*",
"gh repo view*",
"gh repo list*",
"gh run view*",
"gh run list*",
"gh search*"
]
},
"permissions": {
"allow": [
"Bash(ls:*)",
"Bash(cat:*)",
"Bash(head:*)",
"Bash(tail:*)",
"Bash(less:*)",
"Bash(more:*)",
"Bash(grep:*)",
"Bash(find:*)",
"Bash(pwd:*)",
"Bash(wc:*)",
"Bash(diff:*)",
"Bash(file:*)",
"Bash(stat:*)",
"Bash(du:*)",
"Bash(df:*)",
"Bash(whoami:*)",
"Bash(hostname:*)",
"Bash(date:*)",
"Bash(which:*)",
"Bash(type:*)",
"Bash(git status:*)",
"Bash(git log:*)",
"Bash(git diff:*)",
"Bash(git show:*)",
"Bash(git branch:*)",
"Bash(npm list:*)",
"Bash(gh pr list --author @me*)",
"Bash(node --version:*)",
"Bash(npm --version:*)"
],
"ask": [
"Bash(curl *)",
"Bash(wget *)",
"Bash(ssh *)",
"Bash(scp *)",
"Bash(rsync *)",
"Bash(docker *)",
"Bash(kubectl *)",
"Bash(helm *)",
"Bash(aws *)",
"Bash(rm:*)",
"Bash(mv:*)",
"Bash(cp:*)",
"Bash(touch:*)",
"Bash(mkdir:*)",
"Bash(rmdir:*)",
"Bash(chmod:*)",
"Bash(chown:*)",
"Bash(ln:*)",
"Bash(dd:*)",
"Bash(gh api*)",
"Bash(git push*)",
"Bash(npm publish)",
"Bash(gh pr comment*)",
"Bash(gh pr create*)",
"Bash(gh pr view*)",
"Bash(gh pr list*)",
"Bash(terraform* state*)"
],
"deny": [
"Read(~/.ssh/**)",
"Read(~/.aws/**)",
"Read(~/.gnupg/**)",
"Read(~/.bash_history)",
"Read(~/**/.env*)",
"Read(~/**/*.pem)",
"Read(~/**/*.key)",
"Read(~/**/*.p12)",
"Read(~/**/*.pfx)",
"Read(~/**/*.crt)",
"Read(~/**/*.cer)",
"Bash(env)",
"Bash(env *)",
"Bash(printenv)",
"Bash(printenv *)",
"Bash(rm -rf /)",
"Bash(sudo *)",
"Bash(gh secret*)",
"Bash(gh ssh-key*)",
"Bash(gh variable*)",
"Bash(gh repo delete*)",
"Bash(gh repo edit*)",
"Bash(gh ruleset*)",
"Bash(gh gist*)",
"Bash(git push --force*)",
"Bash(git push -f *)",
"Bash(git push* origin main*)",
"Bash(git branch -D*)",
"Bash(aws iam *)",
"Bash(aws s3 rm*)",
"Bash(aws ec2 terminate*)",
"Bash(aws secretsmanager *)",
"Bash(aws kms *)",
"Bash(aws sts assume-role*)",
"Bash(assume *)",
"Bash(kubectl delete *)",
"Bash(kubectl apply*)",
"Bash(kubectl exec *)",
"Bash(kubectl edit *)",
"Bash(terraform* apply*)",
"Bash(terraform state* rm)"
]
}
}
What Each Tier Does
allow
Behavior: Auto-approved, no prompt.
What’s in it: Read-only file inspection, git read ops, system info, version checks, your own PR list.
ask
Behavior: Prompts you before running.
What’s in it: Network tools, infra CLIs, file mutations, all pushes, PR creates/comments, publishing.
deny
Behavior: Hard blocked, no override. Your agent will not be able to perform this action.
What’s in it: Infra CLI destructive actions, file mutations, force pushes, ssh files, and access to secrets.
When creating your own personal settings, think about what each tool has access to. If it can cause a catastrophic outage or a major security risk, ensure it does not have access. You need to understand your use case and your company’s security standards, and write your settings based on the approved actions.
Example — It would be a very bad idea for your agent to write, plan, and deploy terraform to production without any human intervention. If you don't have the proper infrastructure guardrails in place, it could completely wipe out a production environment with a single prompt.
Key Sandbox Settings
enableWeakerNetworkIsolation: false (It is dangerous to change this to true)
Keeps enforcement at the OS level. Setting this to true downgrades to app-level filtering, which can be bypassed and can cause reduced sandbox security. Don't use this dangerous setting — always keep it false and figure out a safe workaround.
allowedDomains: ["github.com"]
Domains that are allowed to have outbound connections from sandboxed commands.
allowUnsandboxedCommands: false (It is dangerous to change this to true)
Prevents Claude from retrying failed sandboxed commands outside of the sandbox. This keeps enforcement strict.
Why Deny Rules Use Absolute Paths
The deny rules for credentials and cert files use full absolute paths (e.g. Read(/Users/YOUR_USER/.env*)) rather than relative paths like Read(./.env*), because relative paths only protect files under whatever folder you happened to launch Claude Code from. Start a session in /Users/YOUR_USER/projects/side-project, and a relative deny rule misses every .env file outside that one folder — including the one sitting in your home directory. Absolute paths don't have that blind spot: they cover your entire home directory no matter where you launched from.
MCP Tools Bypass the Permission System
Overarching permission rules do not catch MCP tool calls on their own. You have to explicitly add the MCP rules that you need to override. Traditional commands like curl or bash don't apply because MCPs are connected directly to tool calls.
To add MCP permissions you need to follow the proper syntax:
mcp__<server>__<tool> for a specific tool
mcp__<server> for an entire server.
These rules work in allow, ask, and deny at any settings scope.
Example:
{
"permissions": {
"ask": [
"mcp__claude_ai_Atlassian__createJiraIssue",
"mcp__claude_ai_Atlassian__editJiraIssue",
"mcp__claude_ai_Slack__slack_send_message"
],
"deny": [
"mcp__claude_ai_Atlassian__transitionJiraIssue"
]
}
}
Read Before You Approve
The ask prompts show the full command and arguments before anything runs. Read them. If you don't understand what the command is doing, always research it before you approve. You are the guardrail at that point — if you don't know what it's doing, you should not be allowing your agent to run the command.
Relax Knowing That You Made Your World A Little Safer
By adding these security settings, you are adding an extra layer of protection to yourself and your company. Security is everyone's responsibility in the AI era — at the end of the day, you are responsible for what your agent does. Hopefully these settings will help protect you and your company. Good luck, and happy Clauding.