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