Tips & Tricks (Updated: 6/7/2026)

Claude Code Security: The 6-Perspective Map to Stop Accidents Before They Happen

One map for AI-assisted dev security: permissions, secrets, dangerous commands, dependencies, logs, production. With copy-paste config.

Claude Code Security: The 6-Perspective Map to Stop Accidents Before They Happen

I’ve read maybe ten articles on security, and I still couldn’t tell you whether my own project was actually safe.

I was right there with you. “Put API keys in environment variables.” “Keep permissions minimal.” I knew all the correct answers. But the knowledge was scattered across a dozen disconnected dots, and none of it answered the one question that mattered: so what am I missing right now? I’d only find out after an accident, when I’d think “oh, that’s the thing that article warned about.” I did that more than once.

So this article pulls AI-assisted development security into a single map. I’ll leave the fine-grained how-tos to the deep dives and instead give you a view from above, so you can spot which perspective you’re missing. There are only six places to defend. Close them in order, and even when you hand work off to an AI overnight, you won’t wake up to a terminal that’s gone pale.

I’m writing this from the defender’s side. This is not about how attacks work. It’s about how to build something that doesn’t break even when it’s attacked.

Key takeaways

  • AI-assisted accidents almost always come in through one of six holes: permissions, secrets, dangerous commands, dependencies and input, logs, and production isolation.
  • What works isn’t “being careful.” It’s “a system that stops you even when you aren’t.” The deny block in .claude/settings.json and your .gitignore are the two lifelines.
  • Close them in this order: (1) don’t hand over or commit secrets, (2) block dangerous commands with deny, (3) narrow what can be read and written. That alone erases most serious accidents.
  • The deep dives live in the internal links. Use this article as the checklist for catching what you’ve missed.
  • At the end there’s a settings.json you can copy whole, plus three commands to confirm it’s working.

Why does handing work to an AI suddenly get dangerous?

A normal text editor just displays characters. There’s no trapdoor in it.

But an AI agent like Claude Code is a tool that moves its hands inside your computer. It reads, writes, and deletes files. It runs commands in your terminal. It reaches out to the internet and posts to outside services. And all of it fires off a single “yes” from you.

The problem is that after you’ve tapped the approval button a few dozen times, you stop reading what’s in it. The instant you fall into the “yeah yeah, OK” rhythm, a dangerous operation slips right through. It’s like a knife: the sharper it cuts, the more it hurts you before you’ve learned to handle it. Because it’s fast and clever, it sprints at full speed in the wrong direction too.

So flip your whole mindset here, once. Not “I’ll be careful,” but “I’ll put a system in place first, so that even when I slip, nothing breaks.” All six perspectives on the map are arranged around that single idea.

The big picture: there are only six places to defend

Start with the whole thing on one page. Here’s what guards what, viewed from above.

#PerspectiveExample accident it preventsMain toolDeep dive
1Narrow permissionsRewriting or deleting on its ownallow/ask/deny in settings.jsonPermissions guide
2Don’t hand over or commit secretsAPI key leak.gitignore, env vars, maskingSecrets management
3Stop dangerous commandsrm -rf, force pushdeny rulesAvoiding dangerous prompts
4Verify dependencies and inputSupply chain, injectionlockfile, schema validation(this article)
5Don’t leave secrets in logsLeaks from historymasking, output controlSecrets management
6Treat production separatelyDestroying the production DBenv isolation, flagsFailure cases

These six look independent, but they’re actually strung along a single line: what you let the AI see, what you let it do, where you stop it, and how you check. Narrow what it sees (1 and 2), restrict what it does (3), distrust what comes in (4), leave no trace (5), and give production special treatment (6). Let’s go through them in order.

Perspective 1: Narrow permissions — allow reading, forbid deleting

The first line of defense is permissions. Claude Code lets you decide “OK / needs confirmation / forbidden” per operation. In permissions inside .claude/settings.json, you just sort operations into three boxes.

BoxMeaningWhat goes in
allowRun OK without confirmationRead-only, safe operations
askAsk properly every timeWrites, commits, pushes
denyNever let it happenDelete, force push, production DB

