From 5e61c41c69908d18f4a37337d182c9ab2983f6be Mon Sep 17 00:00:00 2001 From: "Jonathan D.A. Jewell" <6759885+hyperpolymath@users.noreply.github.com> Date: Wed, 30 Sep 2026 15:57:21 +0100 Subject: [PATCH 1/3] fix(security): move betlang-playground/keepopen log off predictable /tmp (CWE-377) betlang-playground.sh and keepopen.sh both hardcoded /tmp/${APP_NAME}.log (the latter as keepopen's own default when invoked without an explicit 5th arg). A predictable /tmp path lets another local user pre-create, symlink, or race the file. This is a durable, re-findable log (--logs and --tail in a separate invocation must find what --start wrote), not scratch, so it does not move to mktemp -d. LOG_FILE in both scripts now resolves to: ${XDG_STATE_HOME:-$HOME/.local/state}/launch-scaffolder/${APP_NAME}/server.log with the parent directory created mode 0700 (`mkdir -p` + `chmod 0700`, not `mkdir -p -m 0700`, to avoid a new shellcheck SC2174 warning) right after the LOG_FILE assignment in each script -- before any write, and, in keepopen.sh, before the log() calls that previously ran ahead of the old mkdir (which sat after those calls and silently discarded their output via `2>/dev/null || true` when the directory didn't yet exist). The duplicated literal "/tmp/${APP_NAME}.log" passed to keepopen.sh from mode_start() is replaced with "$LOG_FILE" (single source of truth). Every $LOG_FILE expansion is double-quoted. ui/launcher/README.adoc:112 documented the old /tmp path in its keepopen.sh usage example; updated to match so the doc doesn't go stale (and so Hypatia's ["'/]tmp/ regex doesn't flag it). launch-scaffolder realign will not overwrite either script: neither carries a generator metadata block (verified: no `launch-scaffolder`/ `generated`/`DO NOT EDIT` marker in either file; the launcher-standard_praxis.deed reference in both scripts' header comments is a compliance-doc URL, not a metadata block). Verification: bash -n OK on both scripts; shellcheck clean on both (matches origin/main baseline byte-for-byte, mapped for the added lines -- no new warnings); grep -nE "[\"'/]tmp/" 0 hits on both scripts. Smoke run: ./betlang-playground.sh --status -> "STOPPED"; --logs -> correctly reports no log file yet at the new XDG path; state dir confirmed created mode 0700 (ls -ld showing drwx------). Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_0136eszqrQ53Kj7aBH1D4rXK --- ui/launcher/README.adoc | 2 +- ui/launcher/betlang-playground.sh | 9 +++++++-- ui/launcher/keepopen.sh | 11 ++++++++--- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/ui/launcher/README.adoc b/ui/launcher/README.adoc index 84c967a..4973d91 100644 --- a/ui/launcher/README.adoc +++ b/ui/launcher/README.adoc @@ -109,7 +109,7 @@ keepopen.sh APP_NAME REPO_DIR "GUI_CMD" "TUI_CMD" [LOG_FILE] keepopen.sh betlang-playground /opt/betlang/ui \ "deno task dev" \ "deno task dev --no-browser" \ - /tmp/betlang-playground.log + "${XDG_STATE_HOME:-$HOME/.local/state}/launch-scaffolder/betlang-playground/server.log" ---- == Usage diff --git a/ui/launcher/betlang-playground.sh b/ui/launcher/betlang-playground.sh index 6a06ef1..eac78b3 100755 --- a/ui/launcher/betlang-playground.sh +++ b/ui/launcher/betlang-playground.sh @@ -24,7 +24,12 @@ STANDARDS_COMPLIANCE="launcher-standard-0.2.0" # Use Python's simple HTTP server, falling back to PHP, then Deno GUI_CMD="python3 -m http.server 3000" TUI_CMD="python3 -m http.server 3000" -LOG_FILE="/tmp/${APP_NAME}.log" +# CWE-377: a predictable /tmp log path lets another local user pre-create or +# symlink the file. Durable + re-findable (not scratch): --logs/--tail in a +# separate invocation must find what --start wrote. +LOG_FILE="${XDG_STATE_HOME:-$HOME/.local/state}/launch-scaffolder/${APP_NAME}/server.log" +mkdir -p "$(dirname "$LOG_FILE")" +chmod 0700 "$(dirname "$LOG_FILE")" # Required modes per launcher-standard_praxis.deed MODES=("--start" "--stop" "--status" "--auto" "--browser" "--integ" "--disinteg" "--help" "--debug" "--logs" "--tail") @@ -110,7 +115,7 @@ mode_start() { "$PROJECT_ROOT/public" \ "cd $PROJECT_ROOT/public && $GUI_CMD" \ "cd $PROJECT_ROOT/public && $TUI_CMD" \ - "/tmp/${APP_NAME}.log" + "$LOG_FILE" } # ============================================================================ diff --git a/ui/launcher/keepopen.sh b/ui/launcher/keepopen.sh index 3324674..703b949 100755 --- a/ui/launcher/keepopen.sh +++ b/ui/launcher/keepopen.sh @@ -22,7 +22,13 @@ APP_NAME="${1:-betlang-playground}" REPO_DIR="${2:-"(unknown)"}" GUI_CMD="${3:-"echo GUI mode not configured"}" TUI_CMD="${4:-"echo TUI mode not configured"}" -LOG_FILE="${5:-/tmp/${APP_NAME}.log}" +# CWE-377: a predictable /tmp log path lets another local user pre-create or +# symlink the file. Default matches betlang-playground.sh's LOG_FILE so a run +# without an explicit 5th arg still lands under the same durable, re-findable +# path a caller's --logs/--tail can find. +LOG_FILE="${5:-${XDG_STATE_HOME:-$HOME/.local/state}/launch-scaffolder/${APP_NAME}/server.log}" +mkdir -p "$(dirname "$LOG_FILE")" +chmod 0700 "$(dirname "$LOG_FILE")" # Banner colors per stage GUI_COLOR="yellow" @@ -166,8 +172,7 @@ log "Repo directory: $REPO_DIR" log "Log file: $LOG_FILE" log "========================================" -# Ensure log directory exists -mkdir -p "$(dirname "$LOG_FILE")" 2>/dev/null || true +# Log directory already created (mode 0700) alongside the LOG_FILE default above. # Write initial marker log "Launch attempt started at $(date)" From 0113df03ba0e6e7b927e22cd050a5c9c9a619584 Mon Sep 17 00:00:00 2001 From: "coderabbitai[bot]" <136622811+coderabbitai[bot]@users.noreply.github.com> Date: Wed, 30 Sep 2026 16:19:31 +0000 Subject: [PATCH 2/3] docs(launcher): Document playground startup fallback and exit behavior --- ui/launcher/betlang-playground.sh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ui/launcher/betlang-playground.sh b/ui/launcher/betlang-playground.sh index eac78b3..5a14f60 100755 --- a/ui/launcher/betlang-playground.sh +++ b/ui/launcher/betlang-playground.sh @@ -97,6 +97,11 @@ mode_help() { # Mode: --start # ============================================================================ +# Start the playground through keepopen.sh's GUI/TUI/shell fallback ladder, +# passing the public directory and configured LOG_FILE. +# Exits with status 1 if pgrep finds a command line matching "http.server". +# Replaces the current process; the wrapper determines the eventual exit status. +# Failure to execute the wrapper terminates the launcher. mode_start() { local port="${BETLANG_PORT:-3000}" From d5bd34ec06d1c3c1cc199a3753a0d14fbeb2fe71 Mon Sep 17 00:00:00 2001 From: "coderabbitai[bot]" <136622811+coderabbitai[bot]@users.noreply.github.com> Date: Wed, 30 Sep 2026 16:20:05 +0000 Subject: [PATCH 3/3] fix(launcher): preserve permissions on custom log directories --- ui/launcher/keepopen.sh | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/ui/launcher/keepopen.sh b/ui/launcher/keepopen.sh index 703b949..cbe3962 100755 --- a/ui/launcher/keepopen.sh +++ b/ui/launcher/keepopen.sh @@ -28,7 +28,10 @@ TUI_CMD="${4:-"echo TUI mode not configured"}" # path a caller's --logs/--tail can find. LOG_FILE="${5:-${XDG_STATE_HOME:-$HOME/.local/state}/launch-scaffolder/${APP_NAME}/server.log}" mkdir -p "$(dirname "$LOG_FILE")" -chmod 0700 "$(dirname "$LOG_FILE")" +# Only restrict permissions on the default application log directory. +if [ -z "${5:-}" ]; then + chmod 0700 "$(dirname "$LOG_FILE")" +fi # Banner colors per stage GUI_COLOR="yellow" @@ -172,7 +175,7 @@ log "Repo directory: $REPO_DIR" log "Log file: $LOG_FILE" log "========================================" -# Log directory already created (mode 0700) alongside the LOG_FILE default above. +# Log directory already created during configuration above. # Write initial marker log "Launch attempt started at $(date)"