fix(config): stop sourcing files in unmarked configs/ subdirectories

sourceScanFiles sourced every file two levels deep under configs/, and
sourcing means executing. A directory used there as ordinary storage
therefore turned its contents into a script.

storageIndexSet caches an app -> root TSV at configs/storage/app_locations,
with no .category marker alongside it. Every line is `<slug><TAB><path>`,
which bash reads as a command and its argument. That stayed invisible while
no slug matched a real executable — and became a fork bomb the moment the
index recorded the app named `libreportal`, because that IS the CLI on PATH:
sourcing ran `libreportal /libreportal-containers`, which re-entered the same
scan, which sourced the file again, one process pair per level until the host
died of OOM. Every CLI invocation on the box detonated it, the task
processor's own poll included, so the machine black-screened out of memory
minutes after each boot.

Files in a SUBDIRECTORY are now sourced only when that directory carries
.category — the contract commandReloadConfigs already enforces in the CLI
wrapper, and one every real config category (webui, general, security,
backup, network) already satisfies. Files directly in configs/ are unchanged.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
librelad 2026-08-24 16:03:02 +01:00
parent 8b5e02c760
commit 928e244696

View File

@ -15,12 +15,38 @@ sourceScanFiles()
if [ -f "$file" ]; then
local filename=$(basename "$file")
local should_load=true
# Skip .category files and excluded files
if [[ "$file" =~ \.category$ ]] || [[ "$filename" == "app_categories" ]]; then
should_load=false
fi
# A file in a SUBDIRECTORY is only sourced when that directory is
# a declared config category (carries .category) — the same
# contract commandReloadConfigs enforces in the CLI wrapper.
#
# Sourcing means EXECUTING, so an unmarked directory used as
# ordinary storage turned its contents into a script. That is not
# hypothetical: storageIndexSet caches an app -> root TSV at
# configs/storage/app_locations, no .category alongside it. Every
# line there is `<slug><TAB><path>`, which bash reads as a command
# and its argument. Harmless while no slug matched a real
# executable — and a fork bomb the moment the row was for the app
# named `libreportal`, because that IS the CLI: sourcing ran
# `libreportal /libreportal-containers`, which re-entered this
# scan, which sourced the file again, one process pair per level
# until the host died of OOM. Every CLI invocation on the box,
# including the task processor's own, detonated it.
#
# Depth-1 files (directly in configs/) keep loading as before —
# only the category dirs gained a marker requirement.
if [ "$should_load" = true ]; then
local parent_dir="${file%/*}"
if [[ "$parent_dir" != "${folder_dir%/}" && ! -f "$parent_dir/.category" ]]; then
should_load=false
fi
fi
if [ "$should_load" = true ]; then
source "$file"
# echo "$load_type NEW FILE $file"