Here’s the mantra: read-only is allow, writing is ask, deleting is deny.

What matters is which direction you start from. Lock it down tight at first, and promote only the operations you’ve confirmed are safe later. The reverse direction (loose first, tighten later) always ends up happening after an accident. So start from the tightening direction. The exact specs — how allow, deny, and ask are evaluated in order, and the pattern syntax for Bash, Edit, Read, WebFetch and so on — are laid out in a quick-reference table in the Claude Code permissions guide.

One note: on top of these three categories, current Claude Code also has mechanisms like an auto-approval mode that mechanically judges how risky an operation is. The precise behavior is something the vendor is the source of truth for, so when you’re tuning your config, always check the official settings documentation.

Perspectives 2 and 5: Don’t hand over secrets, don’t commit them, don’t leave them in logs

Leaks almost always come through three routes: hardcoded in the code, handed straight to the AI, and left in the logs. We’ll seal all three.

First, stop hardcoding, and isolate keys into .env. Then declare that .env so Git “absolutely never picks it up.” This is the lifeline.

# Always write this in .gitignore (insurance against accidentally committing a key)
.env
.env.*
!.env.example   # only the sample is fine to share
*.pem
*.key
*-service-account.json   # don't forget cloud service-account keys either

In the code, instead of writing the value directly, load it as an environment variable. Create a state where the actual key never appears in the code at all.

// OK: read from the environment variable. don't write the value in code at all
import { config } from "dotenv";
config();

const token = process.env.QIITA_TOKEN;
if (!token) {
  // if the key is missing, report "you forgot to set it" — not the value
  throw new Error("QIITA_TOKEN is not set. Check your .env.");
}

Next, “handed to the AI” and “left in the logs.” When you want an error fixed and you paste the whole log, a line like DATABASE_URL=postgresql://user:realpassword@... can be hiding in there. The moment you hand it over, the raw key is left in the conversation history and in the subagent’s history. So don’t print the value — not in error output, not in the logs you hand to the AI.

// NG: the API key ends up right there in the error log
throw new Error(`Auth failed: token=${process.env.TOKEN}`);

// OK: don't print the value, just say where to look
throw new Error("Auth failed: check the TOKEN environment variable");

When you hand a log to the AI, replace the values on the key lines with masking first. The AI can do its job knowing only the structure “there’s DB connection info here.”

OK to hand overDon’t hand over
Error type, repro steps, file namesAPI keys, passwords, session cookies
.env.example, the “names” of config itemsProduction DB URL, customer data
Logs with values replaced by maskingReal tokens, service-account .json files

Forbidding the AI from even reading .env makes this much harder still (covered in Perspective 3). The accident of letting it read .env can be wiped out with a single line of config. The full procedure — including how to handle this in CI/CD and how to rotate production keys — is written up in detail in Claude Code secrets management.

Perspective 3: Stop dangerous commands — physically block the irreversible

rm -rf (bulk delete), git push --force (overwriting a teammate’s work), git reset --hard (discarding changes). These are irreversible. Rather than “always ask a human before running,” start by tipping them all the way to “can’t be run at all.”

This is where the deny block shines, combined with the “reading forbidden” rule from Perspective 2.

"deny": [
  "Read(./.env)",
  "Read(./.env.*)",
  "Read(./secrets/**)",
  "Bash(rm -rf*)",
  "Bash(git push --force*)",
  "Bash(git reset --hard*)",
  "Bash(curl * | bash)"
]

Look at that last one, Bash(curl * | bash). That’s the operation “run a script grabbed off the internet, raw, without looking at what’s inside.” You see it constantly in install instructions, but in AI-assisted development it’s especially dangerous. It feeds straight into the supply-chain problem in Perspective 4, so block it early.

And don’t just block it in the config file — also write the intent, in plain language, in CLAUDE.md (your project rules). A mechanical block plus an explanation to the AI. A two-layer setup leaves fewer gaps.

## Forbidden when editing or running (write this in CLAUDE.md)

- Don't read .env / secrets/. If needed, always confirm with a human.
- Don't run rm -rf, force push, or writes to the production DB.
- Don't run scripts fetched from the internet without checking them.

