diff --git a/linux/hotshot-capture.sh b/linux/hotshot-capture.sh index fd0fe6d..d9ef596 100755 --- a/linux/hotshot-capture.sh +++ b/linux/hotshot-capture.sh @@ -262,6 +262,12 @@ has_shell_metachars() { return 1 } +# --- 3. injection safety checks (control chars before shell metachars) -------- +if [ "$DO_TYPE" = 1 ] && has_control_chars "$SHOT_PATH"; then + log_event WARN injection.control_chars_refused "screenshot saved" + DO_TYPE=0 +fi + case "$CLI" in plain) TEXT="$(shell_escape "$SHOT_PATH") " ;; *) @@ -274,10 +280,6 @@ case "$CLI" in esac # --- 4. typed injection ------------------------------------------------------ -if [ "$DO_TYPE" = 1 ] && has_control_chars "$SHOT_PATH"; then - log_event WARN injection.control_chars_refused "screenshot saved" - DO_TYPE=0 -fi if [ "$DO_TYPE" = 1 ]; then if [ "$SESSION" = "x11" ]; then if have xdotool; then diff --git a/linux/tests/hotshot-capture.test.sh b/linux/tests/hotshot-capture.test.sh index 49a083a..861bb19 100755 --- a/linux/tests/hotshot-capture.test.sh +++ b/linux/tests/hotshot-capture.test.sh @@ -360,6 +360,22 @@ check "e2e metachar dir: refusal warning printed" $? grep -q -- "-t image/png -i $out" "$CLIPLOG" check "e2e metachar dir: clipboard still loaded" $? +# Control-char + shell-metachar in the directory -> control char label takes priority. +root="$(spawn_tree claude)" +both_dir="$TMP/shots;rm +dir" +out="$(run_e2e "$root" --full --dir "$both_dir" 2>"$TMP/e2e-both.err")" +rc=$? +assert_eq "e2e both-char dir: exit 0" "0" "$rc" +[ -s "$out" ] +check "e2e both-char dir: screenshot still created" $? +[ ! -e "$TYPELOG" ] +check "e2e both-char dir: nothing typed" $? +grep -q "injection.control_chars_refused" "$TMP/e2e-both.err" +check "e2e both-char dir: control chars label used" $? +! grep -q "injection.shell_metachars_refused" "$TMP/e2e-both.err" +check "e2e both-char dir: shell metachars label not used" $? + # No HOTSHOT_DIR / --dir -> SHOT_DIR resolves through xdg-user-dir (issue #174). # run_e2e always pins HOTSHOT_DIR, so these runs set the environment directly; # the stub sits ahead of the capture stubs so the lookup never depends on