Docs

Security testing

Nifra treats security claims as executable invariants. A rule is useful only when a hostile input, a deliberate mutation, or a recovery fault would make a test fail if the protection disappeared.

One invariant, several kinds of evidence

security-invariants.json is the index for the repository's security claims. Each entry names the behavior, regression tests, mutation targets, and documentation that explain it. bun run check:security-invariants fails when a referenced test or target is removed, so the index cannot silently become a list of aspirations.

The ledger is intentionally separate from an application's secrets and tenant data. Add a reduced public regression fixture for a real finding; keep private application details in the private test suite.

Mutation testing

Mutation testing changes one condition, operator, return value, or statement at a time. A security target passes only when its tests kill every meaningful mutant. A surviving mutant needs a regression test or an explicit equivalence explanation inmutation-equivalents.json; adding a survivor to a baseline is a temporary record, not proof of safety.

Shell
# Validate every invariant has executable evidence
bun run check:security-invariants

# Run the fast changed-line mutation report
bun scripts/mutate.ts --changed

# Run a selected security target locally
bun scripts/mutate.ts --target core-capabilities --gate

# Build artifacts required by the Node, Deno, and workerd lanes
bun run build

# Exercise hostile requests across supported runtimes
bun run check:runtime-differential

What to test at a security boundary

  • Authorization: denial happens before the handler, database write, or side effect; ownership is checked for the requested resource.
  • Replay and recovery: repeated idempotency keys do not repeat effects, and an uncertain commit becomes an explicit reconciliation state.
  • Input limits: raw and multipart bodies stop at their read budget, malformed encodings are rejected, and prototype-pollution keys cannot alter output objects.
  • Data boundaries: response projection strips fields, errors redact provider details, and server-only imports cannot reach browser chunks.
  • Runtime parity: Bun, Node, Deno, and Workers agree on rejection status, headers, redaction, and side-effect behavior for the same hostile request.

Adding a new edge case

Start with the smallest reproduction that proves the failure. Give it a stable diagnostic or test name, add the fixed behavior to the relevant invariant, add a mutation that would remove the guard when practical, and document the trigger, consequence, fix, and verification command. This keeps the docs, CLI, MCP, and test suite growing from evidence.