From 94a52e7ff904349f7c76fe50769266a43a0147f1 Mon Sep 17 00:00:00 2001 From: Stefan Wintermeyer Date: Sun, 18 Oct 2026 13:06:02 +0200 Subject: [PATCH] Write cp, mv and rm with the command prefix Admins often alias cp, mv and rm to -i. Without a TTY the prompt reads EOF: mv -i fails and costs a roundtrip, rm -i keeps the file and exits 0, which reads as success. A stateless command prefix beats tracking aliases in server memory, which would go stale whenever the dotfiles change. An AI agent wrote this text in my name. I know that is problematic. --- .claude/hooks/guard-taboos-test.sh | 12 ++++++++++++ CHANGELOG.md | 9 +++++++++ CLAUDE.md | 12 ++++++++++++ VERSION | 2 +- rules/backups.md | 2 +- rules/file-naming-changes.md | 3 ++- rules/mise.md | 2 +- 7 files changed, 38 insertions(+), 4 deletions(-) diff --git a/.claude/hooks/guard-taboos-test.sh b/.claude/hooks/guard-taboos-test.sh index 06d25e0..cb99c04 100755 --- a/.claude/hooks/guard-taboos-test.sh +++ b/.claude/hooks/guard-taboos-test.sh @@ -283,6 +283,18 @@ check deny "node -e \"require('fs').writeFileSync('/dev/vda','')\"" # denied (see the accepted false positives in the guard). check pass 'ssh -o ControlMaster=auto -o ControlPath=~/.cache/heinzel/ssh-%C root@h "rm -f /var/tmp/old.log; chmod 644 /etc/motd"' +# --- the command prefix (issue #77) ----------------------------- +# heinzel writes cp/mv/rm as `command cp` to skip an rm -i alias. +# The prefix must not hide a taboo from the guard. +check deny 'command rm -rf ~/.ssh' +check deny 'command mv /etc/ssh/sshd_config /tmp/' +check deny 'command cp /tmp/x /etc/sudoers' +check deny 'command dd if=/dev/zero of=/dev/sda' +check deny 'command fdisk /dev/sda' +check deny 'ssh root@h "command rm /etc/ssh/ssh_host_ed25519_key"' +check pass 'command cp -p /etc/nginx/nginx.conf /var/backups/heinzel/' +check pass 'command rm /var/tmp/old.log' + # --- must pass ------------------------------------------------- check pass 'fdisk -l' check pass 'sfdisk -l /dev/sda' diff --git a/CHANGELOG.md b/CHANGELOG.md index 516ad3b..6b3a86b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## 3.2.2 — 2026-10-18 + +- **Aliased `cp`, `mv` and `rm` no longer trip + heinzel.** It writes them as `command cp` etc., so + an `rm -i` alias cannot prompt. Without a TTY that + prompt reads EOF: `mv -i` failed and cost a + roundtrip, `rm -i` kept the file and exited 0 + (#77). + ## 3.2.1 — 2026-10-18 - **Heinzel Pro follows a server to its new address.** diff --git a/CLAUDE.md b/CLAUDE.md index 1de5096..5cffacb 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -615,6 +615,18 @@ operator the report from the rule file. Read `rules/backups.md`. Back up every config file before editing it. +## Shell Aliases + +Write `cp`, `mv` and `rm` as `command cp`, +`command mv`, `command rm`, locally and over SSH. +Many admins alias them to `-i`, and without a TTY +the prompt reads EOF: `mv -i` fails, `rm -i` keeps +the file and exits 0. `command` skips aliases and +functions, so heinzel never needs to know which +exist. After `sudo`, write the tool plainly: sudo +already skips aliases and `command` is no binary +there. + ## Renaming, Moving, Retention Changes Read `rules/file-naming-changes.md` before changing diff --git a/VERSION b/VERSION index e4604e3..be94e6f 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -3.2.1 +3.2.2 diff --git a/rules/backups.md b/rules/backups.md index aeba878..7c6ba32 100644 --- a/rules/backups.md +++ b/rules/backups.md @@ -5,7 +5,7 @@ Before editing any config file, back it up: ``` BACKUP_DIR="/var/backups/heinzel" mkdir -p "$BACKUP_DIR" -cp /etc/some/config.conf \ +command cp /etc/some/config.conf \ "$BACKUP_DIR/config.conf.$(date +%Y%m%d-%H%M%S)" # Clean backups older than 30 days find "$BACKUP_DIR" -type f -mtime +30 -delete diff --git a/rules/file-naming-changes.md b/rules/file-naming-changes.md index 518a73f..ca125a9 100644 --- a/rules/file-naming-changes.md +++ b/rules/file-naming-changes.md @@ -60,7 +60,8 @@ find out the rule was wrong. Write the mapping to `/var/backups/heinzel/` as `-rename-map-.txt` (new TAB old) so the rename can be replayed backwards, and use -`mv -n` so an unforeseen collision cannot overwrite. +`command mv -n` so an unforeseen collision cannot +overwrite. ## 3. Deleting the subject also deletes its housekeeping diff --git a/rules/mise.md b/rules/mise.md index 526c1f1..077c26e 100644 --- a/rules/mise.md +++ b/rules/mise.md @@ -220,7 +220,7 @@ re-runs are no-ops: ```bash [ -f ~/.bash_profile ] && \ - cp ~/.bash_profile ~/.bash_profile.bak.$(date +%F) + command cp ~/.bash_profile ~/.bash_profile.bak.$(date +%F) grep -q '# mise shims' ~/.bash_profile 2>/dev/null || \ cat >> ~/.bash_profile << 'EOF' # mise shims