_TIRITH_BOOTSTRAP_WAS_POSIX=0
[[ -o posix ]] && _TIRITH_BOOTSTRAP_WAS_POSIX=1
_TIRITH_BOOTSTRAP_POSIXLY_SET="${POSIXLY_CORRECT+x}"
_TIRITH_BOOTSTRAP_POSIXLY_VALUE="${POSIXLY_CORRECT-}"
POSIXLY_CORRECT=y
if [[ -o posix ]]; then
\unset -f \
alias bind builtin cd command declare disown enable eval exec exit export \
false getopts hash history jobs kill local printf read readonly return set shift \
shopt source test trap true type typeset ulimit umask unalias unset wait 2>/dev/null
\unalias \
alias bind builtin cd command declare disown enable eval exec exit export \
false getopts hash history jobs kill local printf read readonly return set shift \
shopt source test trap true type typeset ulimit umask unalias unset wait 2>/dev/null
if [[ "$_TIRITH_BOOTSTRAP_POSIXLY_SET" == "x" ]]; then
POSIXLY_CORRECT="$_TIRITH_BOOTSTRAP_POSIXLY_VALUE"
else
\unset POSIXLY_CORRECT
fi
if [[ "$_TIRITH_BOOTSTRAP_WAS_POSIX" == "0" ]]; then
\set +o posix
fi
unset _TIRITH_BOOTSTRAP_WAS_POSIX _TIRITH_BOOTSTRAP_POSIXLY_SET _TIRITH_BOOTSTRAP_POSIXLY_VALUE
if [[ -n "$_TIRITH_BASH_LOADED" ]]; then
if [[ "$(declare -p _TIRITH_BASH_LOADED 2>/dev/null)" =~ ^declare\ -[a-zA-Z]*x ]]; then
unset _TIRITH_BASH_LOADED else
return fi
fi
_TIRITH_BASH_LOADED=1
unset _TIRITH_PENDING_EVAL
unset _TIRITH_PENDING_RECEIPT _TIRITH_PENDING_COMMAND
[[ "$(declare -p _TIRITH_TEST_SKIP_HEALTH 2>/dev/null)" =~ ^declare\ -[a-zA-Z]*x ]] && unset _TIRITH_TEST_SKIP_HEALTH
[[ "$(declare -p _TIRITH_TEST_FAIL_HEALTH 2>/dev/null)" =~ ^declare\ -[a-zA-Z]*x ]] && unset _TIRITH_TEST_FAIL_HEALTH
for _tirith_inherited_state in \
_TIRITH_BASH_INTERNAL \
_tirith_last_key _tirith_last_rc _tirith_last_cmd \
_TIRITH_PREV_DEBUG_TRAP \
_TIRITH_DEGRADE_WARNED _TIRITH_OFF_WARNED _TIRITH_PREEXEC_WARNED _TIRITH_RECEIPT_DEGRADE_WARNED \
_TIRITH_BINDS_INSTALLED _TIRITH_PREEXEC_PROMPT_STATUS \
_TIRITH_PROTECTED_KEYMAPS _TIRITH_SAVED_CTRL_O_KINDS _TIRITH_SAVED_CTRL_O_BINDINGS
do
unset "$_tirith_inherited_state"
done
unset _tirith_inherited_state
if [[ -z "${TIRITH_SESSION_ID:-}" ]]; then
builtin printf -v TIRITH_SESSION_ID '%x-%x-%x-%x' \
"$$" "${SECONDS:-0}" "${RANDOM:-0}" "${RANDOM:-0}"
export TIRITH_SESSION_ID
fi
_TIRITH_BASH_BIN="${BASH:-}"
case "$_TIRITH_BASH_BIN" in
/*) [[ -x "$_TIRITH_BASH_BIN" ]] || _TIRITH_BASH_BIN="" ;;
*) _TIRITH_BASH_BIN="" ;;
esac
_tirith_resolved_bin="$(type -P tirith 2>/dev/null || true)"
_TIRITH_BIN=""
if [[ -n "$_tirith_resolved_bin" ]]; then
_tirith_bin_name="${_tirith_resolved_bin##*/}"
_tirith_bin_dir="${_tirith_resolved_bin%/*}"
[[ "$_tirith_bin_dir" == "$_tirith_resolved_bin" ]] && _tirith_bin_dir="."
_TIRITH_BIN="$(
builtin cd -P -- "$_tirith_bin_dir" 2>/dev/null \
&& printf '%s/%s' "$PWD" "$_tirith_bin_name"
)"
fi
unset _tirith_resolved_bin _tirith_bin_name _tirith_bin_dir
if [[ $- == *i* && ( -z "$_TIRITH_BIN" || ! -x "$_TIRITH_BIN" ) ]]; then
printf '%s\n' "tirith: executable not found; bash hooks disabled" >&2
TIRITH_STATUS=off
return
fi
_TIRITH_MKTEMP_BIN=""
[[ -f /usr/bin/mktemp && -x /usr/bin/mktemp ]] && _TIRITH_MKTEMP_BIN=/usr/bin/mktemp
[[ -z "$_TIRITH_MKTEMP_BIN" && -f /bin/mktemp && -x /bin/mktemp ]] && _TIRITH_MKTEMP_BIN=/bin/mktemp
_TIRITH_RM_BIN=""
[[ -f /bin/rm && -x /bin/rm ]] && _TIRITH_RM_BIN=/bin/rm
[[ -z "$_TIRITH_RM_BIN" && -f /usr/bin/rm && -x /usr/bin/rm ]] && _TIRITH_RM_BIN=/usr/bin/rm
_TIRITH_MKDIR_BIN=""
[[ -f /bin/mkdir && -x /bin/mkdir ]] && _TIRITH_MKDIR_BIN=/bin/mkdir
[[ -z "$_TIRITH_MKDIR_BIN" && -f /usr/bin/mkdir && -x /usr/bin/mkdir ]] && _TIRITH_MKDIR_BIN=/usr/bin/mkdir
_TIRITH_WC_BIN=""
[[ -f /usr/bin/wc && -x /usr/bin/wc ]] && _TIRITH_WC_BIN=/usr/bin/wc
[[ -z "$_TIRITH_WC_BIN" && -f /bin/wc && -x /bin/wc ]] && _TIRITH_WC_BIN=/bin/wc
_TIRITH_STTY_BIN=""
[[ -f /bin/stty && -x /bin/stty ]] && _TIRITH_STTY_BIN=/bin/stty
[[ -z "$_TIRITH_STTY_BIN" && -f /usr/bin/stty && -x /usr/bin/stty ]] && _TIRITH_STTY_BIN=/usr/bin/stty
_tirith_new_capture_file() {
[[ -n "$_TIRITH_MKTEMP_BIN" && -n "$_TIRITH_RM_BIN" ]] || return 1
local base="${TMPDIR:-/tmp}"
case "$base" in
*$'\n'*|*$'\r'*) return 1 ;;
esac
[[ -d "$base" ]] || return 1
(umask 077; builtin command "$_TIRITH_MKTEMP_BIN" "${base%/}/tirith-bash.XXXXXXXX")
}
_tirith_capture_file_is_private() {
local file="${1:-}"
[[ -n "$file" && -f "$file" && ! -L "$file" && -O "$file" ]]
}
_tirith_remove_capture_file() {
local file="${1:-}"
[[ -n "$file" && -n "$_TIRITH_RM_BIN" ]] || return 1
builtin command "$_TIRITH_RM_BIN" -f -- "$file"
}
_tirith_restore_terminal_state() {
local state="${1:-}"
[[ -n "$state" && -n "$_TIRITH_STTY_BIN" ]] || return 0
builtin command "$_TIRITH_STTY_BIN" "$state" 2>/dev/null || true
}
_tirith_fixed_fd_is_valid() {
case "${1:-}" in
10|11|12|13|14|15|16|17|18|19) return 0 ;;
*) return 1 ;;
esac
}
_tirith_open_exact_input_pipe() {
local payload="$1" candidate
local previous_internal="${_TIRITH_BASH_INTERNAL:-0}"
_TIRITH_OPENED_FD=""
for candidate in 19 18 17 16 15 14 13 12 11 10; do
[[ -e "/dev/fd/$candidate" ]] && continue
_TIRITH_PIPE_BYTES="$payload"
_TIRITH_BASH_INTERNAL=1
if builtin eval "exec ${candidate}< <(builtin printf '%s' \"\$_TIRITH_PIPE_BYTES\")"; then
_TIRITH_BASH_INTERNAL="$previous_internal"
_TIRITH_OPENED_FD="$candidate"
unset _TIRITH_PIPE_BYTES
return 0
fi
_TIRITH_BASH_INTERNAL="$previous_internal"
done
_TIRITH_BASH_INTERNAL="$previous_internal"
unset _TIRITH_PIPE_BYTES
return 1
}
_tirith_close_pending_fd() {
local pending_fd="${1:-}"
_tirith_fixed_fd_is_valid "$pending_fd" || return 1
builtin eval "exec ${pending_fd}<&-"
}
_tirith_read_single_capture_line() {
local file="$1" line="" count=0 terminated=0 read_rc=0 byte_count="" expected_bytes=0
_TIRITH_CAPTURE_LINE=""
_tirith_capture_file_is_private "$file" || return 1
while :; do
line=""
IFS= read -r line
read_rc=$?
[[ $read_rc -ne 0 && -z "$line" ]] && break
count=$((count + 1))
[[ $count -eq 1 ]] && _TIRITH_CAPTURE_LINE="$line"
if [[ $read_rc -eq 0 ]]; then
terminated=1
else
terminated=0
fi
[[ $count -gt 1 || $read_rc -ne 0 ]] && break
done < "$file"
[[ $count -eq 1 && $terminated -eq 1 && -n "$_TIRITH_WC_BIN" ]] || return 1
byte_count="$(builtin command "$_TIRITH_WC_BIN" -c < "$file" 2>/dev/null)" || return 1
byte_count="${byte_count//[^0-9]/}"
[[ -n "$byte_count" ]] || return 1
expected_bytes=$((${#_TIRITH_CAPTURE_LINE} + 1))
[[ "$byte_count" == "$expected_bytes" ]]
}
_tirith_parse_v3_receipt_response() {
local file="$1" check_rc="$2" line_ok=0
_TIRITH_PARSED_RECEIPT=""
_tirith_capture_file_is_private "$file" || return 2
if _tirith_read_single_capture_line "$file"; then
line_ok=1
if [[ "$_TIRITH_CAPTURE_LINE" =~ ^TIRITH_EXECUTION_RECEIPT=([0-9a-f]{64})$ ]]; then
_TIRITH_PARSED_RECEIPT="${BASH_REMATCH[1]}"
fi
fi
case "$check_rc" in
0|2)
[[ $line_ok -eq 1 && -n "$_TIRITH_PARSED_RECEIPT" ]] && return 0
return 2
;;
1)
[[ ! -s "$file" ]] && return 1
return 2
;;
*) return 2 ;;
esac
}
_tirith_check_command_syntax() {
local command_text="$1"
_TIRITH_SYNTAX_ERROR="$(
set +o pipefail
builtin printf '%s\n' "$command_text" \
| builtin command "$_TIRITH_BASH_BIN" -n 2>&1
exit "${PIPESTATUS[1]}"
)"
_TIRITH_SYNTAX_RC=$?
}
_TIRITH_RECEIPT_PROTOCOL=0
_TIRITH_RECEIPT_INSTANCE=""
_TIRITH_RECEIPT_SHELL_PID="$$"
_TIRITH_RECEIPT_FAMILY="bash"
_tirith_receipt_parent_context_is_valid() {
[[ "${BASH_SUBSHELL:-0}" == "0" \
&& "$$" == "$_TIRITH_RECEIPT_SHELL_PID" ]]
}
_tirith_receipt_capture_file=""
_tirith_receipt_error_file=""
_TIRITH_RECEIPT_REGISTER_ERROR=""
if [[ $- == *i* ]]; then
_tirith_receipt_capture_file="$(_tirith_new_capture_file 2>/dev/null)" || _tirith_receipt_capture_file=""
_tirith_receipt_error_file="$(_tirith_new_capture_file 2>/dev/null)" || _tirith_receipt_error_file=""
if [[ -n "$_tirith_receipt_capture_file" && -n "$_tirith_receipt_error_file" ]] \
&& builtin command "$_TIRITH_BIN" __execution-receipt capability \
>|"$_tirith_receipt_capture_file" 2>/dev/null \
&& _tirith_read_single_capture_line "$_tirith_receipt_capture_file" \
&& [[ "$_TIRITH_CAPTURE_LINE" == "TIRITH_EXECUTION_RECEIPT_PROTOCOL=3" ]]; then
: >| "$_tirith_receipt_capture_file"
if builtin command "$_TIRITH_BIN" __execution-receipt register \
--family bash --shell-pid "$_TIRITH_RECEIPT_SHELL_PID" \
>|"$_tirith_receipt_capture_file" 2>|"$_tirith_receipt_error_file" \
&& _tirith_read_single_capture_line "$_tirith_receipt_capture_file"; then
_TIRITH_RECEIPT_INSTANCE="$_TIRITH_CAPTURE_LINE"
fi
if [[ "$_TIRITH_RECEIPT_INSTANCE" =~ ^[0-9a-f]{64}$ ]]; then
_TIRITH_RECEIPT_PROTOCOL=3
else
_TIRITH_RECEIPT_INSTANCE=""
IFS= read -r _TIRITH_RECEIPT_REGISTER_ERROR \
< "$_tirith_receipt_error_file" 2>/dev/null || :
fi
fi
[[ -n "$_tirith_receipt_capture_file" ]] \
&& _tirith_remove_capture_file "$_tirith_receipt_capture_file" >/dev/null 2>&1
[[ -n "$_tirith_receipt_error_file" ]] \
&& _tirith_remove_capture_file "$_tirith_receipt_error_file" >/dev/null 2>&1
fi
unset _tirith_receipt_capture_file _tirith_receipt_error_file _TIRITH_CAPTURE_LINE
if [[ -z "${TIRITH_SSH_REMOTE:-}" ]] \
&& { [[ -n "${SSH_CONNECTION:-}" ]] || [[ -n "${SSH_CLIENT:-}" ]] || [[ -n "${SSH_TTY:-}" ]]; }; then
TIRITH_SSH_REMOTE=1
export TIRITH_SSH_REMOTE
fi
if [[ $- == *i* ]]; then
builtin command "$_TIRITH_BIN" env snapshot >/dev/null 2>&1 &
disown 2>/dev/null || true
fi
_tirith_output() {
if [[ "${TIRITH_OUTPUT:-}" == "tty" ]]; then
printf '%s\n' "$@" >/dev/tty
else
printf '%s\n' "$@" >&2
fi
}
_tirith_escape_preview() {
printf '%q' "$1"
}
_tirith_receipt_discard() {
local channel="$1" token="$2"
[[ $_TIRITH_RECEIPT_PROTOCOL -eq 3 && -n "$token" ]] || return 0
_tirith_receipt_parent_context_is_valid || return 1
local input_fd rc
_tirith_open_exact_input_pipe "$token" || return 1
input_fd="$_TIRITH_OPENED_FD"
unset _TIRITH_OPENED_FD
_TIRITH_RECEIPT_INSTANCE="$_TIRITH_RECEIPT_INSTANCE" \
_TIRITH_RECEIPT_SHELL_PID="$_TIRITH_RECEIPT_SHELL_PID" \
_TIRITH_RECEIPT_FAMILY="$_TIRITH_RECEIPT_FAMILY" \
_TIRITH_BASH_INTERNAL=1 builtin command "$_TIRITH_BIN" __execution-receipt discard \
--channel "$channel" <&"$input_fd" >/dev/null 2>&1
rc=$?
_tirith_close_pending_fd "$input_fd" 2>/dev/null || rc=1
return "$rc"
}
_tirith_receipt_consume() {
local channel="$1" token="$2" command_text="$3"
[[ $_TIRITH_RECEIPT_PROTOCOL -eq 3 && -n "$token" && -n "$command_text" ]] || return 1
_tirith_receipt_parent_context_is_valid || return 1
local input_fd rc
_tirith_open_exact_input_pipe "$token"$'\n'"$command_text" || return 1
input_fd="$_TIRITH_OPENED_FD"
unset _TIRITH_OPENED_FD
_TIRITH_RECEIPT_INSTANCE="$_TIRITH_RECEIPT_INSTANCE" \
_TIRITH_RECEIPT_SHELL_PID="$_TIRITH_RECEIPT_SHELL_PID" \
_TIRITH_RECEIPT_FAMILY="$_TIRITH_RECEIPT_FAMILY" \
_TIRITH_BASH_INTERNAL=1 builtin command "$_TIRITH_BIN" __execution-receipt consume \
--channel "$channel" <&"$input_fd" >/dev/null
rc=$?
_tirith_close_pending_fd "$input_fd" 2>/dev/null || rc=1
return "$rc"
}
_tirith_receipt_reconcile() {
local channel="$1" token="$2"
[[ $_TIRITH_RECEIPT_PROTOCOL -eq 3 && -n "$token" ]] || return 1
_tirith_receipt_parent_context_is_valid || return 1
local input_fd rc
_tirith_open_exact_input_pipe "$token" || return 1
input_fd="$_TIRITH_OPENED_FD"
unset _TIRITH_OPENED_FD
_TIRITH_RECEIPT_INSTANCE="$_TIRITH_RECEIPT_INSTANCE" \
_TIRITH_RECEIPT_SHELL_PID="$_TIRITH_RECEIPT_SHELL_PID" \
_TIRITH_RECEIPT_FAMILY="$_TIRITH_RECEIPT_FAMILY" \
_TIRITH_BASH_INTERNAL=1 builtin command "$_TIRITH_BIN" __execution-receipt reconcile \
--channel "$channel" <&"$input_fd" >/dev/null 2>&1
rc=$?
_tirith_close_pending_fd "$input_fd" 2>/dev/null || rc=1
return "$rc"
}
_tirith_parse_approval() {
local file="$1"
_tirith_ap_required="no"
_tirith_ap_timeout=0
_tirith_ap_fallback="block"
_tirith_ap_rule=""
_tirith_ap_desc=""
if [[ ! -r "$file" ]]; then
_tirith_output "tirith: warning: approval file missing or unreadable, failing closed"
_tirith_remove_capture_file "$file" >/dev/null 2>&1 _tirith_ap_required="yes"
_tirith_ap_fallback="block"
_tirith_ap_timeout=0
return 1
fi
local valid_keys=0
while IFS='=' read -r key value; do
case "$key" in
TIRITH_REQUIRES_APPROVAL) _tirith_ap_required="$value"; valid_keys=$((valid_keys + 1)) ;;
TIRITH_APPROVAL_TIMEOUT) _tirith_ap_timeout="$value" ;;
TIRITH_APPROVAL_FALLBACK) _tirith_ap_fallback="$value" ;;
TIRITH_APPROVAL_RULE) _tirith_ap_rule="$value" ;;
TIRITH_APPROVAL_DESCRIPTION) _tirith_ap_desc="$value" ;;
esac
done < "$file"
_tirith_remove_capture_file "$file" >/dev/null 2>&1
if [[ $valid_keys -eq 0 ]]; then
_tirith_output "tirith: warning: approval file corrupt, failing closed"
_tirith_ap_required="yes"
_tirith_ap_fallback="block"
_tirith_ap_timeout=0
return 1
fi
return 0
}
_tirith_parse_warn_ack() {
local file="$1"
_tirith_wa_findings=0
_tirith_wa_max_severity=""
if [[ ! -r "$file" ]]; then
_tirith_remove_capture_file "$file" >/dev/null 2>&1
return 1
fi
while IFS='=' read -r key value; do
case "$key" in
TIRITH_WARN_ACK_FINDINGS) _tirith_wa_findings="$value" ;;
TIRITH_WARN_ACK_MAX_SEVERITY) _tirith_wa_max_severity="$value" ;;
esac
done < "$file"
_tirith_remove_capture_file "$file" >/dev/null 2>&1
return 0
}
_TIRITH_STATE_DIR="${XDG_STATE_HOME:-}"
_TIRITH_STATE_DIR="${_TIRITH_STATE_DIR#"${_TIRITH_STATE_DIR%%[![:space:]]*}"}"
_TIRITH_STATE_DIR="${_TIRITH_STATE_DIR%"${_TIRITH_STATE_DIR##*[![:space:]]}"}"
_TIRITH_STATE_DIR="${_TIRITH_STATE_DIR:-$HOME/.local/state}/tirith"
_TIRITH_SAFE_MODE_FLAG="$_TIRITH_STATE_DIR/bash-safe-mode"
_tirith_check_safe_mode() { [[ -f "$_TIRITH_SAFE_MODE_FLAG" ]]; }
_tirith_persist_safe_mode() {
if [[ -z "$_TIRITH_MKDIR_BIN" ]] \
|| ! builtin command "$_TIRITH_MKDIR_BIN" -p -- "$_TIRITH_STATE_DIR" 2>/dev/null \
|| ! builtin printf '1\n' > "$_TIRITH_SAFE_MODE_FLAG" 2>/dev/null; then
builtin printf '%s\n' "tirith: warning: could not persist safe-mode flag" >&2
fi
}
_TIRITH_ENTER_CAP_SCHEMA=2
_TIRITH_ENTER_CAP_FILE="$_TIRITH_STATE_DIR/bash-enter-capability"
_tirith_enter_capability_proven() {
[[ -r "$_TIRITH_ENTER_CAP_FILE" ]] || return 1
local size
[[ -n "$_TIRITH_WC_BIN" ]] || return 1
size="$(builtin command "$_TIRITH_WC_BIN" -c < "$_TIRITH_ENTER_CAP_FILE" 2>/dev/null)" \
|| return 1
size="${size//[^0-9]/}"
[[ -n "$size" ]] || return 1
(( size > 4096 )) && return 1
local schema="" cache_bash_version="" cache_bash_path="" capability=""
local cache_bash_fingerprint=""
local key value
while IFS='=' read -r key value; do
case "$key" in
schema) schema="$value" ;;
bash_version) cache_bash_version="$value" ;;
bash_path) cache_bash_path="$value" ;;
bash_fingerprint) cache_bash_fingerprint="$value" ;;
enter_capability) capability="$value" ;;
esac
done < "$_TIRITH_ENTER_CAP_FILE"
[[ "$schema" == "$_TIRITH_ENTER_CAP_SCHEMA" ]] || return 1
[[ "$capability" == "works" ]] || return 1
[[ -n "$cache_bash_version" ]] || return 1
[[ "$cache_bash_version" == "$BASH_VERSION" ]] || return 1
[[ -n "$cache_bash_path" ]] || return 1
[[ "$cache_bash_path" == "${BASH:-}" ]] || return 1
[[ -n "$cache_bash_fingerprint" ]] || return 1
local live_mtime live_size live_fp
if live_mtime="$(builtin command stat -Lf %m "${BASH:-/dev/null}" 2>/dev/null)"; then
:
else
live_mtime="$(builtin command stat -Lc %Y "${BASH:-/dev/null}" 2>/dev/null)" || return 1
fi
if live_size="$(builtin command stat -Lf %z "${BASH:-/dev/null}" 2>/dev/null)"; then
:
else
live_size="$(builtin command stat -Lc %s "${BASH:-/dev/null}" 2>/dev/null)" || return 1
fi
[[ -n "$live_mtime" && -n "$live_size" ]] || return 1
live_fp="${live_mtime}:${live_size}"
[[ "$cache_bash_fingerprint" == "$live_fp" ]] || return 1
return 0
}
_tirith_read_history_entry() {
local HISTTIMEFORMAT=''
local raw
raw="$(builtin history 1 2>/dev/null)" || return 1
[[ -z "$raw" ]] && return 1
if [[ "$raw" =~ ^[[:space:]]*([0-9]+)[[:space:]]+(.*)$ ]]; then
printf '%s|%s\n' "${BASH_REMATCH[1]}" "${BASH_REMATCH[2]}"
return 0
fi
return 1
}
_tirith_normalize_spacing() {
local input="$1" s="" pending_space=0 i char next op restore_patsub=0
local default_stdout_fd=' 1>' default_stdin_fd=' 0<'
for ((i=0; i<${#input}; i++)); do
char="${input:i:1}"
next="${input:i+1:1}"
if [[ "$char" == "\\" && "$next" == $'\n' ]]; then
[[ -n "$s" ]] && pending_space=1
i=$((i + 1))
continue
fi
case "$char" in
[[:space:]])
[[ -n "$s" ]] && pending_space=1
;;
*)
[[ $pending_space -eq 1 ]] && s+=" "
s+="$char"
pending_space=0
;;
esac
done
if shopt -q patsub_replacement 2>/dev/null; then
shopt -u patsub_replacement
restore_patsub=1
fi
for op in '|' '&' ';' '>' '<'; do
while [[ "$s" == *" $op"* ]]; do s="${s//" $op"/$op}"; done
while [[ "$s" == *"$op "* ]]; do s="${s//"$op "/$op}"; done
done
while [[ "$s" == *"$default_stdout_fd"* ]]; do s="${s//$default_stdout_fd/>}"; done
while [[ "$s" == *"$default_stdin_fd"* ]]; do s="${s//$default_stdin_fd/<}"; done
[[ "$s" == 1\>* ]] && s="${s#1}"
[[ "$s" == 0\<* ]] && s="${s#0}"
[[ $restore_patsub -eq 1 ]] && shopt -s patsub_replacement
printf '%s' "$s"
}
_tirith_cmd_is_in_line() {
local needle="$1" haystack="$2"
[[ -z "$needle" || -z "$haystack" ]] && return 1
local n_needle n_haystack n_segment segment="" quote="" escaped=0 at_word_start=1
local i char next previous
n_needle="$(_tirith_normalize_spacing "$needle")"
[[ -n "$n_needle" ]] || return 1
n_haystack="$(_tirith_normalize_spacing "$haystack")"
[[ "$n_haystack" == "$n_needle" ]] && return 0
[[ "$haystack" == *'<<'* ]] && return 1
for ((i=0; i<=${#haystack}; i++)); do
char="${haystack:i:1}"
next="${haystack:i+1:1}"
previous="${haystack:i-1:1}"
if [[ $i -eq ${#haystack} ]]; then
char=$'\n'
fi
if [[ $escaped -eq 1 ]]; then
segment+="$char"
escaped=0
at_word_start=0
continue
fi
if [[ "$quote" == "'" ]]; then
segment+="$char"
[[ "$char" == "'" ]] && quote=""
at_word_start=0
continue
fi
if [[ "$quote" == '"' ]]; then
segment+="$char"
if [[ "$char" == "\\" ]]; then
escaped=1
elif [[ "$char" == '"' ]]; then
quote=""
fi
at_word_start=0
continue
fi
case "$char" in
"\\") segment+="$char"; escaped=1; at_word_start=0; continue ;;
"'"|'"') segment+="$char"; quote="$char"; at_word_start=0; continue ;;
'#')
if [[ $at_word_start -eq 1 ]]; then
char=$'\n'
else
segment+="$char"
at_word_start=0
continue
fi
;;
esac
case "$char" in
' '|$'\t'|$'\r') segment+="$char"; at_word_start=1; continue ;;
'&')
if [[ "$next" == '>' || "$previous" == '>' || "$previous" == '<' ]]; then
segment+="$char"
at_word_start=0
continue
fi
;;
'>'|'<') segment+="$char"; at_word_start=1; continue ;;
';'|'|'|'('|')'|$'\n') ;;
*) segment+="$char"; at_word_start=0; continue ;;
esac
n_segment="$(_tirith_normalize_spacing "$segment")"
if [[ -n "$n_segment" ]]; then
[[ "$n_segment" == "$n_needle" ]] && return 0
case "$n_segment" in
if\ *|then\ *|elif\ *|while\ *|until\ *|do\ *|else\ *)
n_segment="${n_segment#* }"
[[ "$n_segment" == "$n_needle" ]] && return 0
;;
esac
fi
segment=""
at_word_start=1
[[ "$char" == $'\n' ]] && break
done
return 1
}
_tirith_history_is_trustworthy_for_enforcement() {
case ":${HISTCONTROL:-}:" in
*:ignorespace:*|*:ignoredups:*|*:ignoreboth:*) return 1 ;;
esac
[[ -n "${HISTIGNORE:-}" ]] && return 1
if ! shopt -oq history 2>/dev/null; then
return 1
fi
return 0
}
_tirith_prepare_extdebug_block() {
if shopt -q extdebug; then
[[ "${_TIRITH_OWNS_EXTDEBUG:-0}" == "1" ]] && return 0
return 2
fi
shopt -s extdebug || return 1
_TIRITH_OWNS_EXTDEBUG=1
return 0
}
_tirith_disable_owned_extdebug() {
[[ "${_TIRITH_OWNS_EXTDEBUG:-0}" == "1" ]] || return 0
shopt -u extdebug || return 1
_TIRITH_OWNS_EXTDEBUG=0
return 0
}
_tirith_run_chained_debug_trap() {
builtin eval -- "$_TIRITH_PREV_DEBUG_TRAP"
}
_tirith_debug_trampoline() {
local _user_bash_command="$BASH_COMMAND"
local _user_line_id="${BASH_LINENO[0]:-0}"
local _user_call_depth="${#FUNCNAME[@]}"
if [[ -n "${_TIRITH_PREV_DEBUG_TRAP:-}" ]]; then
_tirith_run_chained_debug_trap || true
fi
_tirith_preexec "$_user_line_id" "$_user_call_depth" "$_user_bash_command"
}
_tirith_extract_trap_body() {
local specification="${1:-}" signal="${2:-}"
local prefix="trap -- '" suffix="' $signal" encoded quote_escape quartet char i=0
_TIRITH_EXTRACTED_TRAP=""
[[ "$signal" == "DEBUG" && "$specification" == "$prefix"*"$suffix" ]] || return 1
encoded="${specification#"$prefix"}"
encoded="${encoded%"$suffix"}"
quote_escape="'\\''"
while (( i < ${#encoded} )); do
char="${encoded:i:1}"
if [[ "$char" == "'" ]]; then
quartet="${encoded:i:4}"
[[ "$quartet" == "$quote_escape" ]] || return 1
i=$((i + 4))
else
i=$((i + 1))
fi
done
builtin eval -- "_TIRITH_EXTRACTED_TRAP='$encoded'"
}
_tirith_read_debug_trap_capture() {
local file="$1" captured="" line="" first=1 marker="trap -- '" candidate
_TIRITH_CAPTURED_DEBUG_TRAP_SPEC=""
_tirith_capture_file_is_private "$file" || return 1
while IFS= read -r line || [[ -n "$line" ]]; do
if [[ $first -eq 1 ]]; then
captured="$line"
first=0
else
captured+=$'\n'"$line"
fi
done < "$file"
[[ -z "$captured" ]] && return 0
[[ "$captured" == *"$marker"* ]] || return 1
candidate="${marker}${captured##*"$marker"}"
[[ "$candidate" == "$marker"*"' DEBUG" ]] || return 1
_tirith_extract_trap_body "$candidate" DEBUG || return 1
_TIRITH_CAPTURED_DEBUG_TRAP_SPEC="$candidate"
return 0
}
_tirith_install_debug_trap() {
[[ "${_TIRITH_DEBUG_TRAP_CAPTURE_READY:-0}" == "1" ]] || return 1
local current="${_TIRITH_CAPTURED_DEBUG_TRAP_SPEC:-}"
if [[ "$current" == "trap -- '_tirith_debug_trampoline' DEBUG" ]]; then
_TIRITH_DEBUG_TRAP_INSTALLED=1
return 0
fi
[[ "$current" == *"_tirith_debug_trampoline"* ]] && return 1
_TIRITH_PREV_DEBUG_TRAP=""
if [[ -n "$current" ]]; then
_tirith_extract_trap_body "$current" DEBUG || return 1
_TIRITH_PREV_DEBUG_TRAP="$_TIRITH_EXTRACTED_TRAP"
fi
builtin trap '_tirith_debug_trampoline' DEBUG || return 1
_TIRITH_DEBUG_TRAP_INSTALLED=1
return 0
}
_tirith_verify_debug_trap_ownership() {
_TIRITH_DEBUG_TRAP_OWNERSHIP_OK=0
local file="${_TIRITH_DEBUG_OWNERSHIP_FILE:-}"
if [[ -n "$file" ]] \
&& _tirith_read_debug_trap_capture "$file" \
&& [[ "${_TIRITH_CAPTURED_DEBUG_TRAP_SPEC:-}" == "trap -- '_tirith_debug_trampoline' DEBUG" ]]; then
_TIRITH_DEBUG_TRAP_OWNERSHIP_OK=1
return 0
fi
_tirith_session_lost_debug_trap
return 1
}
_tirith_prepare_debug_trap_capture() {
[[ "${_TIRITH_DEBUG_TRAP_CAPTURE_READY:-0}" == "0" ]] || return 0
[[ -n "${_TIRITH_DEBUG_CAPTURE_FILE:-}" ]] && return 0
_TIRITH_DEBUG_CAPTURE_FILE="$(_tirith_new_capture_file 2>/dev/null)" || _TIRITH_DEBUG_CAPTURE_FILE=""
[[ -n "$_TIRITH_DEBUG_CAPTURE_FILE" ]]
}
_tirith_finalize_debug_trap_capture() {
[[ "${_TIRITH_DEBUG_TRAP_CAPTURE_READY:-0}" == "0" ]] || return 0
local capture_ok=0
if [[ -n "${_TIRITH_DEBUG_CAPTURE_FILE:-}" ]] \
&& _tirith_read_debug_trap_capture "$_TIRITH_DEBUG_CAPTURE_FILE"; then
capture_ok=1
fi
if [[ -n "${_TIRITH_DEBUG_CAPTURE_FILE:-}" ]]; then
_tirith_remove_capture_file "$_TIRITH_DEBUG_CAPTURE_FILE" >/dev/null 2>&1 || capture_ok=0
fi
_TIRITH_DEBUG_CAPTURE_FILE=""
if [[ $capture_ok -ne 1 ]]; then
_TIRITH_DEBUG_TRAP_CAPTURE_READY=2
_TIRITH_PREEXEC_PHASE="off"
_TIRITH_PREEXEC_ENFORCE=0
_TIRITH_PREEXEC_RECEIPTS_TRUSTED=0
export TIRITH_BASH_EFFECTIVE_PROTECTION="off"
_tirith_set_status "degraded"
_tirith_output "tirith: bash preexec hook was not installed because the existing DEBUG trap could not be preserved safely; existing debugger state was left unchanged"
return 1
fi
_TIRITH_DEBUG_TRAP_CAPTURE_READY=1
_TIRITH_DEBUG_OWNERSHIP_FILE="$(_tirith_new_capture_file 2>/dev/null)" \
|| _TIRITH_DEBUG_OWNERSHIP_FILE=""
if [[ -z "$_TIRITH_DEBUG_OWNERSHIP_FILE" ]]; then
_TIRITH_DEBUG_TRAP_CAPTURE_READY=2
_TIRITH_PREEXEC_PHASE="off"
_TIRITH_PREEXEC_ENFORCE=0
_TIRITH_PREEXEC_RECEIPTS_TRUSTED=0
export TIRITH_BASH_EFFECTIVE_PROTECTION="off"
_tirith_set_status "degraded"
_tirith_output "tirith: bash preexec hook was not installed because DEBUG-trap ownership could not be checked safely"
return 1
fi
if ! _tirith_install_debug_trap; then
_tirith_remove_capture_file "$_TIRITH_DEBUG_OWNERSHIP_FILE" >/dev/null 2>&1 || true
_TIRITH_DEBUG_OWNERSHIP_FILE=""
_TIRITH_DEBUG_TRAP_CAPTURE_READY=2
_TIRITH_PREEXEC_PHASE="off"
_TIRITH_PREEXEC_ENFORCE=0
_TIRITH_PREEXEC_RECEIPTS_TRUSTED=0
export TIRITH_BASH_EFFECTIVE_PROTECTION="off"
_tirith_set_status "degraded"
_tirith_output "tirith: bash preexec hook was not installed because the existing DEBUG trap could not be chained safely; existing debugger state was left unchanged"
return 1
fi
if [[ "${_TIRITH_PREEXEC_ENFORCE_PENDING:-0}" == "1" ]]; then
_TIRITH_PREEXEC_ENFORCE_PENDING=0
_TIRITH_PREEXEC_ENFORCE=1
export TIRITH_BASH_EFFECTIVE_PROTECTION="blocks"
_tirith_set_status "blocks"
else
export TIRITH_BASH_EFFECTIVE_PROTECTION="warn-only"
[[ "${TIRITH_STATUS:-}" == "degraded" ]] || _tirith_set_status "warn-only"
fi
return 0
}
_tirith_discard_pending_debug_trap_capture() {
[[ "${_TIRITH_DEBUG_TRAP_CAPTURE_READY:-0}" == "0" ]] || return 0
local capture_file="${_TIRITH_DEBUG_CAPTURE_FILE:-}"
_TIRITH_DEBUG_CAPTURE_FILE=""
[[ -n "$capture_file" ]] || return 0
_tirith_capture_file_is_private "$capture_file" || return 0
_tirith_remove_capture_file "$capture_file" >/dev/null 2>&1 || true
}
_tirith_preexec_prompt_begin() {
local previous_status=$?
_TIRITH_PREEXEC_PROMPT_STATUS="$previous_status"
[[ "${_TIRITH_PREEXEC_PHASE:-}" == "off" ]] && return "$previous_status"
_TIRITH_PREEXEC_PHASE="prompt"
_TIRITH_PREEXEC_AWAITING_USER=0
_TIRITH_DEBUG_TRAP_OWNERSHIP_OK=0
_TIRITH_PREEXEC_PROMPT_MUTATED=0
return "$previous_status"
}
_tirith_restore_prompt_status() {
return "${_TIRITH_PREEXEC_PROMPT_STATUS:-0}"
}
_tirith_eval_user_prompt_command() {
builtin eval -- "$_TIRITH_PREEXEC_USER_PROMPT_COMMAND"
}
_tirith_run_user_prompt_command() {
local previous_status=$?
local expected_user_prompt="${_TIRITH_PREEXEC_USER_PROMPT_COMMAND:-}"
[[ -n "${_TIRITH_PREEXEC_USER_PROMPT_COMMAND:-}" ]] \
|| return "$previous_status"
_tirith_restore_prompt_status
_tirith_eval_user_prompt_command
local prompt_rc=$?
if [[ "${PROMPT_COMMAND:-}" != "${_TIRITH_PREEXEC_SCALAR_WRAPPER:-}" ]] \
|| [[ "${_TIRITH_PREEXEC_USER_PROMPT_COMMAND:-}" != "$expected_user_prompt" ]]; then
_TIRITH_PREEXEC_PROMPT_MUTATED=1
_TIRITH_PREEXEC_USER_PROMPT_COMMAND="$expected_user_prompt"
fi
return "$prompt_rc"
}
_tirith_preexec_prompt_end() {
local previous_status=$?
unset _TIRITH_PREEXEC_PROMPT_STATUS
[[ "${_TIRITH_PREEXEC_PHASE:-}" == "off" ]] && return "$previous_status"
if [[ "${_TIRITH_PREEXEC_ENFORCE:-0}" == "1" ]] \
&& [[ "${_TIRITH_OWNS_EXTDEBUG:-0}" != "1" ]] \
&& shopt -q extdebug; then
_tirith_session_degrade_to_warn_only \
"tirith: extdebug became enabled by prompt code outside Tirith; enforcement is disabled because user debugger state cannot be safely restored"
fi
if [[ "${_TIRITH_DEBUG_TRAP_INSTALLED:-0}" == "1" ]]; then
if [[ "${_TIRITH_DEBUG_TRAP_OWNERSHIP_OK:-0}" != "1" ]]; then
_tirith_session_lost_debug_trap
return "$previous_status"
fi
fi
if [[ "${_TIRITH_PREEXEC_PROMPT_GUARDS:-0}" == "1" ]] \
&& { [[ "${_TIRITH_PREEXEC_PROMPT_MUTATED:-0}" == "1" ]] \
|| ! _tirith_preexec_prompt_guards_attached; }; then
_TIRITH_PREEXEC_PROMPT_GUARDS=0
_TIRITH_PREEXEC_PHASE="unbracketed"
_tirith_session_degrade_to_warn_only \
"tirith: PROMPT_COMMAND changed while the prompt was running; enforcement is disabled before the next input boundary"
return "$previous_status"
fi
_TIRITH_PREEXEC_PHASE="user"
_TIRITH_PREEXEC_AWAITING_USER=1
return "$previous_status"
}
_tirith_prompt_command_array_supported() {
(( BASH_VERSINFO[0] > 5 \
|| (BASH_VERSINFO[0] == 5 && BASH_VERSINFO[1] >= 1) ))
}
_tirith_prompt_command_attrs_safe() {
local attrs="$1"
[[ "$attrs" != *[rAilnu]* ]]
}
_tirith_preexec_prompt_guards_attached() {
local pc_decl pc_attrs
pc_decl="$(declare -p PROMPT_COMMAND 2>/dev/null)" || return 1
pc_attrs="${pc_decl#declare }"
pc_attrs="${pc_attrs%% *}"
_tirith_prompt_command_attrs_safe "$pc_attrs" || return 1
if [[ "$pc_attrs" == *a* ]]; then
_tirith_prompt_command_array_supported || return 1
local count="${#PROMPT_COMMAND[@]}"
[[ "$count" -ge 3 ]] || return 1
[[ "${PROMPT_COMMAND[0]}" == "_tirith_preexec_prompt_begin" ]] || return 1
[[ "${PROMPT_COMMAND[1]}" == "$_TIRITH_PREEXEC_BOOTSTRAP_COMMAND" ]] || return 1
[[ "${PROMPT_COMMAND[$((count - 1))]}" == "_tirith_preexec_prompt_end" ]]
return
fi
[[ "${PROMPT_COMMAND:-}" == "${_TIRITH_PREEXEC_SCALAR_WRAPPER:-}" ]]
}
_tirith_install_preexec_prompt_guards() {
if _tirith_preexec_prompt_guards_attached; then
_TIRITH_PREEXEC_PROMPT_GUARDS=1
return 0
fi
local pc_decl pc_attrs
pc_decl="$(declare -p PROMPT_COMMAND 2>/dev/null)" || pc_decl=""
pc_attrs="${pc_decl#declare }"
pc_attrs="${pc_attrs%% *}"
_tirith_prompt_command_attrs_safe "$pc_attrs" || return 1
if [[ "$pc_attrs" == *a* ]]; then
_tirith_prompt_command_array_supported || return 1
PROMPT_COMMAND=(
_tirith_preexec_prompt_begin
"$_TIRITH_PREEXEC_BOOTSTRAP_COMMAND"
"${PROMPT_COMMAND[@]}"
_tirith_preexec_prompt_end
) 2>/dev/null || return 1
else
local existing="${PROMPT_COMMAND:-}"
if [[ -n "$existing" ]]; then
_tirith_check_command_syntax "$existing"
if [[ "${_TIRITH_SYNTAX_RC:-1}" -ne 0 ]] \
|| [[ "${_TIRITH_SYNTAX_ERROR:-}" == *"here-document at line"*"delimited by end-of-file"* ]] \
|| [[ "$existing" == *\\ ]] \
|| [[ "$existing" == *'<<'* ]]; then
unset _TIRITH_SYNTAX_ERROR _TIRITH_SYNTAX_RC
return 1
fi
unset _TIRITH_SYNTAX_ERROR _TIRITH_SYNTAX_RC
fi
_TIRITH_PREEXEC_USER_PROMPT_COMMAND="$existing"
_TIRITH_PREEXEC_SCALAR_WRAPPER="_tirith_preexec_prompt_begin"$'\n'"$_TIRITH_PREEXEC_BOOTSTRAP_COMMAND"$'\n'"_tirith_run_user_prompt_command"$'\n'"_tirith_preexec_prompt_end"
PROMPT_COMMAND="$_TIRITH_PREEXEC_SCALAR_WRAPPER" 2>/dev/null || return 1
fi
_tirith_preexec_prompt_guards_attached || return 1
_TIRITH_PREEXEC_PROMPT_GUARDS=1
return 0
}
_tirith_set_status() {
TIRITH_STATUS="$1"
}
_tirith_warn_degraded_once() {
[[ -n "${_TIRITH_DEGRADE_WARNED:-}" ]] && return 0
_TIRITH_DEGRADE_WARNED=1
[[ $- == *i* ]] || return 0
_tirith_output "tirith: protection downgraded to warn-only (does not block) — run 'tirith doctor' for details"
[[ -n "${1:-}" ]] && _tirith_output " $1"
return 0
}
_tirith_session_degrade_to_warn_only() {
local reason="$1"
_TIRITH_PREEXEC_ENFORCE=0
_TIRITH_WARN_ONLY_USE_BASH_COMMAND=1
_TIRITH_PREEXEC_RECEIPTS_TRUSTED=0
_TIRITH_PREEXEC_WARNED=1 export TIRITH_BASH_EFFECTIVE_PROTECTION="warn-only"
_tirith_set_status "degraded"
_tirith_warn_degraded_once "$reason"
}
_tirith_session_lost_debug_trap() {
_TIRITH_PREEXEC_PHASE="off"
_TIRITH_PREEXEC_ENFORCE=0
_TIRITH_PREEXEC_ENFORCE_PENDING=0
_TIRITH_PREEXEC_RECEIPTS_TRUSTED=0
_TIRITH_DEBUG_TRAP_WATCH=0
_TIRITH_PREEXEC_WARNED=1
export TIRITH_BASH_EFFECTIVE_PROTECTION="off"
_tirith_set_status "degraded"
[[ -n "${_TIRITH_OFF_WARNED:-}" ]] && return 0
_TIRITH_OFF_WARNED=1
[[ $- == *i* ]] || return 0
_tirith_output "tirith: protection is OFF for this shell — run 'tirith doctor' for details"
_tirith_output " another tool replaced or removed Tirith's DEBUG trap; commands are no longer being checked. Tirith did not take the trap back. Restart your shell, or load tirith after that tool."
return 0
}
_tirith_preexec_receipt_check() {
local scan_target="$1" warn_only="$2"
_tirith_receipt_parent_context_is_valid || return 1
local -a render_args
render_args=()
[[ "$warn_only" == "yes" ]] && render_args=(--warn-only)
local stdout_file rc
stdout_file="$(_tirith_new_capture_file 2>/dev/null)" || return 1
[[ -n "$stdout_file" ]] || return 1
_TIRITH_HOOK=1 _TIRITH_BASH_INTERNAL=1 \
_TIRITH_RECEIPT_INSTANCE="$_TIRITH_RECEIPT_INSTANCE" \
_TIRITH_RECEIPT_SHELL_PID="$_TIRITH_RECEIPT_SHELL_PID" \
_TIRITH_RECEIPT_FAMILY="$_TIRITH_RECEIPT_FAMILY" \
builtin command "$_TIRITH_BIN" check --approval-check --execution-receipt bash-preexec \
--non-interactive --interactive --shell posix "${render_args[@]}" -- "$scan_target" \
>|"$stdout_file"
rc=$?
local parse_rc token
_tirith_parse_v3_receipt_response "$stdout_file" "$rc"
parse_rc=$?
token="$_TIRITH_PARSED_RECEIPT"
_tirith_remove_capture_file "$stdout_file" >/dev/null 2>&1 || parse_rc=2
unset _TIRITH_CAPTURE_LINE _TIRITH_PARSED_RECEIPT
case "$parse_rc" in
0) ;;
1) return 1 ;;
*)
_tirith_receipt_discard bash-preexec "$token"
return 1
;;
esac
if ! _tirith_receipt_consume bash-preexec "$token" "$scan_target"; then
_tirith_receipt_reconcile bash-preexec "$token" || return 1
fi
return 0
}
_tirith_preexec_block_current_line() {
local reason="${1:-}" extdebug_state
_tirith_prepare_extdebug_block
extdebug_state=$?
if [[ $extdebug_state -eq 1 ]]; then
_TIRITH_PREEXEC_BLOCK_ACTIVE=0
_TIRITH_PREEXEC_ACTIVE_DECISION="allow"
_tirith_session_degrade_to_warn_only \
"tirith: bash could not enable extdebug for the blocking decision; this command was NOT blocked and enforcement is disabled for this shell"
return 0
fi
if [[ $extdebug_state -eq 2 ]]; then
_tirith_session_degrade_to_warn_only \
"tirith: extdebug became enabled outside Tirith; the current command was blocked, but Tirith cannot safely own or restore debugger state, so enforcement is disabled for this shell"
elif [[ -n "$reason" ]]; then
_tirith_session_degrade_to_warn_only "$reason"
fi
_TIRITH_PREEXEC_BLOCK_ACTIVE=1
_TIRITH_PREEXEC_ACTIVE_DECISION="block"
return 1
}
_tirith_preexec() {
[[ "${_TIRITH_BASH_INTERNAL:-0}" == "1" ]] && return 0
local bash_cmd="${3:-$BASH_COMMAND}"
local entry history_index="" history_line=""
if entry="$(_tirith_read_history_entry)"; then
history_index="${entry%%|*}"
history_line="${entry#*|}"
fi
if [[ "${_TIRITH_PREEXEC_PROMPT_GUARDS:-0}" == "1" ]] \
&& [[ "$bash_cmd" == "_tirith_preexec_prompt_begin" ]]; then
local automatic_prompt_begin=0
if [[ "${_TIRITH_PREEXEC_PHASE:-startup}" != "user" ]] \
|| [[ -n "${_TIRITH_PREEXEC_ACTIVE_DECISION:-}" ]]; then
automatic_prompt_begin=1
elif ! _tirith_cmd_is_in_line "$bash_cmd" "$history_line"; then
automatic_prompt_begin=1
fi
if [[ $automatic_prompt_begin -eq 1 ]]; then
_TIRITH_PREEXEC_PHASE="prompt"
_TIRITH_PREEXEC_AWAITING_USER=0
_TIRITH_PREEXEC_BLOCK_ACTIVE=0
_TIRITH_PREEXEC_ACTIVE_DECISION=""
unset _tirith_last_key _tirith_last_rc _tirith_last_cmd
if ! _tirith_disable_owned_extdebug; then
_tirith_session_degrade_to_warn_only \
"tirith: could not restore Tirith-owned extdebug state at the prompt boundary; enforcement is disabled for this shell"
elif [[ "${_TIRITH_PREEXEC_ENFORCE:-0}" == "1" ]] \
&& shopt -q extdebug; then
_tirith_session_degrade_to_warn_only \
"tirith: extdebug became enabled outside Tirith; enforcement is disabled because user debugger state cannot be safely restored"
fi
return 0
fi
fi
if [[ "${_TIRITH_PREEXEC_PROMPT_GUARDS:-0}" == "1" ]] \
&& [[ "$bash_cmd" == "_tirith_preexec_prompt_end" ]] \
&& [[ "${_TIRITH_PREEXEC_PHASE:-}" == "prompt" ]]; then
return 0
fi
case "${_TIRITH_PREEXEC_PHASE:-startup}" in
startup|prompt|off) return 0 ;;
esac
if [[ "${_TIRITH_PREEXEC_AWAITING_USER:-0}" == "1" ]] \
&& { [[ "$bash_cmd" == "exit" ]] \
|| [[ "$bash_cmd" == 'return "$previous_status"' ]]; } \
&& ! _tirith_cmd_is_in_line "$bash_cmd" "$history_line"; then
return 0
fi
local call_depth="${2:-1}"
if [[ "$call_depth" -gt 1 ]]; then
if [[ "${_TIRITH_PREEXEC_BLOCK_ACTIVE:-0}" == "1" ]] \
&& [[ "${_TIRITH_PREEXEC_ACTIVE_DECISION:-}" == "block" ]]; then
return 1
fi
return 0
fi
if [[ "${_TIRITH_PREEXEC_BLOCK_ACTIVE:-0}" == "1" ]]; then
return 1
fi
_TIRITH_PREEXEC_AWAITING_USER=0
if [[ "${_TIRITH_PREEXEC_PHASE:-}" == "unbracketed" ]] \
&& ! _tirith_cmd_is_in_line "$bash_cmd" "$history_line"; then
return 0
fi
if [[ "${_TIRITH_PREEXEC_PROMPT_GUARDS:-0}" == "1" ]] \
&& ! _tirith_preexec_prompt_guards_attached; then
_TIRITH_PREEXEC_PROMPT_GUARDS=0
_TIRITH_PREEXEC_PHASE="unbracketed"
_TIRITH_PREEXEC_RECEIPTS_TRUSTED=0
_TIRITH_WARN_ONLY_USE_BASH_COMMAND=1
if [[ "${_TIRITH_PREEXEC_ENFORCE:-0}" == "1" ]]; then
_tirith_session_degrade_to_warn_only \
"tirith: PROMPT_COMMAND no longer contains Tirith's prompt-boundary guards; enforcement is disabled for this shell"
fi
if ! _tirith_cmd_is_in_line "$bash_cmd" "$history_line"; then
return 0
fi
fi
if [[ -z "${_TIRITH_PREEXEC_WARNED:-}" ]] \
&& [[ $- == *i* ]] \
&& [[ "${_TIRITH_PREEXEC_ENFORCE:-0}" != "1" ]]; then
_TIRITH_PREEXEC_WARNED=1
_tirith_output "tirith: bash is in preexec mode (warn-only, does not block)"
_tirith_output " Run 'tirith doctor' to test enter mode (blocking) for this shell"
fi
local line_id="${1:-${BASH_LINENO[${#BASH_LINENO[@]}-1]:-0}}"
local _tirith_prev_internal="${_TIRITH_BASH_INTERNAL:-0}"
local rc
if [[ "${_TIRITH_PREEXEC_ENFORCE:-0}" == "1" ]]; then
if [[ -z "$history_index" ]]; then
_tirith_last_key="$line_id"
_tirith_last_rc=1
_tirith_preexec_block_current_line \
"tirith: bash history is unavailable in this shell (history disabled or buffer empty), cannot enforce whole-line semantics; falling back to warn-only. For guaranteed blocking, use enter mode (export TIRITH_BASH_MODE=enter)."
return $?
fi
if ! _tirith_cmd_is_in_line "$bash_cmd" "$history_line"; then
_tirith_last_key="$line_id"
_tirith_last_rc=1
_tirith_preexec_block_current_line \
"tirith: bash history no longer matches BASH_COMMAND (likely HISTCONTROL/HISTIGNORE filtering, an alias, or a shell transformation outside the whole-line drift check); cannot enforce whole-line semantics; falling back to warn-only. For guaranteed blocking, use enter mode (export TIRITH_BASH_MODE=enter)."
return $?
fi
local history_key="${history_index}|${history_line}"
if [[ -n "${_TIRITH_PREEXEC_BLOCK_HISTORY_KEY:-}" ]]; then
if [[ "$_TIRITH_PREEXEC_BLOCK_HISTORY_KEY" == "$history_key" ]]; then
_tirith_last_key="$line_id"
_tirith_last_rc=1
_tirith_preexec_block_current_line
return $?
fi
unset _TIRITH_PREEXEC_BLOCK_HISTORY_KEY
fi
if [[ "${_tirith_last_key:-}" == "$line_id" ]]; then
return "${_tirith_last_rc:-0}"
fi
_TIRITH_BASH_INTERNAL=1
if [[ $_TIRITH_RECEIPT_PROTOCOL -eq 3 \
&& "${_TIRITH_PREEXEC_RECEIPTS_TRUSTED:-0}" == "1" ]]; then
_tirith_preexec_receipt_check "$history_line" no
rc=$?
else
_TIRITH_HOOK=1 builtin command "$_TIRITH_BIN" check --shell posix -- "$history_line"
rc=$?
fi
_TIRITH_BASH_INTERNAL="$_tirith_prev_internal"
case "$rc" in
0|2)
_tirith_last_key="$line_id"
_tirith_last_rc=0
_TIRITH_PREEXEC_ACTIVE_DECISION="allow"
return 0
;;
1)
_tirith_last_key="$line_id"
_tirith_last_rc=1
_TIRITH_PREEXEC_BLOCK_HISTORY_KEY="$history_key"
_tirith_preexec_block_current_line
return $?
;;
*)
_tirith_last_key="$line_id"
_tirith_last_rc=1
_TIRITH_PREEXEC_BLOCK_HISTORY_KEY="$history_key"
_tirith_preexec_block_current_line \
"tirith: preexec enforcement failed unexpectedly (exit $rc), blocking this command and disabling enforcement for this shell"
return $?
;;
esac
fi
if [[ "${_tirith_last_key:-}" == "$line_id" ]] \
&& [[ "${_tirith_last_rc:-}" == "1" ]]; then
return 1
fi
local scan_target
if [[ "${_TIRITH_WARN_ONLY_USE_BASH_COMMAND:-0}" == "1" ]]; then
scan_target="$bash_cmd"
elif [[ -n "$history_line" ]]; then
scan_target="$history_line"
else
scan_target="$bash_cmd"
fi
local dedupe_key="${line_id}|${scan_target}"
if [[ "${_tirith_last_cmd:-}" == "$dedupe_key" ]]; then
_TIRITH_PREEXEC_ACTIVE_DECISION="allow"
return 0
fi
_tirith_last_cmd="$dedupe_key"
_TIRITH_BASH_INTERNAL=1
if [[ $_TIRITH_RECEIPT_PROTOCOL -eq 3 \
&& "${_TIRITH_PREEXEC_RECEIPTS_TRUSTED:-0}" == "1" ]]; then
if _tirith_receipt_parent_context_is_valid; then
if ! _tirith_preexec_receipt_check "$scan_target" yes; then
_TIRITH_HOOK=1 builtin command "$_TIRITH_BIN" check --shell posix --warn-only -- "$scan_target" || true
if [[ -z "${_TIRITH_RECEIPT_DEGRADE_WARNED:-}" ]]; then
_TIRITH_RECEIPT_DEGRADE_WARNED=1
_tirith_output "tirith: execution receipts unavailable; legacy checks remain active but session execution evidence is degraded"
[[ -n "${_TIRITH_RECEIPT_REGISTER_ERROR:-}" ]] \
&& _tirith_output "$_TIRITH_RECEIPT_REGISTER_ERROR"
fi
fi
else
_TIRITH_HOOK=1 builtin command "$_TIRITH_BIN" check --shell posix --warn-only -- "$scan_target" || true
fi
else
_TIRITH_HOOK=1 builtin command "$_TIRITH_BIN" check --shell posix --warn-only -- "$scan_target" || true
fi
_TIRITH_BASH_INTERNAL="$_tirith_prev_internal"
_TIRITH_PREEXEC_ACTIVE_DECISION="allow"
return 0
}
_TIRITH_PROTECTED_KEYMAPS=(emacs-standard vi-insert vi-command)
_TIRITH_SAVED_CTRL_O_KINDS=()
_TIRITH_SAVED_CTRL_O_BINDINGS=()
_tirith_bind_x_record_is_ctrl_o() {
local key="${1%%[[:space:]]*}"
key="${key%:}"
[[ "$key" == '"\C-o"' ]]
}
_tirith_bind_output_has_ctrl_o() {
local output="$1" line
while IFS= read -r line; do
_tirith_bind_x_record_is_ctrl_o "$line" && return 0
done <<< "$output"
return 1
}
_tirith_bind_x_has_exact_binding() {
local output="$1"
local key="$2"
local command="$3"
local line
while IFS= read -r line; do
if [[ "$line" == "\"${key}\" \"${command}\"" \
|| "$line" == "\"${key}\": \"${command}\"" ]]; then
return 0
fi
done <<< "$output"
return 1
}
_tirith_capture_ctrl_o_bindings() {
local map output line index
builtin bind -X >/dev/null 2>&1 || return 1
_TIRITH_SAVED_CTRL_O_KINDS=()
_TIRITH_SAVED_CTRL_O_BINDINGS=()
for ((index = 0; index < ${#_TIRITH_PROTECTED_KEYMAPS[@]}; index++)); do
map="${_TIRITH_PROTECTED_KEYMAPS[index]}"
_TIRITH_SAVED_CTRL_O_KINDS[index]="unbound"
_TIRITH_SAVED_CTRL_O_BINDINGS[index]=""
output="$(builtin bind -m "$map" -X 2>/dev/null)" || return 1
while IFS= read -r line; do
if _tirith_bind_x_record_is_ctrl_o "$line"; then
_TIRITH_SAVED_CTRL_O_KINDS[index]="bind-x"
_TIRITH_SAVED_CTRL_O_BINDINGS[index]="$line"
break
fi
done <<< "$output"
[[ "${_TIRITH_SAVED_CTRL_O_KINDS[index]}" == "bind-x" ]] && continue
output="$(builtin bind -m "$map" -s 2>/dev/null)" || return 1
while IFS= read -r line; do
if [[ "${line%%:*}" == '"\C-o"' ]]; then
_TIRITH_SAVED_CTRL_O_KINDS[index]="binding"
_TIRITH_SAVED_CTRL_O_BINDINGS[index]="$line"
break
fi
done <<< "$output"
[[ "${_TIRITH_SAVED_CTRL_O_KINDS[index]}" == "binding" ]] && continue
output="$(builtin bind -m "$map" -p 2>/dev/null)" || return 1
while IFS= read -r line; do
if [[ "${line%%:*}" == '"\C-o"' ]]; then
_TIRITH_SAVED_CTRL_O_KINDS[index]="binding"
_TIRITH_SAVED_CTRL_O_BINDINGS[index]="$line"
break
fi
done <<< "$output"
done
return 0
}
_tirith_restore_ctrl_o_bindings() {
local map kind binding index restore_rc=0
for ((index = 0; index < ${#_TIRITH_PROTECTED_KEYMAPS[@]}; index++)); do
map="${_TIRITH_PROTECTED_KEYMAPS[index]}"
kind="${_TIRITH_SAVED_CTRL_O_KINDS[index]:-unbound}"
binding="${_TIRITH_SAVED_CTRL_O_BINDINGS[index]:-}"
case "$kind" in
bind-x)
builtin bind -m "$map" -x "$binding" 2>/dev/null || restore_rc=1
;;
binding)
builtin bind -m "$map" "$binding" 2>/dev/null || restore_rc=1
;;
*)
builtin bind -m "$map" -r '\C-o' 2>/dev/null || restore_rc=1
;;
esac
done
unset _TIRITH_SAVED_CTRL_O_KINDS _TIRITH_SAVED_CTRL_O_BINDINGS
return "$restore_rc"
}
_tirith_degrade_to_preexec() {
local reason="${1:-unknown}"
if [[ "${_TIRITH_BINDS_INSTALLED:-0}" == "1" ]]; then
local _tirith_keymap
for _tirith_keymap in "${_TIRITH_PROTECTED_KEYMAPS[@]}"; do
builtin bind -m "$_tirith_keymap" '"\C-m": accept-line' 2>/dev/null || true
builtin bind -m "$_tirith_keymap" '"\C-j": accept-line' 2>/dev/null || true
builtin bind -m "$_tirith_keymap" -r "$_TIRITH_ENTER_CHECK_KEYS" 2>/dev/null || true
builtin bind -m "$_tirith_keymap" -r "$_TIRITH_ENTER_ACCEPT_KEYS" 2>/dev/null || true
builtin bind -m "$_tirith_keymap" '"\e[200~": bracketed-paste-begin' \
2>/dev/null || builtin bind -m "$_tirith_keymap" -r '\e[200~' 2>/dev/null || true
done
_tirith_restore_ctrl_o_bindings || true
_TIRITH_BINDS_INSTALLED=0
fi
_TIRITH_PREEXEC_PHASE="startup"
_TIRITH_PREEXEC_ACTIVE_DECISION=""
_TIRITH_PREEXEC_BLOCK_ACTIVE=0
_TIRITH_PREEXEC_ENFORCE_PENDING=0
if shopt -q extdebug; then
_TIRITH_PREEXEC_PHASE="off"
_TIRITH_PREEXEC_RECEIPTS_TRUSTED=0
_TIRITH_BASH_MODE="preexec"
_tirith_persist_safe_mode
export TIRITH_BASH_EFFECTIVE_MODE="preexec"
export TIRITH_BASH_EFFECTIVE_PROTECTION="off"
_tirith_set_status "degraded"
_tirith_output "tirith: enter mode failed ($reason), and the preexec fallback cannot own a user-enabled extdebug setting; interception is off for this shell"
return 1
elif ! _tirith_prepare_debug_trap_capture; then
_TIRITH_PREEXEC_PHASE="off"
_TIRITH_PREEXEC_RECEIPTS_TRUSTED=0
_TIRITH_BASH_MODE="preexec"
_tirith_persist_safe_mode
export TIRITH_BASH_EFFECTIVE_MODE="preexec"
export TIRITH_BASH_EFFECTIVE_PROTECTION="off"
_tirith_set_status "degraded"
_tirith_output "tirith: enter mode failed ($reason), and the preexec fallback could not preserve the existing DEBUG trap; interception is off for this shell"
return 1
elif _tirith_install_preexec_prompt_guards; then
_TIRITH_PREEXEC_RECEIPTS_TRUSTED=1
export TIRITH_BASH_EFFECTIVE_PROTECTION="off"
_tirith_set_status "off"
else
if [[ -n "${_TIRITH_DEBUG_CAPTURE_FILE:-}" ]]; then
_tirith_remove_capture_file "$_TIRITH_DEBUG_CAPTURE_FILE" >/dev/null 2>&1 || true
fi
_TIRITH_DEBUG_CAPTURE_FILE=""
_TIRITH_DEBUG_TRAP_CAPTURE_READY=2
_TIRITH_PREEXEC_PHASE="off"
_TIRITH_PREEXEC_PROMPT_GUARDS=0
_TIRITH_PREEXEC_RECEIPTS_TRUSTED=0
_TIRITH_BASH_MODE="preexec"
_tirith_persist_safe_mode
export TIRITH_BASH_EFFECTIVE_MODE="preexec"
export TIRITH_BASH_EFFECTIVE_PROTECTION="off"
_tirith_set_status "degraded"
_tirith_output "tirith: enter mode failed ($reason), and the preexec fallback could not safely bracket PROMPT_COMMAND; interception is off for this shell"
return 1
fi
_TIRITH_BASH_MODE="preexec"
_tirith_persist_safe_mode
if [[ $- == *i* ]]; then
export TIRITH_BASH_EFFECTIVE_MODE="preexec"
export TIRITH_BASH_EFFECTIVE_PROTECTION="warn-only"
_tirith_set_status "degraded"
fi
_tirith_warn_degraded_once \
"enter mode failed ($reason); now warn-only. Persistent — restart your shell, or re-enable with TIRITH_BASH_MODE=enter."
}
_tirith_prompt_hook() {
if declare -F _tirith_enter_disarm_accept >/dev/null 2>&1; then
if ! _tirith_enter_disarm_accept; then
local failed_pending_receipt="${_TIRITH_PENDING_RECEIPT:-}"
unset _TIRITH_PENDING_EVAL
unset _TIRITH_PENDING_RECEIPT _TIRITH_PENDING_COMMAND
if [[ -n "$failed_pending_receipt" ]]; then
_tirith_receipt_discard bash-enter "$failed_pending_receipt" || true
fi
_tirith_degrade_to_preexec "could not disarm guarded accept-line" || true
return
fi
fi
local pending_eval="${_TIRITH_PENDING_EVAL:-}"
local pending_receipt="${_TIRITH_PENDING_RECEIPT:-}"
local pending_command="${_TIRITH_PENDING_COMMAND:-}"
unset _TIRITH_PENDING_EVAL
unset _TIRITH_PENDING_RECEIPT _TIRITH_PENDING_COMMAND
if [[ -n "$pending_receipt" ]]; then
if [[ $_TIRITH_RECEIPT_PROTOCOL -ne 3 \
|| -z "$pending_eval" \
|| -z "$pending_command" \
|| "$pending_eval" != "$pending_command" ]]; then
_tirith_receipt_discard bash-enter "$pending_receipt" || true
_tirith_degrade_to_preexec "deferred command state did not match its receipt"
return 1
fi
if ! _tirith_receipt_consume bash-enter "$pending_receipt" "$pending_command"; then
if ! _tirith_receipt_reconcile bash-enter "$pending_receipt"; then
_tirith_degrade_to_preexec "execution receipt could not be committed before delivery"
return 1
fi
fi
elif [[ $_TIRITH_RECEIPT_PROTOCOL -eq 3 \
&& ( -n "$pending_eval" || -n "$pending_command" ) ]]; then
_tirith_degrade_to_preexec "deferred command state lacks its receipt commit marker"
return 1
fi
if [[ -n "$pending_eval" ]]; then
builtin eval -- "$pending_eval"
fi
}
_tirith_is_prompt_hook_attached() {
local pc_decl
pc_decl="$(declare -p PROMPT_COMMAND 2>/dev/null)" || return 1
if [[ "$pc_decl" == "declare -a"* ]]; then
local entry
for entry in "${PROMPT_COMMAND[@]}"; do
[[ "$entry" == "_tirith_prompt_hook" ]] && return 0
done
return 1
else
[[ "$PROMPT_COMMAND" =~ (^|;)[[:space:]]*_tirith_prompt_hook[[:space:]]*(;|$) ]] && return 0
return 1
fi
}
_tirith_ensure_prompt_hook() {
_tirith_is_prompt_hook_attached && return 0
local pc_decl pc_attrs
pc_decl="$(declare -p PROMPT_COMMAND 2>/dev/null)" || pc_decl=""
pc_attrs="${pc_decl#declare }"
pc_attrs="${pc_attrs%% *}"
if [[ -n "$pc_decl" ]]; then
_tirith_prompt_command_attrs_safe "$pc_attrs" || return 1
fi
if [[ "$pc_decl" == "declare -a"* ]]; then
PROMPT_COMMAND=(_tirith_prompt_hook "${PROMPT_COMMAND[@]}") 2>/dev/null || return 1
elif [[ -n "${PROMPT_COMMAND:-}" ]]; then
PROMPT_COMMAND="_tirith_prompt_hook;${PROMPT_COMMAND}" 2>/dev/null || return 1
else
PROMPT_COMMAND="_tirith_prompt_hook" 2>/dev/null || return 1
fi
_tirith_is_prompt_hook_attached
}
_TIRITH_DEBUG_TRAP_CAPTURE_READY=0
_TIRITH_CAPTURED_DEBUG_TRAP_SPEC=""
_TIRITH_DEBUG_TRAP_INSTALLED=0
_TIRITH_DEBUG_CAPTURE_FILE=""
_TIRITH_DEBUG_OWNERSHIP_FILE=""
_TIRITH_DEBUG_TRAP_OWNERSHIP_OK=0
_TIRITH_PREEXEC_ENFORCE_PENDING=0
_TIRITH_PREEXEC_BOOTSTRAP_COMMAND='if [[ "${_TIRITH_DEBUG_TRAP_CAPTURE_READY:-0}" == "0" ]]; then builtin trap -p DEBUG >|"$_TIRITH_DEBUG_CAPTURE_FILE" 2>/dev/null; _tirith_finalize_debug_trap_capture; fi; if [[ "${_TIRITH_DEBUG_TRAP_INSTALLED:-0}" == "1" ]]; then builtin trap -p DEBUG >|"$_TIRITH_DEBUG_OWNERSHIP_FILE" 2>/dev/null; _tirith_verify_debug_trap_ownership; fi; _tirith_restore_prompt_status'
if [[ -n "${TIRITH_BASH_MODE:-}" ]]; then
case "$TIRITH_BASH_MODE" in
enter|preexec) _TIRITH_BASH_MODE="$TIRITH_BASH_MODE" ;;
*)
_TIRITH_BASH_MODE="preexec"
[[ $- == *i* ]] && _tirith_output "tirith: invalid TIRITH_BASH_MODE (expected enter or preexec); using preexec"
;;
esac
elif _tirith_check_safe_mode; then
_TIRITH_BASH_MODE="preexec"
[[ $- == *i* ]] && _tirith_output "tirith: safe mode active (preexec) — previous enter-mode failure detected"
[[ $- == *i* ]] && _tirith_output " Re-enable: TIRITH_BASH_MODE=enter or tirith doctor --reset-bash-safe-mode"
elif [[ -n "${SSH_CONNECTION:-}" || -n "${SSH_TTY:-}" || -n "${SSH_CLIENT:-}" ]]; then
_TIRITH_BASH_MODE="preexec"
elif _tirith_enter_capability_proven; then
_TIRITH_BASH_MODE="enter"
else
_TIRITH_BASH_MODE="preexec"
fi
if [[ $- == *i* ]]; then
export TIRITH_BASH_EFFECTIVE_MODE="$_TIRITH_BASH_MODE"
if [[ "$_TIRITH_BASH_MODE" == "enter" ]]; then
export TIRITH_BASH_EFFECTIVE_PROTECTION="blocks"
_tirith_set_status "blocks"
else
export TIRITH_BASH_EFFECTIVE_PROTECTION="warn-only"
_tirith_set_status "warn-only"
fi
fi
_TIRITH_PREEXEC_ENFORCE=0
_TIRITH_OWNS_EXTDEBUG=0
_TIRITH_WARN_ONLY_USE_BASH_COMMAND=0
_TIRITH_PREEXEC_PROMPT_GUARDS=0
_TIRITH_PREEXEC_PHASE="off"
_TIRITH_PREEXEC_AWAITING_USER=0
_TIRITH_PREEXEC_BLOCK_ACTIVE=0
_TIRITH_PREEXEC_ACTIVE_DECISION=""
_TIRITH_PREEXEC_RECEIPTS_TRUSTED=0
_TIRITH_PREEXEC_BLOCK_HISTORY_KEY=""
_tirith_env_is_truthy() {
case "${1:-}" in
1|true|TRUE|True|yes|YES|Yes|on|ON|On) return 0 ;;
esac
return 1
}
if [[ "$_TIRITH_BASH_MODE" == "preexec" ]] && [[ $- == *i* ]]; then
_TIRITH_PREEXEC_PHASE="startup"
if shopt -q extdebug; then
_TIRITH_PREEXEC_PHASE="off"
_TIRITH_PREEXEC_WARNED=1
_TIRITH_PREEXEC_RECEIPTS_TRUSTED=0
export TIRITH_BASH_EFFECTIVE_PROTECTION="off"
_tirith_set_status "degraded"
_tirith_output "tirith: bash preexec hook was not installed because extdebug is already user-enabled; existing debugger state was left unchanged"
elif ! _tirith_prepare_debug_trap_capture; then
_TIRITH_PREEXEC_PHASE="off"
_TIRITH_PREEXEC_WARNED=1
_TIRITH_PREEXEC_RECEIPTS_TRUSTED=0
export TIRITH_BASH_EFFECTIVE_PROTECTION="off"
_tirith_set_status "degraded"
_tirith_output "tirith: bash preexec hook was not installed because the existing DEBUG trap could not be preserved safely; existing debugger state was left unchanged"
elif _tirith_install_preexec_prompt_guards; then
_TIRITH_PREEXEC_RECEIPTS_TRUSTED=1
export TIRITH_BASH_EFFECTIVE_PROTECTION="off"
_tirith_set_status "off"
else
if [[ -n "${_TIRITH_DEBUG_CAPTURE_FILE:-}" ]]; then
_tirith_remove_capture_file "$_TIRITH_DEBUG_CAPTURE_FILE" >/dev/null 2>&1 || true
fi
_TIRITH_DEBUG_CAPTURE_FILE=""
_TIRITH_DEBUG_TRAP_CAPTURE_READY=2
_TIRITH_PREEXEC_PHASE="off"
_TIRITH_PREEXEC_PROMPT_GUARDS=0
_TIRITH_PREEXEC_WARNED=1
_TIRITH_PREEXEC_RECEIPTS_TRUSTED=0
export TIRITH_BASH_EFFECTIVE_PROTECTION="off"
_tirith_set_status "degraded"
_tirith_output "tirith: bash preexec hook was not installed because PROMPT_COMMAND is readonly, associative, coercing, unsupported, or otherwise cannot be safely bracketed; existing prompt and DEBUG state was left unchanged"
fi
fi
if [[ "$_TIRITH_BASH_MODE" == "preexec" ]] \
&& [[ $- == *i* ]] \
&& _tirith_env_is_truthy "${TIRITH_BASH_PREEXEC_ENFORCE:-}"; then
if [[ "${_TIRITH_PREEXEC_PHASE:-}" == "off" ]]; then
_TIRITH_PREEXEC_WARNED=1
_tirith_set_status "degraded"
elif [[ "${_TIRITH_PREEXEC_PROMPT_GUARDS:-0}" != "1" ]]; then
_TIRITH_PREEXEC_WARNED=1
_tirith_set_status "degraded"
_tirith_warn_degraded_once \
"preexec enforcement could not engage because PROMPT_COMMAND is readonly, associative, or otherwise cannot be safely bracketed. For guaranteed blocking, use enter mode (export TIRITH_BASH_MODE=enter)."
elif shopt -q extdebug; then
_TIRITH_PREEXEC_WARNED=1
_tirith_set_status "degraded"
_tirith_warn_degraded_once \
"preexec enforcement could not engage because extdebug was already enabled outside Tirith; user debugger state was preserved. Disable extdebug or use enter mode for blocking."
elif _tirith_history_is_trustworthy_for_enforcement; then
_TIRITH_PREEXEC_ENFORCE_PENDING=1
else
_TIRITH_WARN_ONLY_USE_BASH_COMMAND=1
_TIRITH_PREEXEC_RECEIPTS_TRUSTED=0
_tirith_set_status "degraded"
_TIRITH_PREEXEC_WARNED=1
_tirith_warn_degraded_once \
"preexec enforcement could not engage (HISTCONTROL/HISTIGNORE or disabled history prevents a trustworthy whole-line view). For guaranteed blocking, use enter mode (export TIRITH_BASH_MODE=enter)."
fi
fi
if [[ $- == *i* ]] && [[ $_TIRITH_RECEIPT_PROTOCOL -ne 3 ]]; then
if [[ -z "${_TIRITH_RECEIPT_DEGRADE_WARNED:-}" ]]; then
_TIRITH_RECEIPT_DEGRADE_WARNED=1
_tirith_output "tirith: execution receipts unavailable; legacy checks remain active but session execution evidence is degraded"
[[ -n "${_TIRITH_RECEIPT_REGISTER_ERROR:-}" ]] \
&& _tirith_output "$_TIRITH_RECEIPT_REGISTER_ERROR"
fi
fi
_tirith_unsafe_to_eval() {
local cmd="$1"
if [[ "$cmd" == *$'\n'* ]]; then
return 0
fi
if [[ "$cmd" == *'\' ]]; then
return 0
fi
if [[ "$cmd" == *'<<'* ]]; then
return 0
fi
local keywords='(^|[;&| ])(\{|\}|function |case |select |for |while |until |coproc )'
if [[ "$cmd" =~ $keywords ]]; then
return 0
fi
if [[ "$cmd" == *'; do'* ]] || [[ "$cmd" == *'; then'* ]]; then
return 0
fi
if [[ "$cmd" == *'( '* ]] || [[ "$cmd" == *' )'* ]]; then
return 0
fi
return 1
}
_tirith_queue_enter_delivery() {
local cmd="$1" receipt_token="$2"
_TIRITH_PENDING_EVAL="$cmd"
_TIRITH_PENDING_COMMAND="$cmd"
if [[ $_TIRITH_RECEIPT_PROTOCOL -eq 3 ]]; then
_TIRITH_PENDING_RECEIPT="$receipt_token"
fi
if ! _tirith_enter_arm_accept; then
unset _TIRITH_PENDING_EVAL _TIRITH_PENDING_COMMAND _TIRITH_PENDING_RECEIPT
_tirith_receipt_discard bash-enter "$receipt_token" || true
READLINE_LINE="$cmd"
READLINE_POINT=${#cmd}
_tirith_degrade_to_preexec "could not arm guarded accept-line" || true
return 1
fi
history -s -- "$cmd"
return 0
}
_tirith_startup_health_check() {
[[ "${_TIRITH_TEST_SKIP_HEALTH:-}" == "1" ]] && return 0
[[ "${_TIRITH_TEST_FAIL_HEALTH:-}" == "1" ]] && return 1
local map xbinds sbinds pbinds
local macro="$_TIRITH_ENTER_CHECK_KEYS$_TIRITH_ENTER_ACCEPT_KEYS"
local cm_needle="\"\\C-m\": \"$macro\""
local cj_needle="\"\\C-j\": \"$macro\""
for map in "${_TIRITH_PROTECTED_KEYMAPS[@]}"; do
xbinds="$(builtin bind -m "$map" -X 2>/dev/null)" || return 1
_tirith_bind_x_has_exact_binding \
"$xbinds" "$_TIRITH_ENTER_CHECK_KEYS" "_tirith_enter" || return 1
_tirith_bind_x_has_exact_binding \
"$xbinds" "$_TIRITH_ENTER_ACCEPT_KEYS" "_tirith_enter_accept_noop" || return 1
_tirith_bind_output_has_ctrl_o "$xbinds" && return 1
sbinds="$(builtin bind -m "$map" -s 2>/dev/null)" || return 1
[[ "$sbinds" == *"$cm_needle"* ]] || return 1
[[ "$sbinds" == *"$cj_needle"* ]] || return 1
_tirith_bind_output_has_ctrl_o "$sbinds" && return 1
pbinds="$(builtin bind -m "$map" -p 2>/dev/null)" || return 1
_tirith_bind_output_has_ctrl_o "$pbinds" && return 1
done
_tirith_is_prompt_hook_attached || return 1
return 0
}
if [[ "$_TIRITH_BASH_MODE" == "enter" ]] && [[ $- == *i* ]]; then
_TIRITH_BINDS_INSTALLED=0
if ! _tirith_ensure_prompt_hook; then
_tirith_degrade_to_preexec "PROMPT_COMMAND is readonly or unattachable"
else
_tirith_enter() {
local _saved_stty
if [[ -n "$_TIRITH_STTY_BIN" ]]; then
_saved_stty="$(builtin command "$_TIRITH_STTY_BIN" -g 2>/dev/null)" || _saved_stty=""
fi
builtin trap '_tirith_restore_terminal_state "$_saved_stty"' RETURN
if ! _tirith_ensure_prompt_hook; then
_tirith_degrade_to_preexec "PROMPT_COMMAND reattachment failed"
return fi
if [[ -n "${_TIRITH_PENDING_EVAL:-}" \
|| -n "${_TIRITH_PENDING_COMMAND:-}" \
|| -n "${_TIRITH_PENDING_RECEIPT:-}" ]]; then
_tirith_receipt_discard bash-enter "${_TIRITH_PENDING_RECEIPT:-}"
unset _TIRITH_PENDING_EVAL
unset _TIRITH_PENDING_RECEIPT _TIRITH_PENDING_COMMAND
_tirith_degrade_to_preexec "previous command not delivered (check shell history)"
return fi
if [[ -z "$READLINE_LINE" ]]; then
READLINE_LINE=""
READLINE_POINT=0
if ! _tirith_enter_arm_accept; then
_tirith_degrade_to_preexec "could not arm guarded accept-line" || true
fi
return
fi
local syntax_err syntax_rc
_tirith_check_command_syntax "$READLINE_LINE"
syntax_err="$_TIRITH_SYNTAX_ERROR"
syntax_rc="$_TIRITH_SYNTAX_RC"
unset _TIRITH_SYNTAX_ERROR _TIRITH_SYNTAX_RC
if [[ $syntax_rc -ne 0 ]] && [[ "$syntax_err" == *"unexpected EOF"* || "$syntax_err" == *"unexpected end of file"* ]]; then
READLINE_LINE+=$'\n'
READLINE_POINT=${#READLINE_LINE}
return
fi
local errfile stdout_file rc
errfile="$(_tirith_new_capture_file 2>/dev/null)" || errfile=""
stdout_file="$(_tirith_new_capture_file 2>/dev/null)" || stdout_file=""
if [[ -z "$errfile" || -z "$stdout_file" ]]; then
[[ -n "$errfile" ]] && _tirith_remove_capture_file "$errfile" >/dev/null 2>&1
[[ -n "$stdout_file" ]] && _tirith_remove_capture_file "$stdout_file" >/dev/null 2>&1
_tirith_degrade_to_preexec "could not create private receipt capture files"
return
fi
local approval_path="" warn_ack_path="" receipt_token=""
local _tirith_prev_internal="${_TIRITH_BASH_INTERNAL:-0}"
_TIRITH_BASH_INTERNAL=1
local -a receipt_args
receipt_args=()
[[ $_TIRITH_RECEIPT_PROTOCOL -eq 3 ]] && receipt_args=(--execution-receipt bash-enter)
_TIRITH_HOOK=1 _TIRITH_RECEIPT_INSTANCE="$_TIRITH_RECEIPT_INSTANCE" \
_TIRITH_RECEIPT_SHELL_PID="$_TIRITH_RECEIPT_SHELL_PID" \
_TIRITH_RECEIPT_FAMILY="$_TIRITH_RECEIPT_FAMILY" \
builtin command "$_TIRITH_BIN" check --approval-check --non-interactive --interactive --shell posix \
"${receipt_args[@]}" -- "$READLINE_LINE" >|"$stdout_file" 2>|"$errfile"
rc=$?
_TIRITH_BASH_INTERNAL="$_tirith_prev_internal"
local output
output=$(<"$errfile")
_tirith_remove_capture_file "$errfile" >/dev/null 2>&1
local receipt_lines=0 path_lines=0 malformed_stdout=0 line
if [[ $_TIRITH_RECEIPT_PROTOCOL -eq 3 ]]; then
local protocol_parse_rc
_tirith_parse_v3_receipt_response "$stdout_file" "$rc"
protocol_parse_rc=$?
receipt_token="$_TIRITH_PARSED_RECEIPT"
_tirith_remove_capture_file "$stdout_file" >/dev/null 2>&1 || protocol_parse_rc=2
unset _TIRITH_CAPTURE_LINE _TIRITH_PARSED_RECEIPT
case "$protocol_parse_rc" in
0)
if [[ $rc -eq 2 ]]; then
local escaped_line
escaped_line=$(_tirith_escape_preview "$READLINE_LINE")
_tirith_output ""
_tirith_output "command> $escaped_line"
[[ -n "$output" ]] && _tirith_output "$output"
fi
;;
1)
local escaped_line
escaped_line=$(_tirith_escape_preview "$READLINE_LINE")
_tirith_output ""
_tirith_output "command> $escaped_line"
[[ -n "$output" ]] && _tirith_output "$output"
READLINE_LINE=""
READLINE_POINT=0
return
;;
*)
local escaped_line
escaped_line=$(_tirith_escape_preview "$READLINE_LINE")
_tirith_output ""
_tirith_output "command> $escaped_line"
[[ -n "$output" ]] && _tirith_output "$output"
_tirith_receipt_discard bash-enter "$receipt_token"
_tirith_degrade_to_preexec "invalid protocol-v3 execution-receipt response (exit $rc)"
return
;;
esac
else
while IFS= read -r line || [[ -n "$line" ]]; do
if [[ "$line" == TIRITH_EXECUTION_RECEIPT=* ]]; then
receipt_lines=$((receipt_lines + 1))
receipt_token="${line#TIRITH_EXECUTION_RECEIPT=}"
elif [[ -n "$line" ]]; then
path_lines=$((path_lines + 1))
if [[ $path_lines -eq 1 ]]; then
approval_path="$line"
elif [[ $path_lines -eq 2 && $rc -eq 3 ]]; then
warn_ack_path="$line"
else
malformed_stdout=1
fi
fi
done < "$stdout_file"
_tirith_remove_capture_file "$stdout_file" >/dev/null 2>&1 || malformed_stdout=1
if [[ $malformed_stdout -ne 0 ]]; then
[[ -n "$approval_path" ]] && _tirith_remove_capture_file "$approval_path" >/dev/null 2>&1
[[ -n "$warn_ack_path" ]] && _tirith_remove_capture_file "$warn_ack_path" >/dev/null 2>&1
_tirith_degrade_to_preexec "invalid legacy check response"
return
fi
fi
local approval_outcome="" warn_acknowledged="no"
if [[ $_TIRITH_RECEIPT_PROTOCOL -ne 3 ]]; then
if [[ $rc -eq 0 ]]; then
: elif [[ $rc -eq 2 || $rc -eq 3 ]]; then
local escaped_line
escaped_line=$(_tirith_escape_preview "$READLINE_LINE")
_tirith_output ""
_tirith_output "command> $escaped_line"
[[ -n "$output" ]] && _tirith_output "$output"
elif [[ $rc -eq 1 ]]; then
local escaped_line
escaped_line=$(_tirith_escape_preview "$READLINE_LINE")
_tirith_output ""
_tirith_output "command> $escaped_line"
[[ -n "$output" ]] && _tirith_output "$output"
else
local escaped_line
escaped_line=$(_tirith_escape_preview "$READLINE_LINE")
_tirith_output ""
_tirith_output "command> $escaped_line"
[[ -n "$output" ]] && _tirith_output "$output"
[[ -n "$approval_path" ]] && _tirith_remove_capture_file "$approval_path" >/dev/null 2>&1
[[ -n "$warn_ack_path" ]] && _tirith_remove_capture_file "$warn_ack_path" >/dev/null 2>&1
_tirith_receipt_discard bash-enter "$receipt_token"
_tirith_degrade_to_preexec "tirith returned unexpected exit code $rc"
return fi
if [[ -n "$approval_path" ]]; then
_tirith_parse_approval "$approval_path"
if [[ "$_tirith_ap_required" == "yes" ]]; then
_tirith_output "tirith: approval required for $_tirith_ap_rule"
[[ -n "$_tirith_ap_desc" ]] && _tirith_output " $_tirith_ap_desc"
local response=""
local approval_read_rc=0
if [[ "$_tirith_ap_timeout" -gt 0 ]]; then
read -t "$_tirith_ap_timeout" -p "Approve? (${_tirith_ap_timeout}s timeout) [y/N] " response </dev/tty 2>/dev/null || approval_read_rc=$?
else
read -p "Approve? [y/N] " response </dev/tty 2>/dev/null || approval_read_rc=$?
fi
if [[ "$response" == [yY]* ]]; then
approval_outcome="granted"
else
if [[ "$_tirith_ap_timeout" -gt 0 && $approval_read_rc -ne 0 ]]; then
approval_outcome="timed-out"
else
approval_outcome="rejected"
fi
case "$_tirith_ap_fallback" in
allow)
_tirith_output "tirith: approval not granted — fallback: allow"
;;
warn)
_tirith_output "tirith: approval not granted — fallback: warn"
;;
*)
_tirith_output "tirith: approval not granted — fallback: block"
[[ -n "$warn_ack_path" ]] && _tirith_remove_capture_file "$warn_ack_path" >/dev/null 2>&1
_tirith_receipt_discard bash-enter "$receipt_token"
READLINE_LINE=""
READLINE_POINT=0
return
;;
esac
fi
elif [[ $rc -eq 1 ]]; then
[[ -n "$warn_ack_path" ]] && _tirith_remove_capture_file "$warn_ack_path" >/dev/null 2>&1
_tirith_receipt_discard bash-enter "$receipt_token"
READLINE_LINE=""
READLINE_POINT=0
return
fi
elif [[ $rc -eq 1 ]]; then
_tirith_receipt_discard bash-enter "$receipt_token"
READLINE_LINE=""
READLINE_POINT=0
return
fi
if [[ $rc -eq 3 && -n "$warn_ack_path" ]]; then
if ! _tirith_parse_warn_ack "$warn_ack_path"; then
_tirith_receipt_discard bash-enter "$receipt_token"
_tirith_output "tirith: warning acknowledgement metadata is invalid; command blocked"
READLINE_LINE=""
READLINE_POINT=0
return
fi
local response=""
read -p "tirith: proceed with ${_tirith_wa_findings} warning(s)? [y/N] " response </dev/tty 2>/dev/null
if [[ "$response" == [yY]* ]]; then
warn_acknowledged="yes"
else
_tirith_output "tirith: warnings not acknowledged — command blocked"
_tirith_receipt_discard bash-enter "$receipt_token"
READLINE_LINE=""
READLINE_POINT=0
return
fi
elif [[ -n "$warn_ack_path" ]]; then
_tirith_remove_capture_file "$warn_ack_path" >/dev/null 2>&1
fi
fi
local cmd="$READLINE_LINE"
READLINE_LINE=""
READLINE_POINT=0
if _tirith_unsafe_to_eval "$cmd"; then
_tirith_queue_enter_delivery "$cmd" "$receipt_token"
return $?
fi
_tirith_queue_enter_delivery "$cmd" "$receipt_token"
return $?
}
_tirith_paste() {
local _saved_stty
if [[ -n "$_TIRITH_STTY_BIN" ]]; then
_saved_stty="$(builtin command "$_TIRITH_STTY_BIN" -g 2>/dev/null)" || _saved_stty=""
fi
builtin trap '_tirith_restore_terminal_state "$_saved_stty"' RETURN
local pasted=""
local char
while IFS= read -r -n 1 -d '' -t 1 char; do
pasted+="$char"
if [[ "$pasted" == *$'\e[201~' ]]; then
pasted="${pasted%$'\e[201~'}"
break
fi
done
if [[ -n "$pasted" ]]; then
local tmpfile
tmpfile="$(_tirith_new_capture_file 2>/dev/null)" || {
_tirith_output "tirith: paste check failed (could not create private output capture)"
return
}
local _tirith_prev_internal="${_TIRITH_BASH_INTERNAL:-0}"
_TIRITH_BASH_INTERNAL=1
printf '%s' "$pasted" | builtin command "$_TIRITH_BIN" paste --shell posix --interactive >|"$tmpfile" 2>&1
local rc=$?
_TIRITH_BASH_INTERNAL="$_tirith_prev_internal"
local output=$(<"$tmpfile")
_tirith_remove_capture_file "$tmpfile" >/dev/null 2>&1
if [[ $rc -eq 0 ]]; then
:
elif [[ $rc -eq 2 ]]; then
[[ -n "$output" ]] && { _tirith_output ""; _tirith_output "$output"; }
else
local escaped_paste
escaped_paste=$(_tirith_escape_preview "$pasted")
_tirith_output ""
_tirith_output "paste> $escaped_paste"
[[ -n "$output" ]] && _tirith_output "$output"
[[ $rc -ne 1 ]] && _tirith_output "tirith: paste check failed (exit code $rc)"
return
fi
fi
READLINE_LINE="${READLINE_LINE:0:$READLINE_POINT}${pasted}${READLINE_LINE:$READLINE_POINT}"
READLINE_POINT=$((READLINE_POINT + ${#pasted}))
}
_TIRITH_ENTER_CHECK_KEYS='\C-x\C-t7'
_TIRITH_ENTER_ACCEPT_KEYS='\C-x\C-r7'
_tirith_enter_accept_noop() { :; }
_tirith_enter_arm_accept() {
local _tirith_keymap _tirith_bind_rc=0
for _tirith_keymap in "${_TIRITH_PROTECTED_KEYMAPS[@]}"; do
builtin bind -m "$_tirith_keymap" \
"\"$_TIRITH_ENTER_ACCEPT_KEYS\": accept-line" 2>/dev/null \
|| _tirith_bind_rc=1
done
return "$_tirith_bind_rc"
}
_tirith_enter_disarm_accept() {
local _tirith_keymap _tirith_bind_rc=0
for _tirith_keymap in "${_TIRITH_PROTECTED_KEYMAPS[@]}"; do
builtin bind -m "$_tirith_keymap" -x \
"\"$_TIRITH_ENTER_ACCEPT_KEYS\": _tirith_enter_accept_noop" 2>/dev/null \
|| _tirith_bind_rc=1
done
return "$_tirith_bind_rc"
}
if ! _tirith_capture_ctrl_o_bindings; then
_tirith_degrade_to_preexec "could not preserve existing Ctrl-O bindings"
else
_tirith_bind_install_ok=1
for _tirith_keymap in "${_TIRITH_PROTECTED_KEYMAPS[@]}"; do
builtin bind -m "$_tirith_keymap" -x \
"\"$_TIRITH_ENTER_CHECK_KEYS\": _tirith_enter" || _tirith_bind_install_ok=0
builtin bind -m "$_tirith_keymap" \
"\"\C-m\": \"$_TIRITH_ENTER_CHECK_KEYS$_TIRITH_ENTER_ACCEPT_KEYS\"" \
|| _tirith_bind_install_ok=0
builtin bind -m "$_tirith_keymap" \
"\"\C-j\": \"$_TIRITH_ENTER_CHECK_KEYS$_TIRITH_ENTER_ACCEPT_KEYS\"" \
|| _tirith_bind_install_ok=0
builtin bind -m "$_tirith_keymap" -x '"\e[200~": _tirith_paste' \
|| _tirith_bind_install_ok=0
builtin bind -m "$_tirith_keymap" -r '\C-o' 2>/dev/null \
|| _tirith_bind_install_ok=0
done
_tirith_enter_disarm_accept || _tirith_bind_install_ok=0
_TIRITH_BINDS_INSTALLED=1
if [[ "$_tirith_bind_install_ok" != "1" ]] || ! _tirith_startup_health_check; then
_tirith_degrade_to_preexec "startup health check failed (enter macro or PROMPT_COMMAND)"
fi
unset _tirith_keymap _tirith_bind_install_ok
fi
fi
fi
_tirith_exit_summary() {
_tirith_discard_pending_debug_trap_capture
if _tirith_capture_file_is_private "${_TIRITH_DEBUG_OWNERSHIP_FILE:-}"; then
_tirith_remove_capture_file "$_TIRITH_DEBUG_OWNERSHIP_FILE" >/dev/null 2>&1 || true
fi
_TIRITH_DEBUG_OWNERSHIP_FILE=""
local pending_receipt="${_TIRITH_PENDING_RECEIPT:-}"
unset _TIRITH_PENDING_EVAL
unset _TIRITH_PENDING_RECEIPT _TIRITH_PENDING_COMMAND
[[ -n "$pending_receipt" ]] && _tirith_receipt_discard bash-enter "$pending_receipt"
[[ -n "${TIRITH_SESSION_ID:-}" ]] || return
local _sd="${XDG_STATE_HOME:-$HOME/.local/state}/tirith"
[[ -f "$_sd/sessions/$TIRITH_SESSION_ID.json" ]] || return
builtin command "$_TIRITH_BIN" warnings --summary
}
_tirith_exit_trampoline() {
if [[ -n "${_TIRITH_PREV_EXIT_TRAP:-}" ]]; then
builtin eval -- "$_TIRITH_PREV_EXIT_TRAP" || true
fi
_tirith_exit_summary
}
_tirith_prev_exit_spec="$(builtin trap -p EXIT 2>/dev/null)"
_TIRITH_PREV_EXIT_TRAP=""
if _tirith_extract_trap_body "$_tirith_prev_exit_spec" EXIT; then
_TIRITH_PREV_EXIT_TRAP="$_TIRITH_EXTRACTED_TRAP"
fi
if [[ -n "$_TIRITH_PREV_EXIT_TRAP" ]]; then
builtin trap '_tirith_exit_trampoline' EXIT
else
builtin trap '_tirith_exit_summary' EXIT
fi
unset _tirith_prev_exit_spec _TIRITH_EXTRACTED_TRAP
else
TIRITH_STATUS=off
TIRITH_BASH_EFFECTIVE_MODE=off
TIRITH_BASH_EFFECTIVE_PROTECTION=off
if [[ $- == *i* ]]; then
if [[ -x /usr/bin/printf ]]; then
/usr/bin/printf '%s\n' 'tirith: bash hooks disabled because trusted builtin lookup could not be established' >&2
elif [[ -x /bin/printf ]]; then
/bin/printf '%s\n' 'tirith: bash hooks disabled because trusted builtin lookup could not be established' >&2
fi
fi
fi