From 928e244696503830348855d1223f75d037e02016 Mon Sep 17 00:00:00 2001 From: librelad Date: Mon, 24 Aug 2026 16:03:02 +0100 Subject: [PATCH] fix(config): stop sourcing files in unmarked configs/ subdirectories MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 ``, 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 --- scripts/source/loading/scan_files.sh | 30 ++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/scripts/source/loading/scan_files.sh b/scripts/source/loading/scan_files.sh index 77297ed..c008bed 100755 --- a/scripts/source/loading/scan_files.sh +++ b/scripts/source/loading/scan_files.sh @@ -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 ``, 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"