Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .claude/hooks/guard-taboos-test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -456,6 +456,15 @@ check deny 'vi /etc/heinzel.conf'
check deny 'ssh root@h "rm /etc/heinzel.conf"'
check deny 'ssh root@h "sed -i s/false/true/ /etc/heinzel.conf"'
check deny 'python3 -c "import os; os.remove(\"/etc/heinzel.conf\")"'
# Other spellings of the same file: a case-insensitive filesystem
# (macOS), // and /./, a relative path after cd, find -delete.
check deny 'rm /ETC/HEINZEL.CONF'
check deny 'rm /etc//heinzel.conf'
check deny 'rm /etc/./heinzel.conf'
check deny 'cd /etc && rm heinzel.conf'
check deny 'cd /etc; echo allow_changes=true > heinzel.conf'
check deny 'find /etc -name heinzel.conf -delete'
check deny 'ln -sf /dev/null /etc/heinzel.conf'
check pass 'cat /etc/heinzel.conf'
check pass 'ssh root@h "cat /etc/heinzel.conf"'
check pass 'ssh root@h "if [ -e /etc/heinzel.conf ]; then cat /etc/heinzel.conf || echo HEINZEL_CONF_UNREADABLE; else echo HEINZEL_CONF_ABSENT; fi"'
Expand Down
17 changes: 12 additions & 5 deletions .claude/hooks/guard-taboos.sh
Original file line number Diff line number Diff line change
Expand Up @@ -362,9 +362,14 @@ EFIVARS='/sys/firmware/efi/efivars'

# The server lock. The machine's owner writes it to keep heinzel
# hands-off; a heinzel that could flip it to true or delete it
# would make it worthless. Unanchored on the left, so macOS's
# /private/etc matches too. Reading it stays allowed.
LOCKFILE='/etc/heinzel\.conf'
# would make it worthless. Matched by its file name alone, in any
# case: the full path misses /ETC/HEINZEL.CONF on macOS, /etc//
# and /etc/./, and a relative heinzel.conf after cd /etc. A write
# to any file of that name is denied with it. Not closable: a
# glob (/etc/heinzel.c*) or a name built at run time still gets
# through, so the written rule stays the first line of defence.
# Reading it stays allowed.
LOCKFILE='[Hh][Ee][Ii][Nn][Zz][Ee][Ll]\.[Cc][Oo][Nn][Ff]'

# A general-purpose language runtime. See the interpreter section
# at the bottom for why this list, and not a list of the ways
Expand Down Expand Up @@ -970,15 +975,17 @@ account work is a Heinzel Pro feature"
fi

# --- The server lock ------------------------------------------
# Same write set as sudoers above: a redirect, sed -i, an editor,
# a delete, move or re-permission, a copy or tee onto it. Moving
# Same write set as sudoers above (a redirect, sed -i, an editor,
# a delete, move or re-permission, a copy or tee onto it), plus
# find -delete, which deletes by a name it never spells as a path. Moving
# or deleting it lifts the lock as surely as writing true.
if hit "$LOCKFILE"; then
if hit '>>?[[:space:]]*["'\'']?[^[:space:];|&]*'"$LOCKFILE" \
|| { hit '(^|[^[:alnum:]_-])(sed|perl)([^[:alnum:]_-]|$)' \
&& hit '(^|[[:space:]])-i'; } \
|| hit "(^|[^[:alnum:]_-])$EDITOR([^[:alnum:]_-]|\$)" \
|| hit "(^|[^[:alnum:]_-])($CLOBBER|cp)([^[:alnum:]_-]|\$)" \
|| hit '(^|[[:space:]])-delete([[:space:]]|$)' \
|| writes_to "$LOCKFILE"
then
deny "/etc/heinzel.conf is the server lock; only the \
Expand Down
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# Changelog

## 3.1.1 — 2026-10-15

- **The server lock holds under other spellings.** The
guard now matches `heinzel.conf` by name in any case,
so `/ETC/HEINZEL.CONF` on macOS, `/etc//heinzel.conf`
and a relative path after `cd /etc` are blocked too,
and so is `find … -delete`.

## 3.1.0 — 2026-10-15

- **A server can lock itself.** `allow_changes = false`
Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
3.1.0
3.1.1
Loading