The way a vague one-liner invites the AI to run wild — and the templates for making the request itself safe — are collected in how “just do everything” causes accidents and how to prevent them. Binding things with config and changing how you ask are two wheels of the same cart.

Perspective 4: Verify dependencies and input — distrust what the AI brings in

This is the perspective traditional security articles tend to skimp on. When you hand work to an AI, code you didn’t write and input you didn’t check both flow into your project.

The first is dependencies (the supply chain). The AI will cheerfully run npm install saying “I’ll just add this library.” But the package name might be one character off from the real one (typosquatting), or it might be an unmaintained, ancient thing. So put dependencies into the ask box where a human confirms them, commit the lockfile to pin versions, and run an audit in CI. Known vulnerabilities then get caught automatically.

# Mechanically check whether dependencies have known vulnerabilities
npm audit --audit-level=high

# Install exactly per the lockfile (prevents arbitrary version bumps)
npm ci

The second is input. It’s dangerous when AI-generated code trusts values coming from outside (user input, API responses, file contents) and runs with them as-is. SQL injection and command injection both come from this “not distrusting the input.” Always validate the type and shape at the boundary.

import { z } from "zod";

// Always validate the shape of values from outside, at the boundary
const Payload = z.object({
  email: z.string().email(),
  amount: z.number().int().positive().max(100000),
});

// Input that doesn't pass parse is rejected right there (never passed to the DB downstream)
const safe = Payload.parse(await req.json());

Verifying dependencies and input is continuous with web application security in general. The OWASP Top Ten is the industry-standard checklist for the main categories of attack, so reading it once will help you notice the perspectives you’ve missed.

Perspective 6: Treat production separately — so one wrong character doesn’t wipe everything

Last one. myapp_dev and myapp_prod are one character apart. Ask only “clean out the old data” without the AI checking which one it’s connected to, and what disappears might be your production customers’ data.

So make writing to production take one extra step. That inconvenience is the lifeline.

// scripts/db-query.mjs
const env = process.env.NODE_ENV ?? "development";

// If it tries to write to production, stop unless the dedicated flag is present
if (env === "production" && process.argv.includes("--write")) {
  console.error("Writing to production requires the --force-production flag.");
  process.exit(1);
}

Real cases of a production DB getting blown away, of CI running wild — the raw causes, recoveries, and prevention measures — are collected in Claude Code security failure cases. Other people’s accidents are the cheapest lesson you can buy.

Copy-paste ready: a settings.json that packs all six perspectives onto one page

I’ve put all six perspectives above into one file. Drop it in as .claude/settings.json and it works as-is. Add and subtract to fit your own environment.

{
  "$schema": "https://json.schemastore.org/claude-code-settings.json",
  "permissions": {
    "defaultMode": "default",
    "allow": [
      "Read(**)",
      "Glob(**)",
      "Grep(**)",
      "Bash(npm run lint*)",
      "Bash(npm run test*)"
    ],
    "ask": [
      "Write(**)",
      "Edit(**)",
      "Bash(npm install*)",
      "Bash(git commit*)",
      "Bash(git push*)"
    ],
    "deny": [
      "Read(./.env)",
      "Read(./.env.*)",
      "Read(./secrets/**)",
      "Bash(rm -rf*)",
      "Bash(git push --force*)",
      "Bash(git reset --hard*)",
      "Bash(curl * | bash)"
    ]
  }
}

Confirm it took with three commands.

# 1. Is .env out of Git's management? (no output = not tracked = OK)
git ls-files --error-unmatch .env

# 2. Did a key get committed by accident? Naively search the history
git grep -nE "(api[_-]?key|secret|token|password)\s*=\s*['\"][A-Za-z0-9]" $(git rev-list --all) -- . || echo "no obvious hardcoded keys found"

# 3. Do dependencies have known vulnerabilities?
npm audit --audit-level=high

If command 1 errors on .env, it’s “not tracked,” so you’re safe. Command 2 is a naive search, so it’s not foolproof, but it catches obvious hardcoding. If command 3 turns up high-risk items, bump or replace the dependency.

The full checklist

