-
Notifications
You must be signed in to change notification settings - Fork 0
Configuration
~/.hermes/custom-dangerous-patterns.yaml
Override with env var:
export HERMES_CUSTOM_PATTERNS_PATH=/path/to/custom-dangerous-patterns.yamlThese 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 |
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 |
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).
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 |
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.
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, aWARNINGis 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, aCRITICALwarning 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.
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").
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--yoloandmode=offby 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
--yolodoes not bypass them.
# ~/.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'| 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 |