Challenge Overrides
Override challenge types per pattern, optionally scoped to specific branches
Challenge overrides let you change the challenge type for specific patterns in your project. Unlike deny lists (which block entirely), overrides change the difficulty of the confirmation challenge.
How overrides work
An override specifies a pattern ID and a new challenge type. When a command matches that pattern, the override challenge is used instead of the default.
# .shellfirm.yaml
version: 1
overrides:
- id: "fs:recursively_delete"
challenge: Yes
With this override, rm -rf will always require typing "yes" to confirm, regardless of what the global challenge setting is.
Escalation only
Overrides follow the same escalation-only rule as everything else in shellfirm:
The override challenge must be stricter than the current challenge. If it is not, the stricter of the two is used.
Challenge strictness order: Math < Enter < Yes
| Current challenge | Override | Effective challenge |
|---|---|---|
| Math | Enter | Enter |
| Math | Yes | Yes |
| Enter | Math | Enter (override ignored, Enter is stricter) |
| Enter | Yes | Yes |
| Yes | Math | Yes (override ignored, Yes is stricter) |
| Yes | Enter | Yes (override ignored, Yes is stricter) |
Branch-scoped overrides
Overrides can be scoped to specific git branches using on_branches. The override only applies when the current branch matches one of the listed branches.
overrides:
- id: "git:reset_hard"
challenge: Yes
on_branches:
- main
- production
- staging
With this override:
- On
main,production, orstaging:git reset --hardrequires typing "yes" - On
feature/my-thingor any other branch: the default challenge type applies
Multiple overrides
You can specify multiple overrides in a single policy:
overrides:
# Always require "yes" for recursive deletes
- id: "fs:recursively_delete"
challenge: Yes
# Require "yes" for git reset only on protected branches
- id: "git:reset_hard"
challenge: Yes
on_branches:
- main
- production
# Require "yes" for Docker system prune
- id: "docker:system_prune_all"
challenge: Yes
# Require Enter (instead of Math) for stash drops
- id: "git:stash_drop"
challenge: Enter
Combining with global settings
Overrides in .shellfirm.yaml are merged with all other escalation layers. The effective challenge is always the strictest across the full pipeline:
Global challenge setting: Math
Severity escalation (Critical): Yes
Group escalation: (none)
Check-id escalation: (none)
Context escalation (root): Yes
Policy override for rm -rf: Yes
Effective challenge for rm -rf as root: Yes
Policy overrides are the final layer — they can only escalate on top of everything else (severity, group/check-id, and context).
Practical examples
Protect database operations on production branches
version: 1
overrides:
- id: "database:drop_database"
challenge: Yes
on_branches:
- main
- production
- id: "database:truncate_table"
challenge: Yes
on_branches:
- main
- production
Strict protection for a shared infrastructure repo
version: 1
overrides:
- id: "terraform:destroy"
challenge: Yes
- id: "kubernetes:delete_namespace"
challenge: Yes
- id: "aws:delete_stack"
challenge: Yes
- id: "docker:system_prune_all"
challenge: Yes
Gradual escalation by branch
version: 1
overrides:
# Feature branches: Enter confirmation
- id: "fs:recursively_delete"
challenge: Enter
# Protected branches: Yes confirmation
- id: "fs:recursively_delete"
challenge: Yes
on_branches:
- main
- production
When a pattern has multiple overrides (one without branches, one with), the branch-specific override takes priority when the branch matches.