Skip to content

Configuration

Stephen Cross edited this page Jun 3, 2026 · 5 revisions

Configuration

Config File Location

~/.hermes/custom-dangerous-patterns.yaml

Override with env var:

export HERMES_CUSTOM_PATTERNS_PATH=/path/to/custom-dangerous-patterns.yaml

Block Patterns

These commands trigger the approval prompt:

patterns:
  - pattern: '\bvultr\b'
    description: 'Vultr CLI command'
    examples:
      - 'vultr account info'
      - 'vultr instance list'
Field Required Description
pattern Yes Python regex (matched with `re.IGNORECASE
description Yes Human-readable label shown in the approval prompt
examples No Documentation-only list of example commands

Allow Patterns

Exempt specific commands from approval, even if they match a block pattern:

allow_patterns:
  - pattern: '\bvultr\s+(account\s+info|instance\s+list)\b'
    description: 'Read-only Vultr commands'
Field Required Description
pattern Yes Python regex (same flags as block patterns)
description No Documentation-only label

Deny Patterns

Commands matching deny patterns are blocked immediately without an approval prompt. They are checked before allow and block patterns — deny wraps the original guard function and intercepts first. Useful for commands that should never be run without manual config changes.

deny_patterns:
  - pattern: '\bgit\s+push\s+--force\b'
    description: 'Force git push'
    examples:
      - 'git push --force origin main'
Field Required Description
pattern Yes Python regex (same flags as block patterns)
description Yes Human-readable label shown in the block message
examples No Documentation-only list of example commands

Known limitation: Deny patterns intercept before --yolo/mode=off checks inside the original guard function, so --yolo does not bypass deny patterns. This will be addressed in a future Hermes core integration (v0.5.0).

Additional Pattern Fields

All pattern types (block, allow, deny) support these additional optional fields:

Field Required Type Default Description
enabled No bool true Set false to temporarily disable a pattern without removing it
group No string "" Optional tag for categorization (e.g., cloud, database, testing)
protected No bool false If true, the pattern regex is integrity-checked across sessions

Directory Config Loading

Instead of a single file, you can set your config path to a directory. The plugin loads all *.yaml files in alphabetical order and merges them:

  • Lists (patterns, allow_patterns, deny_patterns) are extended (appended)
  • Scalars override previous values

This is useful for splitting configs by tool or team:

~/.hermes/custom-patterns.d/
├── 10-cloud.yaml       # cloud CLI patterns
├── 20-database.yaml     # database patterns
└── 30-deployment.yaml   # deploy tool patterns

Set the directory path via $HERMES_CUSTOM_PATTERNS_PATH or symlink the config path to a directory.

Config Integrity

Starting in v0.2.0, the plugin tracks configuration integrity across sessions:

  • A SHA-256 hash of the full config YAML is stored in ~/.hermes/.custom-patterns-hash. If the config changes between sessions, a WARNING is logged with old and new pattern counts.
  • Protected patterns (protected: true) have their individual regex hashed and tracked. If a protected pattern is modified or removed, a CRITICAL warning is logged at startup.
  • Allow shadowing detection warns if an allow pattern could bypass a built-in dangerous pattern without a corresponding custom block pattern.

These checks are detective, not preventive — they log changes but do not prevent them.

A Note on \b (Word Boundaries)

Patterns use \b to match whole words only. This prevents false positives:

Pattern Matches Doesn't match
\bvultr\b vultr instance list echo vultr_test, my-vultr-server
\baws\s+ec2\b aws ec2 describe-instances aws-ec2-tool, paws ec2
\bterraform destroy\b terraform destroy -auto-approve echo "terraform destroy" in a script

Without \b, \bvultr would match any string containing "vultr" — including hostnames, variable names, or unrelated commands.

Tip: Use single-quoted YAML strings for patterns — backslashes pass through literally ('\bvultr\b'), avoiding the double-escaping needed with double quotes ("\\bvultr\\b").

Evaluation Order

Note: The runtime evaluation order differs because the deny-pattern wrapper runs before the original check_all_command_guards() function that contains the yolo/mode=off check. Deny patterns intercept --yolo and mode=off by design.

Each check is tagged with its source:

  • [Plugin] — this plugin's custom checks
  • [Hermes] — Hermes Agent's built-in checks
 1. [Plugin]  Deny patterns (custom)        → BLOCKED immediately, no prompt
               (wraps original check_all_command_guards)
 2. [Hermes]  Hardline check                → blocked unconditionally
 3. [Hermes]  Sudo stdin guard              → blocked unconditionally
 4. [Hermes]  Yolo / mode=off               → bypasses steps 5-7
 5. [Plugin]  Allow patterns (custom)       → command runs, no prompt (allow wins)
 6. detect_dangerous_command():             — same approval prompt for both —
    a. [Plugin]  Block patterns (custom)    → [o]nce/[s]ession/[a]lways/[d]eny
    b. [Hermes]  Built-in patterns          → [o]nce/[s]ession/[a]lways/[d]eny
 7. [Hermes]  Tirith security scan          → approval prompt if findings

Key rules:

  • Allow wins over block. If a command matches both an allow pattern and a block pattern, allow wins and no prompt is shown.
  • Deny wins over allow. Deny patterns are checked before allow patterns. If a command matches a deny pattern, it is blocked before allow patterns are even evaluated.
  • Deny is immediate-block; block is approval-prompt. Block patterns and built-in patterns both go through the same detect_dangerous_command() approval flow. Deny patterns skip it entirely.
  • Deny bypasses yolo. Deny patterns are evaluated outside the original guard function, so --yolo does not bypass them.

Full Example

# ~/.hermes/custom-dangerous-patterns.yaml
#
# TIP: Use single-quoted strings for patterns — backslashes pass through
# literally:  '\bvultr\b'  not  "\\bvultr\\b"

patterns:
  # ── Cloud CLI tools (destructive) ────────────────────────────────
  - pattern: '\bvultr\s+(instance\s+create|instance\s+delete|snapshot\s+create|snapshot\s+delete)\b'
    description: 'Vultr destructive instance/snapshot command'
    examples:
      - 'vultr instance delete --instance-id cb670a12-e4f5-6d78-ab90-1234567890ab'
      - 'vultr snapshot delete --snapshot-id 5a3b2c1d'

  - pattern: '\bterraform\s+(destroy|apply)\b'
    description: 'Terraform destroy/apply (infrastructure mutation)'
    examples:
      - 'terraform destroy -auto-approve'
      - 'terraform apply -auto-approve'

  - pattern: '\baws\s+(ec2|s3|rds|iam|lambda|cloudformation)\b'
    description: 'AWS CLI mutating service command'
    examples:
      - 'aws ec2 terminate-instances --instance-ids i-12345'
      - 'aws s3 rb s://my-bucket --force'
      - 'aws rds delete-db-instance --db-instance-identifier mydb'

  - pattern: '\bgcloud\s+(compute\s+instances\s+delete|projects\s+delete)\b'
    description: 'GCP destructive command'
    examples:
      - 'gcloud compute instances delete my-vm --zone=us-central1-a'

  - pattern: '\boci\s+(compute\s+instance\s+terminate|database\s+db\s+system\s+delete|network\s+vcn\s+delete)\b'
    description: 'Oracle Cloud destructive command'
    examples:
      - 'oci compute instance terminate --instance-id ocid1.instance.oc1..aaaaaaaa'
      - 'oci database db-system delete --db-system-id ocid1.dbsystem.oc1..aaaaaaaa'

  - pattern: '\bdoctl\s+(compute\s+droplet\s+delete|kubernetes\s+cluster\s+delete|databases\s+delete)\b'
    description: 'DigitalOcean destructive command'
    examples:
      - 'doctl compute droplet delete 12345678'
      - 'doctl kubernetes cluster delete my-cluster'

  - pattern: '\bkubectl\s+delete\s+namespace\b'
    description: 'Kubernetes namespace deletion'
    examples:
      - 'kubectl delete namespace staging'

  # ── Deployment tools ─────────────────────────────────────────────
  - pattern: '\bcap\s+\w+\s+deploy\b'
    description: 'Capistrano production deploy'
    examples:
      - 'cap production deploy'

  - pattern: '\bfab\s+\w*\s*deploy\b'
    description: 'Fabric deploy'
    examples:
      - 'fab deploy production'

  # ── Database operations ──────────────────────────────────────────
  - pattern: '\bDROP\s+(TABLE|DATABASE)\b'
    description: 'SQL DROP statement'
    examples:
      - 'DROP TABLE users'
      - 'DROP DATABASE production'

  - pattern: '\bmongodump\b.*--drop\b'
    description: 'MongoDB dump with --drop (overwrites existing data)'
    examples:
      - 'mongodump --drop --db production'

# ── Allow patterns ────────────────────────────────────────────────
# Commands matching these are EXEMPT from approval, even if they
# also match a blocked pattern. Evaluated BEFORE block patterns.
# Allow wins over block.

allow_patterns:
  # ── Read-only cloud commands (safe) ─────────────────────────────
  - pattern: '\bvultr\s+(account\s+info|instance\s+list|dns\s+list|plan\s+list)\b'
    description: 'Read-only Vultr commands'

  - pattern: '\baws\s+(ec2\s+describe|s3\s+ls|s3\s+cp.*--dry-run|iam\s+list)\b'
    description: 'AWS read-only commands'

  - pattern: '\bterraform\s+(plan|state\s+list|output)\b'
    description: 'Terraform read-only commands'

  - pattern: '\boci\s+(compute\s+instance\s+list|network\s+vcn\s+list|database\s+db\s+system\s+list)\b'
    description: 'Oracle Cloud read-only commands'

  - pattern: '\bdoctl\s+(compute\s+droplet\s+list|kubernetes\s+cluster\s+list|databases\s+list)\b'
    description: 'DigitalOcean read-only commands'

  # ── Help and utility (safe) ─────────────────────────────────────
  - pattern: '\b(vultr|gcloud|aws|terraform|kubectl|oci|doctl)\s+(-h|--help|help)\b'
    description: 'Help flags are safe'

  - pattern: '\b(vultr|gcloud|aws|terraform|oci|doctl)\s+completion\b'
    description: 'Shell completion scripts are safe'

Edge Cases

Scenario Behavior
Config file missing Plugin loads silently, no patterns injected
Config file invalid YAML Log WARNING, plugin loads with empty pattern list
Invalid regex in pattern Log WARNING for that pattern, skip it, load valid ones
Pattern matches but allow also matches Allow wins — no prompt
Deny pattern match Blocked immediately, no prompt
Config changed since last session WARNING logged with old/new pattern counts
Protected pattern missing/modified CRITICAL warning logged
Allow pattern shadows built-in pattern WARNING logged with details
--yolo mode Block patterns (custom + built-in) bypassed. Deny patterns still block — checked outside the original guard function.
approvals.mode: off Block patterns bypassed. Deny patterns still block — checked outside the original guard function.
approvals.mode: smart Custom patterns assessed by auxiliary LLM
Cron session + cron_mode: deny Custom patterns blocked in cron
Container backend (docker, etc.) All approval checks skipped (sandboxed)
command_allowlist "always" choice Persisted to config.yaml — survives restarts

Clone this wiki locally