Finish the wonderland: System told truthfully, in eight tabs instead of ten
Claude-Session: https://claude.ai/code/session_01Ms2FbjQy31TVf3CEvQhGM8
This commit is contained in:
@@ -198,6 +198,102 @@ jq -e 'has("pwned") | not' "$settings" >/dev/null || fail 'a file outside the ba
|
||||
# ── A snapshot that is not listed is refused ─────────────────────────────────
|
||||
run restore "settings-20000101-000000000.json" >/dev/null 2>&1 && fail 'a missing snapshot was reported restored'
|
||||
|
||||
# ── A snapshot can be given a name, and the name cannot be a path ────────────
|
||||
#
|
||||
# Fifteen rows reading "2026-08-24 11:03:07" are fifteen rows nobody can choose
|
||||
# between, which is the same as having no backups: the one you want is the one
|
||||
# you took before the thing you are undoing, and the timestamp does not say
|
||||
# what that was. So `create` takes a name.
|
||||
#
|
||||
# A name typed by a person then reaches a filename, which is the oldest way a
|
||||
# helper gets talked into writing outside its own directory. The charset is the
|
||||
# snapshot charset -- letters, digits, dot, dash, underscore -- and everything
|
||||
# else is either sanitized out or refused; either is fine, writing outside
|
||||
# BACKUP_DIR is not.
|
||||
|
||||
rm -f "$backups"/*.json
|
||||
printf '{"gapsOut":24}' >"$settings"
|
||||
printf '{"initialized":true,"favorites":[]}' >"$home"
|
||||
|
||||
run create >/dev/null || fail 'create without a name failed'
|
||||
[[ "$(run list | jq 'length')" == "1" ]] || fail 'an unnamed create did not produce one snapshot'
|
||||
|
||||
run create "Before the theme experiment" >/dev/null || fail 'create with a name failed'
|
||||
named="$(run list | jq -r '.[0]')"
|
||||
jq -e '.label == "Before the theme experiment"' <<<"$named" >/dev/null \
|
||||
|| fail "a named snapshot did not carry its name back: $named"
|
||||
named_file="$(jq -r .name <<<"$named")"
|
||||
[[ "$named_file" =~ ^[A-Za-z0-9_.-]+$ ]] \
|
||||
|| fail "a named snapshot produced the filename \"$named_file\", which is outside the snapshot charset"
|
||||
|
||||
# Every one of these either fails or produces a file inside the backup
|
||||
# directory. None of them may produce a file anywhere else, and none may
|
||||
# remove or overwrite something that is not a snapshot.
|
||||
outside="$work/state/panama/NOT-A-BACKUP"
|
||||
printf 'untouched\n' >"$outside"
|
||||
for hostile in \
|
||||
'../../../NOT-A-BACKUP' \
|
||||
'/etc/panama-owned' \
|
||||
'a/b' \
|
||||
'..' \
|
||||
'.' \
|
||||
$'tab\there' \
|
||||
'-rf'; do
|
||||
run create "$hostile" >/dev/null 2>&1 || true
|
||||
[[ "$(<"$outside")" == 'untouched' ]] \
|
||||
|| fail "the snapshot name \"$hostile\" wrote outside the backup directory"
|
||||
done
|
||||
while read -r listed; do
|
||||
[[ -n "$listed" ]] || continue
|
||||
[[ "$listed" =~ ^[A-Za-z0-9_.-]+$ ]] \
|
||||
|| fail "a hostile snapshot name produced the listed filename \"$listed\""
|
||||
done < <(run list | jq -r '.[].name')
|
||||
|
||||
# ── A snapshot can be deleted, and only a snapshot ───────────────────────────
|
||||
#
|
||||
# Same boundary as restore, in the other direction, and with a worse failure:
|
||||
# restore reading the wrong file overwrites settings, delete resolving the
|
||||
# wrong name destroys something that is not a backup at all.
|
||||
|
||||
before_delete="$(run list | jq 'length')"
|
||||
(( before_delete >= 2 )) || fail 'not enough snapshots to exercise delete'
|
||||
victim="$(run list | jq -r '.[0].name')"
|
||||
run delete "$victim" >/dev/null || fail 'deleting a listed snapshot failed'
|
||||
[[ ! -e "$backups/$victim" ]] || fail 'a deleted snapshot is still on disk'
|
||||
[[ "$(run list | jq 'length')" == "$((before_delete - 1))" ]] \
|
||||
|| fail 'delete removed a different number of snapshots than one'
|
||||
|
||||
printf '{"pwned":false}' >"$work/delete-target.json"
|
||||
for hostile in \
|
||||
'../../delete-target.json' \
|
||||
'/etc/passwd' \
|
||||
"$victim" \
|
||||
'settings-20000101-000000000.json' \
|
||||
'' ; do
|
||||
run delete "$hostile" >/dev/null 2>&1 \
|
||||
&& fail "delete accepted \"$hostile\", which is not a snapshot in the backup directory"
|
||||
done
|
||||
[[ -f "$work/delete-target.json" ]] || fail 'delete followed a traversing name out of the backup directory'
|
||||
|
||||
escape="settings-20000101-000000009.json"
|
||||
ln -s "$work/delete-target.json" "$backups/$escape"
|
||||
run delete "$escape" >/dev/null 2>&1 \
|
||||
&& fail 'delete accepted a symlink escaping the backup directory'
|
||||
[[ -f "$work/delete-target.json" ]] || fail 'delete removed the target of an escaping symlink'
|
||||
rm -f "$backups/$escape"
|
||||
|
||||
# ── The list says how much room a snapshot takes ─────────────────────────────
|
||||
#
|
||||
# Fifteen snapshots of a settings file are nothing; fifteen of a settings file
|
||||
# somebody grew are not, and the page offers a Delete button now, which is a
|
||||
# decision nobody can make without the size.
|
||||
run list | jq -e 'all(.[]; (.bytes | type == "number") and .bytes > 0)' >/dev/null \
|
||||
|| fail 'the snapshot list does not report each snapshot’s size'
|
||||
listed_bytes="$(run list | jq -r '.[0].bytes')"
|
||||
actual_bytes="$(stat -c %s "$backups/$(run list | jq -r '.[0].name')")"
|
||||
[[ "$listed_bytes" == "$actual_bytes" ]] \
|
||||
|| fail "the list reports $listed_bytes bytes for a snapshot that is $actual_bytes on disk"
|
||||
|
||||
# ── Snapshots are capped ─────────────────────────────────────────────────────
|
||||
for _ in $(seq 1 20); do
|
||||
printf '{"n":%s}' "$RANDOM" >"$settings"
|
||||
|
||||
Reference in New Issue
Block a user