Files

55 lines
3.1 KiB
Bash
Raw Permalink Normal View History

#!/bin/bash
# Claude Code PreToolUse hook for Bash (wired in via claude-settings.json):
# commands that destroy things (containers, VMs, pools, disks, root-level
# folders, cluster members…) get permissionDecision "ask", so Claude has to get
# your OK even in auto mode (devdesk#27). It never blocks and never allows on
# its own; anything else passes through untouched.
#
# DEVDESK_GUARD=0 in /etc/devdesk/env.local turns it off. Extra rules go in
# /etc/devdesk/guard.local, one per line: an extended regex, a tab, a reason.
# shellcheck source=libexec/devdesk-lib.sh
. "${DEVDESK_LIBEXEC:-/usr/lib/devdesk}/devdesk-lib.sh" 2>/dev/null || exit 0
[ "${DEVDESK_GUARD:-1}" != 0 ] || exit 0
command=$(jq -rM '.tool_input.command // empty' 2>/dev/null) || exit 0
[ -n "$command" ] || exit 0
# Built-in rules: extended regex <TAB> reason.
rules=$(
cat <<'RULES'
\bpct[[:space:]]+destroy\b destroys a Proxmox container
\bqm[[:space:]]+destroy\b destroys a Proxmox VM
\bpvecm[[:space:]]+delnode\b removes a node from the Proxmox cluster
\bpveceph[[:space:]]+(osd|pool|mon|mgr|mds|fs)[[:space:]]+destroy\b destroys Ceph components
\bceph[[:space:]]+osd[[:space:]]+(purge|destroy|rm|out)\b removes or takes out a Ceph OSD
\bceph[[:space:]]+osd[[:space:]]+pool[[:space:]]+(delete|rm)\b deletes a Ceph pool
\bceph[[:space:]]+(fs[[:space:]]+rm|mon[[:space:]]+remove|auth[[:space:]]+del)\b removes Ceph components
\brbd[[:space:]]+(rm|remove|purge)\b deletes RBD images
\bzfs[[:space:]]+destroy\b destroys a ZFS dataset or snapshot
\bzpool[[:space:]]+(destroy|labelclear)\b destroys a ZFS pool
\b(lvremove|vgremove|pvremove|wipefs|shred)\b wipes volumes or disks
\bmkfs(\.[a-z0-9]+)?\b formats a filesystem
\bsgdisk[[:space:]]+(-[a-zA-Z]*Z|--zap) wipes a partition table
\bdd\b[^|;&]*\bof=/dev/ writes raw to a device
\brm[[:space:]]+(-[^[:space:]]*[[:space:]]+)*-[^[:space:]]*[rR][^[:space:]]*[[:space:]]+(-[^[:space:]]*[[:space:]]+)*['"]?/([a-z0-9_.*-]+/?)?['"]?([[:space:];&|)]|$) recursively deletes / or a top-level folder
\bgit[[:space:]]+push\b[^|;&]*([[:space:]]-f\b|--force) force-pushes (rewrites remote history)
\b(DROP[[:space:]]+(DATABASE|SCHEMA|TABLE)|TRUNCATE[[:space:]]+TABLE)\b drops database objects
\bsystemctl[[:space:]]+(stop|restart|kill|disable|mask)[[:space:]]+devdesk-tmux ends every DevDesk session
(^|[;&|][[:space:]]*|\bssh[[:space:]]+[^[:space:]]+[[:space:]]+['"]?|\bsystemctl[[:space:]]+)(reboot|poweroff|halt|shutdown)\b reboots or shuts down a machine
\bkubectl[[:space:]]+delete\b deletes Kubernetes resources
\bdocker[[:space:]]+(system[[:space:]]+prune|volume[[:space:]]+(rm|prune))\b deletes Docker data
RULES
cat "${DEVDESK_GUARD_LOCAL:-/etc/devdesk/guard.local}" 2>/dev/null
)
while IFS=$'\t' read -r pattern reason; do
if [ -z "$pattern" ] || [ "${pattern:0:1}" = "#" ]; then continue; fi
if grep -Eiq -- "$pattern" <<<"$command" 2>/dev/null; then
jq -cMn --arg r "DevDesk guard: this command ${reason:-matches a rule in guard.local}. Confirm before it runs." \
'{hookSpecificOutput: {hookEventName: "PreToolUse", permissionDecision: "ask", permissionDecisionReason: $r}}'
exit 0
fi
done <<<"$rules"
exit 0