Skip to content
Back to skills

Fail Closed Audit

ASecurity

Use when reviewing any gate, guard, validator, or permission check — audit that it FAILS CLOSED (absent input/config/token/manifest → block, never pass). Prevents the fail-open pattern (bug-230) where a gate silently succeeds because its input is missing.

  • 3 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added September 11, 2026
securityrustgorailstestingbackendsecurity

Security analysis

A100/100

Scanned September 11, 2026

npx -y skills add SkyyRoseLLC/DevSkyy --skill fail-closed-audit --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Fail Closed Audit?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Fail Closed Audit
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/skyyrosellc-fail-closed-audit/badge)](https://www.skillsdirectory.com/skills/skyyrosellc-fail-closed-audit)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: fail-closed-audit
description: Use when reviewing any gate, guard, validator, or permission check — audit that it FAILS CLOSED (absent input/config/token/manifest → block, never pass). Prevents the fail-open pattern (bug-230) where a gate silently succeeds because its input is missing.
origin: SkyyRose
---

# Fail-Closed Audit

The most dangerous defect in this codebase is not a gate that fails — it is a gate that
**passes when it should not**. A guard whose input is absent, whose config is missing, or
whose token is unset, and which therefore returns "OK", is unfalsifiable: it looks green
while protecting nothing. This is **bug-230** (recurred ×6) and it has bitten imagery
gates, embedding gates, encryption-key loading, and the per-collection SOT drift check.

> **Boot first:** read the canonical sources — `SOT.md` → `.wolf/anatomy.md` →
> `.wolf/cerebrum.md` → `.wolf/buglog.json` (grep `bug-230` — the offender may already be
> logged) → `CLAUDE.md`. Do not audit from memory.

## When to Use

- Reviewing any `verify_*` / `validate_*` / `check_*` / `require_*` / `_gate` function.
- Any `if not X: return ok`, `except: return default`, `getenv(...) or <fallback>`.
- Before trusting a green test that exercises a guard — pair with [[mutation-testing]].
- Any manifest / credential / config load that has a "not present" branch.

## The one rule

**Every gate fails CLOSED. Absent manifest / config / token / key = BLOCK, never pass.**
A check that cannot return "no" is not verification — it is a guess with a citation.

## Method

1. **Find the absent-input branch** — what does the guard do when its input is missing,
   empty, unreadable, or unparseable? Trace the `else` / `except` / `... or default` path.
2. **Classify** — does that branch BLOCK (raise / return a failing verdict / refuse) or
   PASS (return ok / generate an ephemeral fallback / skip silently)?
3. **If it passes → fail-open defect.** Rewrite it to block. Real repo precedents:
   - Encryption: `ENCRYPTION_MASTER_KEY` unset in production must `raise`, not mint an
     ephemeral key (silent data loss on restart) — mirror JWT's `_require_secret()`
     `ENVIRONMENT=production` guard.
   - Asset/imagery manifest: a missing `manifest.json` returns a `manifest_missing`
     finding that blocks the paid run — not "zero drift → clean".
   - Generated-artifact guard: never regenerate-then-verify its **own** output (it always
     agrees with itself); compare against the committed tree.
4. **Allow a deliberate skip only when provably deliberate** — e.g. skip in a sparse
   worktree *only* when `core.sparseCheckout=true` AND the path is absent; a full checkout
   with a genuinely missing tree must still fail (see [[test-isolation]]).

## Loop until closed

Bounded, like [[drive-to-green]] — never more than 5 turns, stop if the same result
repeats twice (that is guessing, not auditing):

```
1. Pick a guard.  2. Delete/empty its input (env unset, file removed, token blank).
3. Run the guard → does it BLOCK?  4. If it passes → fix the branch to raise/refuse.
5. Re-run step 2-3.  Repeat for every guard in the surface.  Stop when all block.
```

## Verify from an authoritative source

The audit is only real if the "block" is **observed**, never assumed:

- **Run it with the input removed and read the outcome** — `monkeypatch.delenv(...)` /
  delete the manifest / blank the token, then assert it raises / returns a failing verdict.
  The check must be able to return "no".
- **Prove falsifiability** — disable the guard (mutate it to a no-op) and confirm the
  test now FAILS; restore. If the test still passes with the guard gone, the test never
  tested the guard. This is [[mutation-testing]].
- **Cite `file:line`** for the fixed branch; quote the raising code, not a paraphrase.

## Adversarial pass

- **Before** wide audits: [[adversarial-planning]] — have a genuinely different model
  (Codex via `codex exec`) name the guard you'd *miss*.
- **After** the fix: [[adversarial-verification]] — try to REFUTE "it fails closed now";
  default to fail-open until independently proven otherwise. For security/credential
  guards, use ≥2 skeptics.

## Guardrails · Handoff · Log

- A fix that leaves any absent-input branch returning "ok" is not a fix.
- Backend/security fail-open → hand to `skyyrose-core`; imagery/theme → `skyyrose-design`
  (see `CROSS-PLUGIN.md`), then **re-verify here**.
- Log every fail-open to `.wolf/buglog.json` under the `bug-230` lineage (bump
  `occurrences`, never duplicate), run `scripts/wolf_recurring_sync.py`, and record the
  lesson via [[continuous-learning]].

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.