Skip to content

Commit 77fafb7

Browse files
fix: allow DSO shim ticket commands in tickets-tracker-guard (merge worktree-20260324-091358)
2 parents a9b74f3 + c2b7cd1 commit 77fafb7

File tree

2 files changed

+59
-10
lines changed

2 files changed

+59
-10
lines changed

plugins/dso/hooks/lib/pre-bash-functions.sh

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -744,7 +744,7 @@ print(json.dumps(entry))
744744
#
745745
# .tickets-tracker/ is an event-sourced log; direct Bash modifications bypass
746746
# event sourcing invariants and may corrupt the event log. All mutations must
747-
# go through ticket CLI commands (ticket *, tk *).
747+
# go through ticket CLI commands (ticket *, .claude/scripts/dso ticket *).
748748
#
749749
# Logic:
750750
# 1. Only fires on Bash tool calls
@@ -753,7 +753,7 @@ print(json.dumps(entry))
753753
# 4. If command contains .tickets-tracker/ AND NOT allowlisted: return 2 (block)
754754
# 5. All other cases: return 0 (allow, fail-open)
755755
#
756-
# Allowlist: ticket CLI scripts (ticket, tk) are the sanctioned write path.
756+
# Allowlist: ticket CLI scripts (ticket, .claude/scripts/dso ticket) are the sanctioned write path.
757757
#
758758
# REVIEW-DEFENSE: This function is intentionally not wired into dispatchers yet.
759759
# Task dso-280g ("Wire tickets-tracker guards into dispatchers") handles dispatcher
@@ -783,12 +783,24 @@ hook_tickets_tracker_bash_guard() {
783783
return 0
784784
fi
785785

786-
# Allowlist: ticket CLI patterns (ticket *) — sanctioned write path
787-
# Check if command's first meaningful token is 'ticket'
788-
local FIRST_TOKEN
789-
FIRST_TOKEN="${COMMAND##*([[:space:]])}" # trim leading whitespace
790-
FIRST_TOKEN="${FIRST_TOKEN%%[[:space:]]*}" # first token
791-
if [[ "$FIRST_TOKEN" == "ticket" ]]; then
786+
# Allowlist: ticket CLI patterns — sanctioned write path.
787+
# Three invocation forms:
788+
# 1. "ticket <subcommand> ..." — bare ticket dispatcher
789+
# 2. ".claude/scripts/dso ticket <subcommand> ..." — via DSO shim
790+
# 3. "bash .claude/scripts/dso ticket <subcommand> ..." — shim via bash
791+
local _TRIMMED
792+
_TRIMMED="${COMMAND##*([[:space:]])}" # trim leading whitespace
793+
local _FIRST_TOKEN="${_TRIMMED%%[[:space:]]*}"
794+
# Form 1: bare "ticket" command
795+
if [[ "$_FIRST_TOKEN" == "ticket" ]]; then
796+
return 0
797+
fi
798+
# Form 2: DSO shim — command starts with the shim path + "ticket"
799+
if [[ "$_TRIMMED" == ".claude/scripts/dso ticket "* ]] || [[ "$_TRIMMED" == ".claude/scripts/dso ticket" ]]; then
800+
return 0
801+
fi
802+
# Form 3: shim invoked via bash
803+
if [[ "$_TRIMMED" == "bash .claude/scripts/dso ticket "* ]] || [[ "$_TRIMMED" == "bash .claude/scripts/dso ticket" ]]; then
792804
return 0
793805
fi
794806

tests/hooks/test-tickets-tracker-guard.sh

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,11 +108,48 @@ EXIT_CODE=$(run_bash_guard "$INPUT")
108108
assert_eq_verbose "test_bash_tickets_tracker_reference_blocks" "2" "$EXIT_CODE"
109109

110110
# --- test_bash_ticket_cli_allowlisted ---
111-
# Bash command that is a ticket CLI invocation (tk show) must be allowed (exit 0).
112-
INPUT='{"tool_name":"Bash","tool_input":{"command":"tk show dso-1234"}}'
111+
# Bash command that is a bare ticket CLI invocation must be allowed (exit 0).
112+
INPUT='{"tool_name":"Bash","tool_input":{"command":"ticket show dso-1234"}}'
113113
EXIT_CODE=$(run_bash_guard "$INPUT")
114114
assert_eq_verbose "test_bash_ticket_cli_allowlisted" "0" "$EXIT_CODE"
115115

116+
# --- test_bash_dso_shim_ticket_comment_allowlisted ---
117+
# Bash command via DSO shim (.claude/scripts/dso ticket comment) must be allowed (exit 0).
118+
INPUT='{"tool_name":"Bash","tool_input":{"command":".claude/scripts/dso ticket comment 4506-e5da \"## Description\""}}'
119+
EXIT_CODE=$(run_bash_guard "$INPUT")
120+
assert_eq_verbose "test_bash_dso_shim_ticket_comment_allowlisted" "0" "$EXIT_CODE"
121+
122+
# --- test_bash_dso_shim_ticket_create_allowlisted ---
123+
# Bash command via DSO shim (.claude/scripts/dso ticket create) must be allowed (exit 0).
124+
INPUT='{"tool_name":"Bash","tool_input":{"command":".claude/scripts/dso ticket create bug \"some title\""}}'
125+
EXIT_CODE=$(run_bash_guard "$INPUT")
126+
assert_eq_verbose "test_bash_dso_shim_ticket_create_allowlisted" "0" "$EXIT_CODE"
127+
128+
# --- test_bash_dso_shim_ticket_transition_allowlisted ---
129+
# Bash command via DSO shim (.claude/scripts/dso ticket transition) must be allowed (exit 0).
130+
INPUT='{"tool_name":"Bash","tool_input":{"command":".claude/scripts/dso ticket transition w21-u3op open in_progress"}}'
131+
EXIT_CODE=$(run_bash_guard "$INPUT")
132+
assert_eq_verbose "test_bash_dso_shim_ticket_transition_allowlisted" "0" "$EXIT_CODE"
133+
134+
# --- test_bash_dso_shim_ticket_list_allowlisted ---
135+
# Bash command via DSO shim (.claude/scripts/dso ticket list) must be allowed (exit 0).
136+
INPUT='{"tool_name":"Bash","tool_input":{"command":".claude/scripts/dso ticket list 2>/dev/null | python3 -c \"...\""}}'
137+
EXIT_CODE=$(run_bash_guard "$INPUT")
138+
assert_eq_verbose "test_bash_dso_shim_ticket_list_allowlisted" "0" "$EXIT_CODE"
139+
140+
# --- test_bash_dso_shim_via_bash_allowlisted ---
141+
# Bash command via "bash .claude/scripts/dso ticket ..." must be allowed (exit 0).
142+
INPUT='{"tool_name":"Bash","tool_input":{"command":"bash .claude/scripts/dso ticket show w21-1234"}}'
143+
EXIT_CODE=$(run_bash_guard "$INPUT")
144+
assert_eq_verbose "test_bash_dso_shim_via_bash_allowlisted" "0" "$EXIT_CODE"
145+
146+
# --- test_bash_embedded_dso_ticket_in_echo_blocks ---
147+
# A command that embeds "/dso ticket" as a string argument (not a real invocation)
148+
# while also referencing .tickets-tracker/ must still be blocked (exit 2).
149+
INPUT='{"tool_name":"Bash","tool_input":{"command":"echo \"/dso ticket\" > /repo/.tickets-tracker/event.json"}}'
150+
EXIT_CODE=$(run_bash_guard "$INPUT")
151+
assert_eq_verbose "test_bash_embedded_dso_ticket_in_echo_blocks" "2" "$EXIT_CODE"
152+
116153
# --- test_bash_no_tickets_tracker_ref_allows ---
117154
# Bash command with no .tickets-tracker/ reference must be allowed (exit 0).
118155
INPUT='{"tool_name":"Bash","tool_input":{"command":"echo hello world"}}'

0 commit comments

Comments
 (0)