Security Hardening the IA Framework: A Systematic Audit
A systematic gap analysis against Claude Code security standards revealed dormant hooks, missing defense layers, and undocumented decisions. Here is what I found and what I fixed.
The Security Code That Never Ran
I built a bash command validator months ago. It blocks fork bombs, prevents curl | bash piped execution, catches rm -rf / before it fires. Comprehensive pattern matching, clear error messages, safe alternatives suggested in every block response.
It never ran. Not once.
The code existed in the repository. The TypeScript compiled. The tests passed. But no one wired it into the PreToolUse hooks in settings.json. Having security code is not the same as having security enforcement, and I did not catch the difference until I ran a systematic audit against external security standards.
This post documents that audit: what methodology I used, what gaps I found, what I fixed, and what the resulting security architecture looks like. If you are configuring Claude Code for any serious work -- or building any AI-assisted framework you intend to deploy yourself -- the methodology transfers directly. That is the point: security hardening should be something you can do for your own configuration, not something you outsource to a consultant.
The Gap Analysis Methodology
Security improvements tend to happen reactively. Something breaks, someone patches it. The IA Framework had been accumulating security features this way for months -- credential guardians here, pre-commit hooks there -- without a systematic review of what was actually running versus what was supposed to be running. If you are building your own framework, this is the trap: organic growth without periodic verification.
The approach I used was straightforward:
- Find an external baseline. I used the Trail of Bits claude-code-config repository as the primary reference -- opinionated defaults covering sandboxing, permissions, hooks, and deny rules from a team that does this professionally.
- Compare systematically. Walk through every recommendation and check whether the framework implements it, partially implements it, or misses it entirely. No assumptions. No "I think we already have that."
- Categorize gaps by priority. P1 for active security gaps (capability missing entirely), P2 for missing standards (capability exists but incomplete), P3 for enhancements (nice-to-have improvements).
- Implement and document. Fix each gap, test each layer independently, and document architectural decisions -- including decisions to intentionally skip recommendations.
This methodology is not novel. AWS published a similar phased approach for their Agentic AI Security Scoping Matrix, and CrowdStrike's AI agent security whitepaper recommends a 90-day roadmap with prioritized rollout. The common thread is systematic comparison against established baselines rather than ad-hoc patching.
What made this audit useful was the external reference point. Reviewing your own security configuration without a baseline means you only find what you already know to look for. Trail of Bits provided the checklist I did not know I needed. Anyone can do this -- pick a credible reference, compare systematically, document what you find.
What I Found
The audit identified six gaps across three priority levels. Some were embarrassing. All were fixable.
P1: Active Security Gaps
Dormant bash command validator. The hook existed with comprehensive pattern matching -- blocking destructive commands like rm -rf /, piped execution from untrusted sources (curl | bash, wget | sh), force pushes, overly permissive chmod 777, device writes, and fork bombs. But it was never registered in settings.json as a PreToolUse hook. The code was dead.
This is a common antipattern that security literature rarely discusses because it is embarrassing to admit. You write the security control, you test it in isolation, you commit it, and you move on -- without verifying it actually executes in the production path. Configuration matters as much as implementation.
No declarative deny rules. Claude Code supports a permissions.deny section in settings.json that blocks tool access at the declarative level, before any hooks execute. The framework had none. If hooks failed to load for any reason, there was no fallback layer protecting sensitive paths like ~/.ssh, ~/.aws, or ~/.kube.
P2: Missing Standards
No package manager enforcement. The framework standardized on Bun, but nothing prevented an agent from typing npm install out of habit. Mixing package managers creates conflicting lock files and can pull different dependency versions -- a supply chain risk that security literature increasingly flags as a real concern, not just a DevOps inconvenience.
No work completion enforcement. Claude exhibits specific linguistic patterns when rationalizing incomplete work: phrases like "out of scope," "future improvement," or "left as an exercise" combined with deferral signals like "I'll address later" or "we can revisit." These patterns are detectable and, left unchecked, result in tasks marked complete that are actually deferred.
Code quality standards undocumented. Function length limits, complexity thresholds, parameter counts -- none of these were documented anywhere the AI agent could reference during code generation. The internet has plenty of 500-line functions. Without explicit guidance on what "good" looks like in this codebase, generated code drifts toward whatever the training data normalized.
P3: Enhancements
MCP (Model Context Protocol) Server bypass undocumented. The framework intentionally does not use MCP servers, preferring direct TypeScript API clients for performance and debuggability. But this decision was tribal knowledge. During the audit, the missing .mcp.json file looked like a gap until I checked the rationale. If it looked like a gap to me, it would look like a gap to anyone else auditing the configuration.
No mutation audit logging. Write, Edit, and Bash operations were tracked for session replay but not logged in a structured forensics format. After an incident, reconstructing what happened required parsing unstructured session files.
What I Fixed
P1 Fixes: Wiring the Bash Validator and Adding Deny Rules
The bash command validator was wired into settings.json as a PreToolUse hook for the Bash tool. Every bash command now passes through pattern matching before execution. Blocked commands return clear error messages with safe alternatives.
For the deny rules layer, I added permissions.deny entries to settings.json covering sensitive credential paths:
~/.ssh/*
~/.aws/*
~/.kube/*
~/.gnupg/*
~/.docker/config.json
~/.npmrc
~/.pypirc
~/.gem/credentials
~/.cargo/credentials*
~/.config/gh/hosts.yml
~/.netrc
These rules fire at the declarative level before hooks execute. If hook loading fails, the deny rules still protect credential paths. This is the critical distinction: defense in depth requires independent failure modes, not just multiple implementations of the same check.
P2 Fixes: Package Manager Enforcement, Anti-Rationalization, and Code Standards
Package manager enforcement is a PreToolUse hook that detects the Bun lock file and blocks npm, yarn, and pnpm commands. Rather than just blocking, it provides command equivalents in the error message -- npm install maps to bun install, npx maps to bunx, and so on. The hook allows npx for legitimate one-off tool execution while blocking persistent package manager commands. Nuanced enforcement over blanket blocking.
Anti-rationalization detection is a Stop hook that scans Claude's output for combined rationalization and deferral patterns. It requires both a rationalization phrase ("out of scope", "beyond the scope", "future improvement") AND a deferral signal ("I'll", "we can", "should") to fire, reducing false positives. On first detection, it forces continuation. A cooldown mechanism prevents infinite loops -- if the same session triggers twice, it allows the stop. This mirrors behavioral modification principles: interrupt the pattern once, let the agent course-correct.
Code quality standards were documented in CLAUDE.md with explicit thresholds: function length at or under 100 lines, cyclomatic complexity at or under 8, 100-character line width, parameter count at or under 5, zero warnings tolerance. These are not pre-commit enforced yet -- they guide AI code generation before the code is written, a different intervention point than blocking bad code at commit time.
P3 Fixes: Architecture Documentation and Mutation Logging
The MCP bypass was documented as an explicit architectural decision. The documentation explains why direct TypeScript API clients are preferred (no process spawn overhead, stack traces instead of opaque JSON-RPC, Bun dependency management instead of runtime npx pulls) and when to reconsider the decision (if MCP gains in-process execution or a critical capability becomes MCP-only).
Anthropic's own sandboxing documentation discusses how sandboxing reduces permission prompts by 84% while increasing security. The IA Framework's MCP bypass follows a similar philosophy: fewer moving parts, tighter control, less attack surface.
Mutation audit logging was added to the existing tool tracker hook. All Write, Edit, Bash, and NotebookEdit operations now log to a JSONL file with timestamps, operation types, targets, and exit codes. API keys in Bash commands are sanitized before logging. The format is JSONL specifically because each line is valid JSON -- you can stream to analysis tools, grep for patterns, or process incrementally without parsing the entire file.
The Resulting Architecture: 4-Layer Credential Protection
After the hardening, the framework's credential protection follows a defense-in-depth architecture with four independent layers:
Layer 1 -- AI Instructions. CLAUDE.md explicitly instructs the agent to never read credential files (.env, API keys, SSH keys, cloud credentials). The agent actively refuses requests and suggests secure alternatives like "verify the script loads .env correctly by reviewing the code that uses it."
Layer 2 -- Declarative Deny Rules. settings.json contains permissions.deny entries that block Read and Edit tool access to sensitive paths before hooks execute. This layer requires no code execution -- it is a declarative configuration that Claude Code enforces at the platform level.
Layer 3 -- PreToolUse Hooks. credential-guardian.ts performs pattern matching on file paths, validating that template files (like .env.example) contain only safe placeholder content. Hard block on credential files, fail-open on hook errors so legitimate work is not interrupted.
Layer 4 -- Pre-commit Hooks. Git hooks scan staged changes for hardcoded API keys, AWS credentials, private keys, and common secret patterns before allowing commits.
Each layer was added because the layer before it has a failure mode. AI instructions can be overridden by prompt injection. Deny rules protect when hooks fail to load. Hooks catch what deny rules cannot pattern-match. Pre-commit hooks catch what slips through all three runtime layers.
Meta's Agents Rule of Two framework argues that AI agents should satisfy no more than two of three properties: untrusted data, production access, and stateful changes. The IA Framework's 4-layer approach ensures that even when the agent handles untrusted data and makes stateful changes, multiple independent controls limit the blast radius.
Lessons for Others Configuring Claude Code
These lessons are designed to be deployable. You do not need a security consultant to implement any of them -- just the methodology and the willingness to look honestly at your own configuration.
Audit execution paths, not just code existence. The dormant bash validator is not unique to this framework. If you have security hooks, verify they are registered in settings.json and actually fire on tool invocation. Backslash's Claude Code security guide recommends an allowlist approach for permissions -- but an allowlist only works if it is loaded.
Add deny rules as a declarative safety net. Hooks are code. Code can fail to load. permissions.deny in settings.json is configuration that Claude Code enforces at the platform level. Use it as your baseline layer, especially for credential paths. GitGuardian's git hooks reference makes the same distinction between client-side hooks (developer education, easy to bypass) and server-side hooks (enforcement). Deny rules are your server-side equivalent.
Enforce package manager consistency. If you standardized on a package manager, enforce it. Lock file conflicts from mixed package managers are silent failures that introduce supply chain risk. A PreToolUse hook that blocks non-standard commands takes minimal effort and prevents real drift.
Document your "no" decisions. If you reviewed a technology and chose not to use it, document that choice and the rationale. The absence of a configuration file is invisible -- it looks the same whether you evaluated and rejected something or never considered it. Explicit documentation of "no, and here is why" prevents false positives in future audits.
Test layers independently. Defense in depth only works if each layer functions independently. Disable hooks and verify deny rules still block. Remove deny rules and verify hooks still catch. Legit Security's analysis of defense in depth versus layered security draws an important distinction: defense in depth spans people, process, and technology. Layered security is controls within a single layer. Aim for the former.
Every one of these steps can be done on a laptop, with free tools, in an afternoon. Enterprise security expertise is not behind a paywall -- it is a methodology you can learn and apply to your own setup.
What Changed
Before this audit, the framework had two credential protection layers: AI instructions and a PreToolUse hook. The bash command validator existed but did not run. Package managers were not enforced. Architectural decisions lived in someone's head. Mutation logging was unstructured.
After the audit, the framework has four credential protection layers with independent failure modes, active bash command validation, package manager enforcement with educational error messages, anti-rationalization detection for work completion, structured mutation audit logging, documented architectural decisions, and explicit code quality standards.
The methodology -- external baseline, systematic comparison, prioritized remediation, documented decisions -- is the transferable part. The specific fixes are framework-dependent. The process works for any Claude Code configuration, and frankly, for any security audit where you suspect the gap between "what I think I have" and "what actually runs" might be wider than expected.
It usually is.
The broader point is that security hardening does not require a security team. It requires a systematic methodology and the discipline to follow it. Pick an external baseline, compare honestly, fix what you find, document what you chose not to fix. That is expertise you can deploy yourself -- and once you have the methodology, you can rerun it every time your configuration changes.
Found This Helpful?
Want to put this into practice? Subscribe for free to get methodology guides like this one. Contributors get implementation deep dives and full hook source code.
Sources
Security Frameworks and Standards
- Claude Code Security Standards - Backslash - Allowlist approach for permissions, denylist for bypass attempts
- Trail of Bits Claude Code Config Repository - Opinionated defaults for sandboxing, permissions, hooks, and deny rules
- AWS Agentic AI Security Scoping Matrix - Defense-in-depth at network, application, agent, and data layers
- Meta Agents Rule of Two Framework - Limiting intersection of risk properties for AI agents
- CrowdStrike AI Agent Security Architecture - 90-day roadmap for AI agent security hardening
Defense in Depth
- Defense in Depth - Fortinet - Multiple security layers protecting assets with backup on compromise
- Defense in Depth vs Layered Security - Legit Security - Wide-angle approach spanning people, process, and technology
Git Security and Secret Management
- GitGuardian Git Hooks Reference - Client-side vs server-side hook enforcement models
- Orca Security Git Hooks Guide - Layered workflow from .gitignore to emergency response
Claude Code Platform
- Claude Code Sandboxing - Anthropic Engineering - Sandboxing reduces permission prompts by 84% via filesystem isolation