home / skills / ed3dai / ed3d-plugins / defense-in-depth

This skill enforces defense-in-depth by validating data at entry, business, environment, and debug layers to prevent bugs across execution paths.

npx playbooks add skill ed3dai/ed3d-plugins --skill defense-in-depth

Review the files below or copy the command above to add this skill to your agents.

Files (1)
SKILL.md
4.6 KB
---
name: defense-in-depth
description: Use when invalid data causes failures deep in execution - validates at every layer data passes through to make bugs structurally impossible rather than temporarily fixed
user-invocable: false
---

# 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.

## When to Use

**Use when:**
- Invalid data caused a bug deep in the call stack
- Data crosses system boundaries (API → service → storage)
- Multiple code paths can reach the same vulnerable code
- Tests mock intermediate layers (bypassing validation)

**Don't use when:**
- Pure internal function with single caller (validate at caller)
- Data already validated by framework/library you trust
- Adding validation would duplicate identical checks at adjacent layers

## The Four Layers

### Layer 1: Entry Point Validation
**Purpose:** Reject invalid input at API/system boundary

```typescript
function createProject(name: string, workingDirectory: string) {
  if (!workingDirectory?.trim()) {
    throw new Error('workingDirectory cannot be empty');
  }
  if (!existsSync(workingDirectory)) {
    throw new Error(`workingDirectory does not exist: ${workingDirectory}`);
  }
  // ... proceed
}
```

**When needed:** Always. This is your first line of defense.

### Layer 2: Business Logic Validation
**Purpose:** Ensure data makes sense for this specific operation

```typescript
function initializeWorkspace(projectDir: string, sessionId: string) {
  if (!projectDir) {
    throw new Error('projectDir required for workspace initialization');
  }
  // ... proceed
}
```

**When needed:** When business rules differ from entry validation, or when mocks might bypass Layer 1.

### Layer 3: Environment Guards
**Purpose:** Prevent dangerous operations in specific contexts

```typescript
async function gitInit(directory: string) {
  if (process.env.NODE_ENV === 'test') {
    const normalized = normalize(resolve(directory));
    if (!normalized.startsWith(tmpdir())) {
      throw new Error(`Refusing git init outside temp dir in tests: ${directory}`);
    }
  }
  // ... proceed
}
```

**When needed:** When operation is destructive/irreversible, especially in test environments.

### Layer 4: Debug Instrumentation
**Purpose:** Capture context for forensics when other layers fail

```typescript
async function gitInit(directory: string) {
  logger.debug('git init', { directory, cwd: process.cwd(), stack: new Error().stack });
  // ... proceed
}
```

**When needed:** When debugging is difficult, or when you need to trace how bad data arrived.

## Decision Heuristic

| Situation | Layers Needed |
|-----------|---------------|
| Public API, simple validation | 1 only |
| Data crosses multiple services | 1 + 2 |
| Destructive operations (delete, init, write) | 1 + 2 + 3 |
| Chasing a hard-to-reproduce bug | 1 + 2 + 3 + 4 |
| Tests mock intermediate layers | At minimum: 1 + 3 |

## Applying the Pattern

When you find a bug caused by invalid data:

1. **Trace the data flow** - Where does the bad value originate? Where is it used?
2. **Map checkpoints** - List every function/layer the data passes through
3. **Decide which layers** - Use heuristic above
4. **Add validation** - Entry → business → environment → debug
5. **Test each layer** - Verify Layer 2 catches what bypasses Layer 1

## Quick Reference

| Layer | Question It Answers | Typical Check |
|-------|---------------------|---------------|
| Entry | Is input valid? | Non-empty, exists, correct type |
| Business | Does it make sense here? | Required for this operation, within bounds |
| Environment | Is this safe in this context? | Not in prod, inside temp dir, etc. |
| Debug | How did we get here? | Log stack, cwd, inputs |

## Common Mistakes

| Mistake | Fix |
|---------|-----|
| One validation point, call it done | Add at least entry + business layers |
| Identical checks at adjacent layers | Make each layer check something different |
| Environment guards only in prod | Add them in test too (prevent test pollution) |
| Skipping debug logging | Add it during the bug hunt, keep it |
| Validation but no useful error message | Include the bad value and expected format |

## Key Insight

During testing, each layer catches bugs the others miss:
- Different code paths bypass entry validation
- Mocks bypass business logic checks
- Edge cases need environment guards
- Debug logging identifies structural misuse

**Don't stop at one validation point.** The bug isn't fixed until it's impossible.

Overview

This skill enforces defense-in-depth validation for data that flows through an application. It adds layered checks at entry points, business logic, environment guards, and debug instrumentation so invalid inputs are rejected or caught before they cause deep failures. The goal is to make bugs caused by bad data structurally impossible rather than intermittently fixed.

How this skill works

The skill instruments code paths to validate data at every layer it traverses: API boundaries, service/business logic, environment-sensitive operations, and debug/logging checkpoints. It recommends specific checks for each layer, inserts environment guards for destructive actions, and adds contextual debug information to speed forensic analysis. A decision heuristic maps common situations to the minimal set of layers required.

When to use it

  • A bug was caused by invalid data appearing deep in the call stack
  • Data crosses boundaries (API → service → storage) or multiple components can produce inputs
  • Tests mock intermediate layers and can bypass validation
  • Operations are destructive or irreversible (delete, init, write)
  • Chasing a hard-to-reproduce or flaky production bug

Best practices

  • Always validate at the entry point (reject bad inputs at the boundary)
  • Add business-specific validation where data must meet operation-specific rules
  • Use environment guards to prevent dangerous operations in tests or unexpected contexts
  • Log contextual debug details (stack, cwd, inputs) when other layers fail
  • Keep checks orthogonal — adjacent layers should validate different properties, not duplicate verbatim checks

Example use cases

  • API receives malformed payloads that later cause filesystem or DB errors — add entry + business validation
  • A refactor introduces a new code path that bypasses initial checks — enforce Layer 2 for business logic
  • Tests perform destructive operations in CI — add environment guards to restrict actions to temp dirs
  • Intermittent production failures with unclear origin — enable Layer 4 debug instrumentation to capture context
  • Multi-service workflows where data crosses RPC boundaries — apply entry and business validations on each service

FAQ

Won't multiple checks slow my system or duplicate work?

Layered validation is about guarding different failure modes. Keep checks cheap at entry, and make each layer validate distinct properties (existence, business rules, environment safety). Profiling will show negligible overhead compared to the cost of production failures.

When is a single validation enough?

If a pure internal function has a single caller and no cross-boundary use, validate at the caller. For public APIs or multi-service flows, at least entry plus business checks are recommended.