Open a project and confirm from the top down. If even one box is unchecked, that’s your next hole.

  • .claude/settings.json exists, and deny includes rm -rf and force push (Perspectives 1 and 3)
  • Read-only is allow, write-family operations are ask (Perspective 1)
  • .gitignore has .env and key files (*.pem, *.key, *-service-account.json) (Perspective 2)
  • Keys aren’t hardcoded in code or prompts; they’re read from environment variables (Perspective 2)
  • deny forbids reading .env and secrets/ (Perspectives 2 and 5)
  • No key values appear in error output or in logs handed to the AI (Perspective 5)
  • npm install is ask, the lockfile is committed, and npm audit is in CI (Perspective 4)
  • External input is validated with a schema (Perspective 4)
  • Writing to production takes one extra step, like a flag (Perspective 6)
  • CLAUDE.md spells out “what’s forbidden” and “OK / not OK to hand over” in plain language (all perspectives)

FAQ

Q. This is a lot. In the first 30 minutes, what should I prioritize? A. Just the top three. (1) Create .env, move your keys into it, and add it to .gitignore. (2) Add rm -rf and reading .env to the deny block in .claude/settings.json. (3) Set write-family operations to ask. That alone stops most serious accidents (key leaks and production deletion). The rest can come later.

Q. Is it OK to use the “fully autonomous” mode that runs without confirmation? A. Only inside a trusted, isolated environment (a disposable container or a dedicated sandbox). Skipping confirmation on your main machine, or on anything connected to production, is like taking off your training wheels and riding straight down a hill. Convenience and irreversibility are always back to back.

Q. Even if I put it in deny, can’t a reworded command slip through? A. Pattern matching isn’t foolproof, so a workaround written a different way is possible. That’s why you shouldn’t over-trust deny as “the last line of defense.” Layer it: spell out the intent in CLAUDE.md, isolate production (Perspective 6), and keep the human ask confirmation. Not one wall, but several.

Q. Is it realistic to check every library the AI installs? A. Eyeballing all of them is impossible, so hand it to a machine. Set npm install to ask and glance at the name for a second; put npm audit in CI so known vulnerabilities get rejected automatically. Humans just check “is this name weird?”; the deep inspection goes to the machine. That’s how you keep it up.

Q. Do I need to go this far on a personal hobby project? A. For key leaks alone, yes — defend seriously even on a hobby project. The instant you publish to GitHub, bots around the world pick keys up. Production isolation (Perspective 6), on the other hand, you can skip if a hobby project has no production. Picking and choosing per perspective, by “does this apply to me?”, is plenty.

Wrapping up

Security in AI-assisted development isn’t about the volume of hard knowledge. It’s the work of sealing six holes, from the top down.

PerspectiveIn a wordMain move
1. Narrow permissionsAllow readingallow/ask/deny
2. Don’t hand over secretsKeys outside the code.env + .gitignore
3. Stop dangerous commandsForbid deletingdeny rules
4. Verify dependencies and inputDistrust what’s brought inaudit + schema validation
5. Don’t leave them in logsErase the traildon’t print values, mask
6. Treat production separatelyPrevent the one-character slipenv isolation, flags

The idea was the same the whole way through. Stop “being careful,” and put “a system that stops you even when you aren’t” in place first. Rather than straining to master a clever AI, lay down a floor that keeps you from getting hurt when you fall. That’s the easiest and fastest path — that’s where I’ve landed.

Bookmark this article, and run the checklist from the top every time you start a new project. When you want to go deeper on a perspective, follow the internal links from the body to the deep dives. And if you’re unsure how far to lock things down as a team, I have materials and support ready too. Start by peeking at the materials list.

#claude-code #security #permissions #secrets #checklist #best-practices
Free

Free PDF: Claude Code Cheatsheet

Enter your email and download the one-page Claude Code cheatsheet for commands, review habits, and safe workflows.

We handle your data with care and never send spam.

Level up your Claude Code workflow

Start with the free PDF, use Gumroad guides when you need repeatable workflows, and book consultation when rollout or revenue paths need human judgment.

Masa

About the Author

Masa

Engineer focused on practical Claude Code workflows. Runs claudecode-lab.com, a 10-language technical media site.