Follow-up to 928e244, which stopped configs/ subdirectories being sourced without a .category marker. That closed the hole; this removes the thing that fell into it. storageIndexFile pointed at configs/storage/app_locations. The file's requirements are only "manager-owned" and "not on a removable disk" — configs/ satisfies both, which is why I put it there, and it was still wrong: that tree carries a third property the file violates. sourceScanFiles SOURCES what it finds under configs/, and sourcing means executing. The index is a TSV of "<slug><TAB><root>", which bash reads as a command and its argument. Harmless while no slug matched a real executable. The row for the app named `libreportal` armed it, because that IS the CLI on PATH: sourcing ran `libreportal /libreportal-containers`, which re-entered the scan, which sourced the file again — one process pair per level until the host OOMed and took the desktop session with it. It now lives at $system_dir/storage/app_locations, with a one-shot migration so an install that already has an index keeps knowing where its apps live rather than silently forgetting. libreportal-ownership reconciles the new directory, and scan_files.sh gained a note that configs/storage/ carries no .category on purpose. scripts/dev/lp-configs-guard-test covers both ends: the index never lands in configs/, a legacy one migrates, and a file of the exact detonating shape placed in an unmarked configs/ subdirectory is not executed while a marked category still loads. Also wires sourceStorageLocations into the config scan beside sourceBackupLocations — per-location configs sit at depth 3, below the generic scan, and need their own walker like the backup ones do. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
45 lines
2.5 KiB
Bash
Executable File
45 lines
2.5 KiB
Bash
Executable File
#!/bin/bash
|
|
# Regression test for the configs/ fork bomb: a data file in a configs/
|
|
# subdirectory must never be sourced, and the index must not land there at all.
|
|
R="$(cd "$(dirname "$0")/../.." && pwd)"
|
|
B=$(mktemp -d); trap 'rm -rf "$B"' EXIT
|
|
export LP_SYSTEM_DIR="$B/sys" LP_CONTAINERS_DIR="$B/primary" LP_BACKUPS_DIR="$B/bk"
|
|
export LP_STORAGE_REGISTRY="$B/storage.roots"
|
|
mkdir -p "$B/sys/configs/general" "$B/primary" "$B/sys/configs/storage"
|
|
: > "$B/sys/configs/general/.category"
|
|
echo 'CFG_REAL_OPTION=yes' > "$B/sys/configs/general/general_test"
|
|
source "$R/scripts/source/paths.sh"
|
|
fail=0
|
|
|
|
echo "--- 1. the index no longer lives under configs/ ---"
|
|
idx=$(storageIndexFile)
|
|
echo " index path: $idx"
|
|
[[ "$idx" == "$B/sys/storage/app_locations" ]] && echo " ok outside configs/" || { echo " FAIL still in configs/"; fail=1; }
|
|
|
|
echo "--- 2. writing the index does not create configs/storage/app_locations ---"
|
|
runInstallOp(){ "$@"; }; runInstallWrite(){ cat > "$1"; }
|
|
storageIndexSet libreportal "$B/primary"
|
|
[[ -f "$B/sys/configs/storage/app_locations" ]] && { echo " FAIL wrote into configs/"; fail=1; } || echo " ok nothing written into configs/"
|
|
[[ -f "$idx" ]] && echo " ok index written to the system tree" || { echo " FAIL no index"; fail=1; }
|
|
|
|
echo "--- 3. legacy index is migrated, not abandoned ---"
|
|
rm -rf "$B/sys/storage"
|
|
mkdir -p "$B/sys/configs/storage"
|
|
printf 'bookstack\t%s\n' "$B/primary" > "$B/sys/configs/storage/app_locations"
|
|
got=$(storageIndexGet bookstack)
|
|
[[ "$got" == "$B/primary" ]] && echo " ok legacy entry still resolves ($got)" || { echo " FAIL lost entry"; fail=1; }
|
|
[[ -f "$B/sys/configs/storage/app_locations" ]] && { echo " FAIL legacy file left behind"; fail=1; } || echo " ok legacy file removed"
|
|
|
|
echo "--- 4. the actual bomb: a stray executable-looking file in configs/ ---"
|
|
# Recreate the exact shape that detonated: <slug><TAB><path> where slug is a
|
|
# real command on PATH. If the scan sources it, the marker file appears.
|
|
mkdir -p "$B/sys/configs/storage"
|
|
printf 'touch\t%s/DETONATED\n' "$B" > "$B/sys/configs/storage/app_locations"
|
|
configs_dir="$B/sys/configs/"
|
|
source "$R/scripts/source/loading/scan_files.sh"
|
|
sourceScanFiles libreportal_configs >/dev/null 2>&1
|
|
[[ -e "$B/DETONATED" ]] && { echo " FAIL the file was executed"; fail=1; } || echo " ok unmarked configs/ subdir not sourced"
|
|
[[ "${CFG_REAL_OPTION:-}" == "yes" ]] && echo " ok marked category still loads" || { echo " FAIL real config stopped loading"; fail=1; }
|
|
|
|
echo; [[ $fail -eq 0 ]] && echo "ALL PASS" || echo "FAILURES"; exit $fail
|