HookStackGitHub
Back to catalogue
SecurityPreToolUse· BashPreToolUseBefore tool execution · can block⚡ blocking

Destructive command blocking

Blocks potentially destructive shell commands before the agent runs them: wiping root, home or CWD, git reset --hard, TRUNCATE, mkfs, dd to a disk, chmod 777 recursively.

Use cases

  • Guardrail on dev machines
  • Data loss prevention
  • Shared environments
  • Database safety

Providers & tags

Claude Code
#security#bash#safety#guardrail#git#database

settings.json fragment

{
  "hooks": {
    "PreToolUse": [
      {
        "hooks": [
          {
            "command": "node $CLAUDE_PROJECT_DIR/.claude/hooks/block-destructive.mjs",
            "type": "command"
          }
        ],
        "matcher": "Bash"
      }
    ]
  }
}

Script · .claude/hooks/block-destructive.mjs

#!/usr/bin/env node
// Bloc les commandes Bash destructives irréversibles (PreToolUse)
import { readFileSync } from 'fs';
import { fileURLToPath } from 'url';

const BLOCKED = [
  [/rm\s+-rf?\s+\/(?:\s|$)/, 'rm -rf / interdit'],
  [/rm\s+-rf?\s+[~*]/, 'rm -rf ~ / rm -rf * interdit (suppression de masse)'],
  [/rm\s+-rf?\s+\$HOME\b/, 'rm -rf $HOME interdit'],
  [/git\s+push\s+.*--force(?:-with-lease)?\s+.*(?:main|master)/, 'force-push sur main/master interdit'],
  [/git\s+reset\s+--hard/, 'git reset --hard interdit — pertes de modifications non commitées ; faites-le manuellement si intentionnel'],
  [/DROP\s+(?:TABLE|DATABASE)\s+\w+/i, 'DROP TABLE/DATABASE interdit sans confirmation explicite'],
  [/TRUNCATE\s+(?:TABLE\s+)?\w+/i, 'TRUNCATE interdit sans confirmation explicite'],
  [/>\s*\/dev\/(?:sda|nvme|disk)\d*/i, 'Écriture directe sur disque bloquée'],
  [/\bmkfs\b/i, 'Formatage de système de fichiers interdit'],
  [/\bdd\s+if=/i, 'Opération dd sur disque interdite'],
  [/chmod\s+-R\s+777\s+\//i, 'chmod 777 récursif sur / interdit'],
];

// Retire les chaînes entre guillemets (arguments -m "...", --body "...", etc.)
// pour éviter les faux positifs sur des mentions documentaires de patterns dangereux.
function stripQuotedArgs(cmd) {
  return cmd.replace(/"(?:[^"\\]|\\.)*"/g, '""').replace(/'(?:[^'\\]|\\.)*'/g, "''");
}

export function run(input) {
  const command = input.tool_input?.command ?? '';
  const stripped = stripQuotedArgs(command);
  const blocked = BLOCKED.find(([pattern]) => pattern.test(stripped));
  return blocked
    ? { decision: 'block', reason: `Commande destructive bloquée : ${blocked[1]}` }
    : null;
}

/* v8 ignore next 5 */
if (process.argv[1] === fileURLToPath(import.meta.url)) {
  const input = JSON.parse(readFileSync(0, 'utf8'));
  const result = run(input);
  if (result) process.stdout.write(JSON.stringify(result));
}