$ shellfirm

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:

  1. Shows the matched pattern and description
  2. Displays "DENIED" -- no challenge is offered
  3. The command is not executed
  4. 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 IDDescription
git:force_pushgit push --force
git:reset_hardgit reset --hard
fs:recursively_deleterm -rf
fs:format_filesystemmkfs commands
kubernetes:delete_namespacekubectl delete namespace
database:drop_databaseSQL DROP DATABASE
docker:system_prune_alldocker system prune -a
terraform:destroyterraform 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:

  1. Remove the deny entry from .shellfirm.yaml (requires a PR and code review)
  2. Or bypass shellfirm entirely with \command git push --force (which is intentional and visible in git history)