Deny Lists
Block dangerous commands entirely with no challenge — just denied
Deny lists are the strongest enforcement mechanism in shellfirm. Commands matching a denied pattern ID are blocked immediately with no challenge prompt and no way to proceed.
How deny lists work
When a command matches a pattern that is on the deny list, shellfirm:
- Shows the matched pattern and description
- Displays "DENIED" -- no challenge is offered
- The command is not executed
- The event is logged to the audit trail with outcome
DENIED
There is no way to override a deny-listed pattern through a challenge. The command is simply blocked.
Configuring deny lists
In project policy (.shellfirm.yaml)
# .shellfirm.yaml
version: 1
deny:
- "git:force_push"
- "fs:format_filesystem"
- "kubernetes:delete_namespace"
- "database:drop_database"
In global settings (~/.shellfirm/settings.yaml)
deny_patterns_ids:
- "git:force_push"
- "fs:format_filesystem"
Cumulative unions
Deny lists are merged as a union across all sources. If your global settings deny git:force_push and a project policy denies kubernetes:delete_namespace, both are active in that project.
Global deny list: [git:force_push, fs:format_filesystem]
Project policy deny list: [kubernetes:delete_namespace, database:drop_database]
Effective deny list: [git:force_push, fs:format_filesystem, kubernetes:delete_namespace, database:drop_database]
A project policy cannot remove a pattern from the global deny list. This is the additive-only invariant in action.
Finding pattern IDs
To see all available pattern IDs that you can add to a deny list:
shellfirm check --command "any command" --json | jq '.matched_rules[].id'
Or list all active patterns:
shellfirm status
Common pattern IDs you might want to deny:
| Pattern ID | Description |
|---|---|
git:force_push | git push --force |
git:reset_hard | git reset --hard |
fs:recursively_delete | rm -rf |
fs:format_filesystem | mkfs commands |
kubernetes:delete_namespace | kubectl delete namespace |
database:drop_database | SQL DROP DATABASE |
docker:system_prune_all | docker system prune -a |
terraform:destroy | terraform destroy |
Deny lists and AI agents
Deny lists are particularly important for AI agent safety. When an agent calls check_command via the MCP server and the command matches a denied pattern, the response includes:
{
"allowed": false,
"denial_reason": "Command matches a deny-listed pattern",
"matched_rules": [{
"id": "git:force_push",
"description": "Force push can overwrite remote history.",
"severity": "High",
"group": "git"
}]
}
The agent sees the denial and can choose a safer alternative.
Practical example
Consider a team that has been burned by accidental force pushes:
# .shellfirm.yaml
version: 1
deny:
- "git:force_push"
Now when anyone in the team runs:
git push --force origin main
They see:
DENIED: git:force_push
Force push can overwrite remote history.
Alternative: git push --force-with-lease
The command does not execute. No amount of typing "yes" or solving math problems will change this -- the pattern is denied.
To force-push, the developer would need to:
- Remove the deny entry from
.shellfirm.yaml(requires a PR and code review) - Or bypass shellfirm entirely with
\command git push --force(which is intentional and visible in git history)