|
# Block Destructive Operations — Pattern File |
|
# |
|
# Each rule requires: |
|
# pattern - regex matched against the Bash command |
|
# description - what the matched operation does and its effect |
|
# reason - why it is blocked or flagged (safety/security concern) |
|
# mode - "block" (exit 2) or "ask" (exit 0 + permission dialog) |
|
# |
|
# Optional: |
|
# case_insensitive = true (for SQL patterns; shell patterns match as-is) |
|
# |
|
# Rules are evaluated in order; the first match wins. |
|
# Structure: [[category.rules]] groups rules by concern for readability. |
|
|
|
# ───────────────────────────────────────────────────────────────────────────── |
|
# GIT |
|
# ───────────────────────────────────────────────────────────────────────────── |
|
|
|
[git] |
|
description = "Version control history and branch operations" |
|
|
|
[[git.rules]] |
|
pattern = '\bgit\s+filter-branch\b' |
|
description = "Rewrites the entire git commit history" |
|
reason = "Irreversibly rewrites history; filtered-out work is permanently lost once the reflog expires" |
|
mode = "block" |
|
|
|
[[git.rules]] |
|
pattern = '\bgit\s+filter-repo\b' |
|
description = "Rewrites the entire git commit history (modern replacement for filter-branch)" |
|
reason = "Irreversibly rewrites history; filtered-out work is permanently lost" |
|
mode = "block" |
|
|
|
[[git.rules]] |
|
pattern = '\bgit\s+reflog\s+expire\b' |
|
description = "Expires reflog entries, removing the safety net for recovering lost commits" |
|
reason = "Destroys the reflog; without it, accidental resets or lost branches cannot be recovered" |
|
mode = "block" |
|
|
|
# Force push: --force without --force-with-lease (both long and short flags) |
|
[[git.rules]] |
|
pattern = '\bgit\s+push\b(?!.*--force-with-lease).*\s--force\b' |
|
description = "Force-pushes changes to the remote, overwriting its history unconditionally" |
|
reason = "Can erase other contributors' work without warning; use --force-with-lease instead" |
|
mode = "block" |
|
|
|
[[git.rules]] |
|
pattern = '\bgit\s+push\b(?!.*--force-with-lease).*\s-[a-zA-Z]*f[a-zA-Z]*\b' |
|
description = "Force-pushes changes to the remote using the short flag" |
|
reason = "Can erase other contributors' work without warning; use --force-with-lease instead" |
|
mode = "block" |
|
|
|
[[git.rules]] |
|
pattern = '\bgit\s+push\s+--delete\b' |
|
description = "Deletes a remote branch or tag" |
|
reason = "Remote branch deletion affects all collaborators; confirm the target ref" |
|
mode = "ask" |
|
|
|
[[git.rules]] |
|
pattern = '\bgit\s+push\b.*\s--force-with-lease\b' |
|
description = "Force-pushes but verifies no one has pushed since your last fetch" |
|
reason = "Still rewrites remote history; safer than --force but can still discard work if the lease is stale" |
|
mode = "ask" |
|
|
|
[[git.rules]] |
|
pattern = '\bgit\s+reset\s+--hard\b' |
|
description = "Resets the working tree and index to a commit, discarding all uncommitted changes" |
|
reason = "Permanently discards uncommitted work; any unsaved changes are unrecoverable" |
|
mode = "ask" |
|
|
|
[[git.rules]] |
|
pattern = '\bgit\s+clean\s+(?:-[a-zA-Z]*f[a-zA-Z]*\b|-[a-zA-Z]*d[a-zA-Z]*f[a-zA-Z]*\b)' |
|
description = "Removes untracked files (and optionally directories) from the working tree" |
|
reason = "Permanently deletes untracked files that have never been committed; they cannot be recovered" |
|
mode = "ask" |
|
|
|
[[git.rules]] |
|
pattern = '\bgit\s+stash\s+drop\b' |
|
description = "Permanently deletes a single stash entry" |
|
reason = "Stash entries are not in the reflog; a dropped stash cannot be recovered" |
|
mode = "ask" |
|
|
|
[[git.rules]] |
|
pattern = '\bgit\s+stash\s+clear\b' |
|
description = "Permanently deletes all stash entries at once" |
|
reason = "All stash entries are permanently lost with no recovery path" |
|
mode = "ask" |
|
|
|
[[git.rules]] |
|
pattern = '\bgit\s+branch\s+(?:-[a-zA-Z]*D[a-zA-Z]*\b|-D\b)' |
|
description = "Force-deletes a branch even if it has unmerged commits" |
|
reason = "Unmerged commits may become unreachable and eventually garbage-collected" |
|
mode = "ask" |
|
|
|
[[git.rules]] |
|
pattern = '\bgit\s+checkout\s+\.\s*$' |
|
description = "Discards all unstaged changes in the working directory" |
|
reason = "Reverts all modified tracked files to their last committed state; unsaved changes are lost" |
|
mode = "ask" |
|
|
|
[[git.rules]] |
|
pattern = '\bgit\s+restore\s+\.\s*$' |
|
description = "Discards all unstaged changes in the working directory" |
|
reason = "Reverts all modified tracked files to their last committed state; unsaved changes are lost" |
|
mode = "ask" |
|
|
|
[[git.rules]] |
|
pattern = '\bgit\s+add\s+(?:\.\s*$|-A\b)' |
|
description = "Stages all files in the repository, including untracked files" |
|
reason = "May accidentally stage sensitive files (.env, credentials) or large binaries; prefer staging specific files" |
|
mode = "ask" |
|
|
|
[[git.rules]] |
|
pattern = '\bgh\s+repo\s+delete\b' |
|
description = "Deletes an entire GitHub repository" |
|
reason = "Repository deletion removes all code, issues, PRs, and history permanently" |
|
mode = "block" |
|
|
|
# ───────────────────────────────────────────────────────────────────────────── |
|
# FILESYSTEM |
|
# ───────────────────────────────────────────────────────────────────────────── |
|
|
|
[filesystem] |
|
description = "File and directory operations with broad or irreversible effects" |
|
|
|
# rm -rf targeting absolute or home paths (catastrophic) |
|
[[filesystem.rules]] |
|
pattern = '\brm\s+(?:-[a-zA-Z]*\s+)*-[a-zA-Z]*(?:[rR][a-zA-Z]*[fF]|[fF][a-zA-Z]*[rR])[a-zA-Z]*\s+(?:~|/)' |
|
description = "Recursively force-deletes files starting from the home directory or an absolute path" |
|
reason = "Irreversibly deletes entire directory trees; absolute-path rm -rf is almost always catastrophic" |
|
mode = "block" |
|
|
|
[[filesystem.rules]] |
|
pattern = '\bmkfs\.' |
|
description = "Formats a disk partition with a new filesystem" |
|
reason = "Formatting a partition irreversibly erases all data on it" |
|
mode = "block" |
|
|
|
[[filesystem.rules]] |
|
pattern = '\bdd\b.*\bof=/dev/[sh]d' |
|
description = "Writes raw data directly to a disk device" |
|
reason = "dd to a disk device can overwrite partition tables and data with no recovery" |
|
mode = "block" |
|
|
|
[[filesystem.rules]] |
|
pattern = '\bchmod\s+777\s+/' |
|
description = "Sets world-readable/writable/executable permissions on a system path" |
|
reason = "chmod 777 on system paths is a severe security risk" |
|
mode = "block" |
|
|
|
# General recursive rm (not just -rf to absolute paths) |
|
[[filesystem.rules]] |
|
pattern = '\brm\s+(?:-[a-zA-Z]*\s+)*-[a-zA-Z]*r\b' |
|
description = "Recursively deletes files and directories" |
|
reason = "Recursive deletion can remove large directory trees; confirm this is the intended target" |
|
mode = "ask" |
|
|
|
[[filesystem.rules]] |
|
pattern = '\bchmod\s+-R\b' |
|
description = "Recursively changes file permissions on an entire directory tree" |
|
reason = "Recursive permission changes can break file access across an entire tree" |
|
mode = "ask" |
|
|
|
[[filesystem.rules]] |
|
pattern = '\bchown\s+-R\b' |
|
description = "Recursively changes file ownership on an entire directory tree" |
|
reason = "Recursive ownership changes can lock out current users from their own files" |
|
mode = "ask" |
|
|
|
[[filesystem.rules]] |
|
pattern = '\bhistory\s+-c\b' |
|
description = "Clears the current shell history" |
|
reason = "Clearing history removes the audit trail of commands run in this session" |
|
mode = "ask" |
|
|
|
# ───────────────────────────────────────────────────────────────────────────── |
|
# PROCESS MANAGEMENT |
|
# ───────────────────────────────────────────────────────────────────────────── |
|
|
|
[process] |
|
description = "Process signaling and termination" |
|
|
|
[[process.rules]] |
|
pattern = '\bkillall\b' |
|
description = "Sends a signal to all processes matching a name" |
|
reason = "killall can terminate critical system processes or user applications indiscriminately" |
|
mode = "ask" |
|
|
|
[[process.rules]] |
|
pattern = '\bpkill\b' |
|
description = "Sends a signal to processes matching a pattern" |
|
reason = "pkill with a broad pattern can terminate unintended processes" |
|
mode = "ask" |
|
|
|
[[process.rules]] |
|
pattern = '\bkill\s+-9\b' |
|
description = "Sends SIGKILL to a process, forcing immediate termination" |
|
reason = "SIGKILL prevents the process from cleaning up; data corruption or resource leaks may result" |
|
mode = "ask" |
|
|
|
# ───────────────────────────────────────────────────────────────────────────── |
|
# CLOUD PROVIDERS |
|
# ───────────────────────────────────────────────────────────────────────────── |
|
|
|
[cloud] |
|
description = "Cloud provider CLI operations that delete or destroy resources" |
|
|
|
# AWS |
|
[[cloud.rules]] |
|
pattern = '\baws\s+s3\s+rm\b.*\s--recursive\b' |
|
description = "Recursively deletes all objects under an S3 prefix" |
|
reason = "S3 deletions are permanent; recursive removal can erase entire buckets of production data" |
|
mode = "ask" |
|
|
|
[[cloud.rules]] |
|
pattern = '\baws\s+s3\s+sync\b.*\s--delete\b' |
|
description = "Syncs to S3 and deletes remote objects not present in the source" |
|
reason = "Can silently erase production data that exists in S3 but not in the sync source" |
|
mode = "ask" |
|
|
|
# GCP |
|
[[cloud.rules]] |
|
pattern = '\bgcloud\b.*\s(?:delete|destroy)\b' |
|
description = "Deletes or destroys a Google Cloud resource" |
|
reason = "Cloud resource deletions may be irreversible and can affect live production systems" |
|
mode = "ask" |
|
|
|
# Firebase |
|
[[cloud.rules]] |
|
pattern = '\bfirebase\b.*\s(?:delete|projects:delete)\b' |
|
description = "Deletes a Firebase resource or entire project" |
|
reason = "Firebase project deletion is permanent and cannot be undone" |
|
mode = "ask" |
|
|
|
# Vercel |
|
[[cloud.rules]] |
|
pattern = '\bvercel\b.*\s(?:projects\s+rm|remove)\b' |
|
description = "Removes a Vercel project or deployment" |
|
reason = "Vercel project removal permanently deletes all deployments and configuration" |
|
mode = "ask" |
|
|
|
# Netlify |
|
[[cloud.rules]] |
|
pattern = '\bnetlify\b.*\s(?:sites:delete|delete)\b' |
|
description = "Deletes a Netlify site" |
|
reason = "Site deletion removes all deploys and configuration permanently" |
|
mode = "ask" |
|
|
|
# Cloudflare |
|
[[cloud.rules]] |
|
pattern = '\bwrangler\b.*\s(?:delete|destroy)\b' |
|
description = "Deletes a Cloudflare Workers or Pages resource" |
|
reason = "Cloudflare resource deletion affects live traffic immediately" |
|
mode = "ask" |
|
|
|
# Heroku |
|
[[cloud.rules]] |
|
pattern = '\bheroku\s+apps:destroy\b' |
|
description = "Destroys a Heroku application" |
|
reason = "App destruction removes all dynos, add-ons, and configuration permanently" |
|
mode = "ask" |
|
|
|
# Fly.io |
|
[[cloud.rules]] |
|
pattern = '\bfly\s+apps\s+destroy\b' |
|
description = "Destroys a Fly.io application" |
|
reason = "App destruction removes all machines and volumes permanently" |
|
mode = "ask" |
|
|
|
# DigitalOcean |
|
[[cloud.rules]] |
|
pattern = '\bdoctl\b.*\s(?:delete|destroy)\b' |
|
description = "Deletes a DigitalOcean resource" |
|
reason = "Resource deletion may destroy droplets, databases, or storage volumes" |
|
mode = "ask" |
|
|
|
# Supabase |
|
[[cloud.rules]] |
|
pattern = '\bsupabase\b.*\s(?:projects\s+delete|db\s+reset)\b' |
|
description = "Deletes a Supabase project or resets a database" |
|
reason = "Project deletion or database reset permanently removes data and configuration" |
|
mode = "ask" |
|
|
|
# ───────────────────────────────────────────────────────────────────────────── |
|
# INFRASTRUCTURE & ORCHESTRATION |
|
# ───────────────────────────────────────────────────────────────────────────── |
|
|
|
[infra] |
|
description = "Infrastructure-as-code and container orchestration operations" |
|
|
|
# Terraform / OpenTofu |
|
[[infra.rules]] |
|
pattern = '\b(?:terraform|tofu)\s+destroy\b' |
|
description = "Destroys all infrastructure resources managed by this workspace" |
|
reason = "Irreversibly removes cloud resources including databases, servers, and networks" |
|
mode = "ask" |
|
|
|
# Pulumi |
|
[[infra.rules]] |
|
pattern = '\bpulumi\s+destroy\b' |
|
description = "Destroys all resources in a Pulumi stack" |
|
reason = "Irreversibly removes infrastructure resources managed by this stack" |
|
mode = "ask" |
|
|
|
# Serverless Framework |
|
[[infra.rules]] |
|
pattern = '\b(?:serverless|sls)\s+remove\b' |
|
description = "Removes a Serverless Framework service and all its cloud resources" |
|
reason = "Removes Lambda functions, API Gateways, and associated resources permanently" |
|
mode = "ask" |
|
|
|
# AWS SAM |
|
[[infra.rules]] |
|
pattern = '\bsam\s+delete\b' |
|
description = "Deletes an AWS SAM application stack" |
|
reason = "Removes all CloudFormation resources created by the stack" |
|
mode = "ask" |
|
|
|
# Kubernetes |
|
[[infra.rules]] |
|
pattern = '\bkubectl\s+delete\b.*\s(?:--all\b|-A\b|--all-namespaces\b)' |
|
description = "Deletes Kubernetes resources across all namespaces or all resources of a type" |
|
reason = "Can take down entire cluster workloads; --all in core namespaces removes system components" |
|
mode = "block" |
|
|
|
[[infra.rules]] |
|
pattern = '\bkubectl\s+delete\s+namespace\b' |
|
description = "Deletes an entire Kubernetes namespace and all resources within it" |
|
reason = "Namespace deletion cascades to all pods, services, and configs in the namespace" |
|
mode = "ask" |
|
|
|
# Helm |
|
[[infra.rules]] |
|
pattern = '\bhelm\s+(?:uninstall|delete)\b' |
|
description = "Uninstalls a Helm release and removes all associated Kubernetes resources" |
|
reason = "Removes all deployed resources; data in PersistentVolumeClaims may be lost" |
|
mode = "ask" |
|
|
|
[[infra.rules]] |
|
pattern = '\bhelm\s+(?:install|upgrade)\b' |
|
description = "Installs or upgrades a Helm chart on a Kubernetes cluster" |
|
reason = "Helm install/upgrade modifies live cluster workloads" |
|
mode = "ask" |
|
|
|
# Docker |
|
[[infra.rules]] |
|
pattern = '\bdocker\s+system\s+prune\b.*\s-a\b' |
|
description = "Removes all unused Docker images, containers, volumes, and build cache" |
|
reason = "Removes all cached images; recovery requires re-pulling and rebuilding everything" |
|
mode = "ask" |
|
|
|
[[infra.rules]] |
|
pattern = '\bdocker\s+(?:rm|rmi)\s+-f\b' |
|
description = "Force-removes Docker containers or images" |
|
reason = "Force removal skips confirmation and may disrupt running workloads" |
|
mode = "ask" |
|
|
|
[[infra.rules]] |
|
pattern = '\bdocker\s+volume\s+(?:rm|prune)\b' |
|
description = "Removes Docker volumes containing persistent data" |
|
reason = "Volume removal permanently deletes data stored outside containers" |
|
mode = "ask" |
|
|
|
# ───────────────────────────────────────────────────────────────────────────── |
|
# DATABASES |
|
# ───────────────────────────────────────────────────────────────────────────── |
|
|
|
[database] |
|
description = "Database operations that delete or wipe data" |
|
|
|
# Redis |
|
[[database.rules]] |
|
pattern = '\bFLUSHALL\b' |
|
case_insensitive = true |
|
description = "Deletes all keys from all Redis databases simultaneously" |
|
reason = "FLUSHALL is an immediate, unrecoverable wipe of all Redis data across every database" |
|
mode = "block" |
|
|
|
[[database.rules]] |
|
pattern = '\bFLUSHDB\b' |
|
case_insensitive = true |
|
description = "Deletes all keys from the currently selected Redis database" |
|
reason = "FLUSHDB immediately and permanently removes all data from the selected database" |
|
mode = "block" |
|
|
|
[[database.rules]] |
|
pattern = '\bredis-cli\b.*\bFLUSH' |
|
case_insensitive = true |
|
description = "Invokes a Redis FLUSH command via the CLI" |
|
reason = "Redis FLUSH commands permanently wipe all data with no recovery" |
|
mode = "block" |
|
|
|
# MongoDB |
|
[[database.rules]] |
|
pattern = 'dropDatabase\s*\(' |
|
description = "Drops an entire MongoDB database and all its collections" |
|
reason = "MongoDB dropDatabase permanently removes the database; there is no undo operation" |
|
mode = "block" |
|
|
|
[[database.rules]] |
|
pattern = '\.\s*drop\s*\(\s*\)' |
|
description = "Drops a MongoDB collection and all documents within it" |
|
reason = "Collection drops are permanent; all documents are immediately and irreversibly deleted" |
|
mode = "block" |
|
|
|
[[database.rules]] |
|
pattern = '\b(?:mongosh?|mongo)\s+.*--eval\s+.*drop' |
|
description = "Drops a MongoDB database or collection via CLI eval" |
|
reason = "MongoDB drop operations are permanent and irreversible" |
|
mode = "block" |
|
|
|
# PostgreSQL |
|
[[database.rules]] |
|
pattern = '\bdropdb\b' |
|
description = "Drops a PostgreSQL database via the CLI utility" |
|
reason = "dropdb permanently deletes the database and all its data" |
|
mode = "block" |
|
|
|
# MySQL |
|
[[database.rules]] |
|
pattern = '\bmysqladmin\s+drop\b' |
|
description = "Drops a MySQL database via the mysqladmin utility" |
|
reason = "mysqladmin drop permanently deletes the database" |
|
mode = "block" |
|
|
|
# SQL (generic — case-insensitive) |
|
[[database.rules]] |
|
pattern = '(?:^|\b)DROP\s+(?:TABLE|DATABASE|SCHEMA)\b' |
|
case_insensitive = true |
|
description = "Drops a SQL table, database, or schema and all data within it" |
|
reason = "SQL DROP is irreversible; all data in the dropped object is permanently deleted" |
|
mode = "block" |
|
|
|
[[database.rules]] |
|
pattern = '\bDELETE\s+FROM\s+\w+\s*;' |
|
case_insensitive = true |
|
description = "Deletes every row from a SQL table (no WHERE clause)" |
|
reason = "DELETE without WHERE removes all rows; this is almost never intentional" |
|
mode = "block" |
|
|
|
[[database.rules]] |
|
pattern = '\bDELETE\s+FROM\s+\w+\s*$' |
|
case_insensitive = true |
|
description = "Deletes every row from a SQL table (no WHERE clause)" |
|
reason = "DELETE without WHERE removes all rows; add a WHERE clause to scope the deletion" |
|
mode = "block" |
|
|
|
[[database.rules]] |
|
pattern = '\bTRUNCATE\s+(?:TABLE\s+)?\w+' |
|
case_insensitive = true |
|
description = "Removes all rows from a SQL table without logging individual deletions" |
|
reason = "TRUNCATE is faster than DELETE but equally destructive; all data is permanently removed" |
|
mode = "ask" |
|
|
|
# ───────────────────────────────────────────────────────────────────────────── |
|
# PUBLISHING & DISTRIBUTION |
|
# ───────────────────────────────────────────────────────────────────────────── |
|
|
|
[publishing] |
|
description = "Package and image publishing to registries" |
|
|
|
[[publishing.rules]] |
|
pattern = '\b(?:npm|yarn|pnpm)\s+publish\b' |
|
description = "Publishes a package to the npm registry" |
|
reason = "Publishing is a public, largely irreversible action; verify version and contents first" |
|
mode = "ask" |
|
|
|
[[publishing.rules]] |
|
pattern = '\bnpm\s+unpublish\b' |
|
description = "Removes a published package version from the npm registry" |
|
reason = "Unpublishing can break downstream consumers who depend on this version" |
|
mode = "ask" |
|
|
|
[[publishing.rules]] |
|
pattern = '\bdocker\s+push\b' |
|
description = "Pushes a container image to a registry" |
|
reason = "Pushed images are immediately available to consumers; verify the tag and contents" |
|
mode = "ask" |
|
|
|
[[publishing.rules]] |
|
pattern = '\buv\s+publish\b' |
|
description = "Publishes a Python package to PyPI" |
|
reason = "Publishing to PyPI is public and version numbers cannot be reused once published" |
|
mode = "ask" |
|
|
|
# ───────────────────────────────────────────────────────────────────────────── |
|
# PACKAGE MANAGEMENT (destructive) |
|
# ───────────────────────────────────────────────────────────────────────────── |
|
|
|
[package] |
|
description = "Package operations that remove dependencies" |
|
|
|
[[package.rules]] |
|
pattern = '\buv\s+(?:pip\s+)?(?:remove|uninstall)\b' |
|
description = "Removes a Python package from the environment" |
|
reason = "Package removal may break dependent code; confirm this is intentional" |
|
mode = "ask" |
|
|
|
# ───────────────────────────────────────────────────────────────────────────── |
|
# NETWORK ACCESS |
|
# ───────────────────────────────────────────────────────────────────────────── |
|
|
|
[network] |
|
description = "Commands establishing connections to remote hosts" |
|
|
|
[[network.rules]] |
|
pattern = '\bssh\s+' |
|
description = "Opens an SSH connection to a remote host" |
|
reason = "SSH establishes interactive access to a remote system; confirm the target host" |
|
mode = "ask" |
|
|
|
[[network.rules]] |
|
pattern = '\bscp\b' |
|
description = "Copies files to or from a remote host over SSH" |
|
reason = "scp transfers files to/from remote systems; confirm source, destination, and host" |
|
mode = "ask" |
|
|
|
[[network.rules]] |
|
pattern = '\brsync\b' |
|
description = "Synchronizes files between local and remote paths" |
|
reason = "rsync can overwrite or delete files on the destination; confirm direction and target" |
|
mode = "ask" |