defense-in-depth-validation — Claude Code skill: validate every layer
A skill that pushes your agent to validate data at every layer it passes through, so the bug becomes structurally impossible. Useful when bad data fails deep in execution.
npx skills add secondsky/claude-skills --skill defense-in-depth-validation -a claude-codeWhat defense-in-depth-validation does
The skill lays out four validation layers: entry point at the API boundary, business logic, environment guards, and debug instrumentation. Each layer catches a different class of failure, so one unchecked code path does not leave a hole. Your agent gets TypeScript and JavaScript examples and works through a real bug with them.
The workflow is: trace where the bad value originates and where it is used, list every checkpoint the data passes, add validation at each one, then try to bypass layer 1 and confirm layer 2 catches it. In the worked example an empty projectDir ran git init in the source tree, and all four layers closed that path.
A separate section covers security: rate-limit and filter at the edge, re-derive authorization server-side instead of trusting a client-supplied userId, and enforce the ownership clause inside the SQL query. There are no libraries to wire up and no API keys — it is a written guide the agent reads while debugging. Worth installing if you have already hit bugs that only surfaced on one execution path.
What it helps with
- close an invalid-data bug so it stops reproducing
- map every checkpoint data passes before it is used
- stop mocks and alternate paths from skipping entry validation
- refuse dangerous operations like
git initoutside a temp dir in tests - re-check ownership in the SQL query instead of trusting client claims
Example prompts for the agent
- Walk through this empty projectDir bug and add validation at every layer the data passes through
- Check whether the test mocks bypass entry validation and add a business logic guard
- Add the ownership clause to the SQL query so a handler bug can't cross tenants
How to install defense-in-depth-validation
1Skills CLI (Vercel)
npx skills add secondsky/claude-skills --skill defense-in-depth-validation -a claude-code2Manually: put the files in place
~/.claude/skills/defense-in-depth-validation/SKILL.mdCopy the whole skill folder; for one project use .claude/skills/defense-in-depth-validation/
1Skills CLI (Vercel)
npx skills add secondsky/claude-skills --skill defense-in-depth-validation -a codex2Manually: put the files in place
.agents/skills/defense-in-depth-validation/SKILL.mdFor all projects use ~/.agents/skills/
1Skills CLI (Vercel)
npx skills add secondsky/claude-skills --skill defense-in-depth-validation -a cursor2Manually: put the files in place
.cursor/skills/defense-in-depth-validation/SKILL.mdCursor also reads .agents/skills/ and .claude/skills/
1GitHub CLI
gh skill install secondsky/claude-skills defense-in-depth-validation2Manually: put the files in place
.github/skills/defense-in-depth-validation/SKILL.md1Skills CLI (Vercel)
npx skills add secondsky/claude-skills --skill defense-in-depth-validation -a gemini-cli2Platform CLI
gemini skills install https://github.com/secondsky/claude-skills --path plugins/defense-in-depth-validation/skills/defense-in-depth-validationManually: put the files in place
.devin/skills/defense-in-depth-validation/SKILL.mdThe legacy .windsurf/skills/ path still works
Skills CLI (Vercel)
npx skills add secondsky/claude-skills --skill defense-in-depth-validation -a opencodeClaude.ai
Zip the folder and upload it in Settings → Capabilities → Skills
Step by step, with commands for each platform: How to install skills in Claude Code: five ways
Questions about defense-in-depth-validation
How do I install defense-in-depth-validation in Claude?
Run npx skills add secondsky/claude-skills --skill defense-in-depth-validation -a claude-code. The skill lands at ~/.claude/skills/defense-in-depth-validation/SKILL.md.
What does defense-in-depth-validation do?
It describes four validation layers: entry, business logic, environment guards, and debug logging. The agent applies them when invalid data causes failures deep in execution.
How is it different from a single input check?
One check can be bypassed by another code path, a refactor, or a mock. The skill asks for validation at every layer so the bug becomes structurally impossible.
Source text
SKILL.md · MIT
---
name: defense-in-depth-validation
description: Validate at every layer data passes through to make bugs impossible. Use when invalid data causes failures deep in execution, requiring validation at multiple system layers.
metadata:
version: "1.1.0"
license: MIT
---
# Defense-in-Depth Validation
## Overview
When you fix a bug caused by invalid data, adding validation at one place feels sufficient. But that single check can be bypassed by different code paths, refactoring, or mocks.
**Core principle:** Validate at EVERY layer data passes through. Make the bug structurally impossible.
## Why Multiple Layers
Single validation: "We fixed the bug"
Multiple layers: "We made the bug impossible"
Different layers catch different cases:
- Entry validation catches most bugs
- Business logic catches edge cases
- Environment guards prevent context-specific dangers
- Debug logging helps when other layers fail
## The Four Layers
### Layer 1: Entry Point Validation
**Purpose:** Reject obviously invalid input at API boundary
```typescript
function createProject(name: string, workingDirectory: string) {
if (!workingDirectory || workingDirectory.trim() === '') {
throw new Error('workingDirectory cannot be empty');
}
if (!existsSync(workingDirectory)) {
throw new Error(`workingDirectory does not exist: ${workingDirectory}`);
}
if (!statSync(workingDirectory).isDirectory()) {
throw new Error(`workingDirectory is not a directory: ${workingDirectory}`);
}
// ... proceed
}
```
### Layer 2: Business Logic Validation
**Purpose:** Ensure data makes sense for this operation
```typescript
function initializeWorkspace(projectDir: string, sessionId: string) {
if (!projectDir) {
throw new Error('projectDir required for workspace initialization');
}
// ... proceed
}
```
### Layer 3: Environment Guards
**Purpose:** Prevent dangerous operations in specific contexts
```typescript
async function gitInit(directory: string) {
// In tests, refuse git init outside temp directories
if (process.env.NODE_ENV === 'test') {
const normalized = normalize(resolve(directory));
const tmpDir = normalize(resolve(tmpdir()));
if (!normalized.startsWith(tmpDir)) {
throw new Error(
`Refusing git init outside temp dir during tests: ${directory}`
);
}
}
// ... proceed
}
```
### Layer 4: Debug Instrumentation
**Purpose:** Capture context for forensics
```typescript
async function gitInit(directory: string) {
const stack = new Error().stack;
logger.debug('About to git init', {
directory,
cwd: process.cwd(),
stack,
});
// ... proceed
}
```
## Applying the Pattern
When you find a bug:
1. **Trace the data flow** - Where does bad value originate? Where used?
2. **Map all checkpoints** - List every point data passes through
3. **Add validation at each layer** - Entry, business, environment, debug
4. **Test each layer** - Try to bypass layer 1, verify layer 2 catches it
## Example from Session
Bug: Empty `projectDir` caused `git init` in source code
**Data flow:**
1. Test setup → empty string
2. `Project.create(name, '')`
3. `WorkspaceManager.createWorkspace('')`
4. `git init` runs in `process.cwd()`
**Four layers added:**
- Layer 1: `Project.create()` validates not empty/exists/writable
- Layer 2: `WorkspaceManager` validates projectDir not empty
- Layer 3: `WorktreeManager` refuses git init outside tmpdir in tests
- Layer 4: Stack trace logging before git init
**Result:** All 1847 tests passed, bug impossible to reproduce
## Key Insight
All four layers were necessary. During testing, each layer caught bugs the others missed:
- Different code paths bypassed entry validation
- Mocks bypassed business logic checks
- Edge cases on different platforms needed environment guards
- Debug logging identified structural misuse
**Don't stop at one validation point.** Add checks at every layer.
## Security defense-in-depth
The same layered approach is the standard pattern for security controls: trust
is never granted at a single point, so that a bug or bypass in any one layer
cannot by itself compromise the system. Each request is checked independently
at the edge, in the handler, and at the data layer.
- **Layer 1 (edge)**: rate-limit and filter at the edge (CDN/WAF such as
Cloudflare or AWS WAF) so abusive traffic never reaches the origin.
- **Layer 2 (handler)**: re-derive authorization server-side from the session
— never trust a client-supplied claim like a `userId` in the body.
- **Layer 3 (data)**: enforce the ownership invariant in the database query so
that even a handler bug cannot cross tenant boundaries.
```javascript
// Layer 2 — re-derive authz server-side, never trust client claims
app.put('/docs/:id', async (req, res) => {
const user = await verifySession(req.cookies.session); // throws if invalid
// Layer 3 — DB clause re-checks ownership; a handler bug still can't cross tenants
const updated = await db.query(
'UPDATE docs SET title = $1 WHERE id = $2 AND owner_id = $3 RETURNING *',
[req.body.title, req.params.id, user.id]
);
if (!updated.rowCount) return res.status(403).send('Not allowed');
res.json(updated.rows[0]);
});
```
Defense in depth means no single layer's failure is sufficient to compromise
the system.
© secondsky · MIT · secondsky/claude-skills