From 8bf6b8321b4f7f06b8dc50386a1ab88fcdc42ebd Mon Sep 17 00:00:00 2001 From: kreschnick Date: Mon, 18 May 2026 11:02:22 +0200 Subject: [PATCH] fix(gstack-paths): shell-quote output so eval is safe for paths with spaces MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GSTACK_HOME (and the other path roots) may contain spaces or shell metacharacters — for example an iCloud-synced directory under `Library/Mobile Documents/`. The previous bare echo emitted: GSTACK_STATE_ROOT=/path/with spaces which eval split at the first space, leaving GSTACK_STATE_ROOT truncated and attempting to execute the remainder as a command. Fix: add a POSIX-compatible _shell_quote helper that wraps values in single quotes and escapes any embedded single quotes. Works in sh / bash / zsh / dash. Tested with: - normal path (no spaces) — unchanged behaviour - path with spaces — now works - path with single quote — now works - actual iCloud vault path — now works --- bin/gstack-paths | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/bin/gstack-paths b/bin/gstack-paths index eee603d61b..9616bb1a63 100755 --- a/bin/gstack-paths +++ b/bin/gstack-paths @@ -13,11 +13,17 @@ # PLAN_ROOT: GSTACK_PLAN_DIR -> CLAUDE_PLANS_DIR -> $HOME/.claude/plans -> .claude/plans # TMP_ROOT: TMPDIR -> TMP -> .gstack/tmp (and mkdir -p, best-effort) # -# Security: output values are not sanitized — callers may receive paths with -# shell-special characters if env vars contain them. Skills should always quote -# expansions ("$GSTACK_STATE_ROOT", not $GSTACK_STATE_ROOT). +# Output values are shell-quoted so eval "$(gstack-paths)" is safe for paths +# containing spaces, $, ;, backticks, and other metacharacters. Single quotes +# are used for portability (POSIX sh / bash / zsh / dash). set -u +# Wrap a path in single quotes, escaping any embedded single quotes. +# Produces output safe for: eval "$(gstack-paths)" +_shell_quote() { + printf "'%s'" "$(printf '%s' "$1" | sed "s/'/'\\\\''/g")" +} + # State root: where gstack writes projects/, sessions/, analytics/. if [ -n "${GSTACK_HOME:-}" ]; then _state_root="$GSTACK_HOME" @@ -56,6 +62,6 @@ fi # will discover that on their own write attempt. Don't fail the eval here. mkdir -p "$_tmp_root" 2>/dev/null || true -echo "GSTACK_STATE_ROOT=$_state_root" -echo "PLAN_ROOT=$_plan_root" -echo "TMP_ROOT=$_tmp_root" +printf 'GSTACK_STATE_ROOT=%s\n' "$(_shell_quote "$_state_root")" +printf 'PLAN_ROOT=%s\n' "$(_shell_quote "$_plan_root")" +printf 'TMP_ROOT=%s\n' "$(_shell_quote "$_tmp_root")"