From fef44f00154be89f871a94f1d01ba5db9622c0ec Mon Sep 17 00:00:00 2001 From: Krzysztof Rudnicki Date: Sat, 1 Nov 2025 15:36:22 +0100 Subject: [PATCH] fix: shellcheck issues --- hosts/guard/enforce-hosts.sh | 20 +-- hosts/guard/install_pacman_hooks.sh | 6 +- .../pacman-hooks/pacman-post-relock-hosts.sh | 34 ++-- .../pacman-hooks/pacman-pre-unlock-hosts.sh | 39 +++-- hosts/guard/psychological/unlock-hosts.sh | 42 ++--- hosts/guard/setup_hosts_guard.sh | 154 ++++++++++++------ 6 files changed, 178 insertions(+), 117 deletions(-) diff --git a/hosts/guard/enforce-hosts.sh b/hosts/guard/enforce-hosts.sh index ba4e4a0..2eba90a 100755 --- a/hosts/guard/enforce-hosts.sh +++ b/hosts/guard/enforce-hosts.sh @@ -9,24 +9,24 @@ TARGET="/etc/hosts" LOG_FILE="/var/log/hosts-guard.log" log() { - printf '%s - %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$*" | tee -a "$LOG_FILE" >&2 + printf '%s - %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$*" | tee -a "$LOG_FILE" >&2 } -if [[ ! -f "$CANONICAL_SOURCE" ]]; then - log "Canonical hosts not found at $CANONICAL_SOURCE; aborting enforcement" - exit 0 +if [[ ! -f $CANONICAL_SOURCE ]]; then + log "Canonical hosts not found at $CANONICAL_SOURCE; aborting enforcement" + exit 0 fi if ! cmp -s "$CANONICAL_SOURCE" "$TARGET"; then - log "Difference detected – restoring $TARGET from canonical copy" - cp "$CANONICAL_SOURCE" "$TARGET" - chmod 644 "$TARGET" + log "Difference detected – restoring $TARGET from canonical copy" + cp "$CANONICAL_SOURCE" "$TARGET" + chmod 644 "$TARGET" else - log "No drift detected (contents identical)" + log "No drift detected (contents identical)" fi # Re-apply protective attributes: immutable first, then read-only bind mount handled by separate unit -chattr -i -a "$TARGET" 2>/dev/null || true +chattr -i -a "$TARGET" 2> /dev/null || true chattr +i "$TARGET" || log "Failed to set immutable attribute" -log "Enforcement complete" \ No newline at end of file +log "Enforcement complete" diff --git a/hosts/guard/install_pacman_hooks.sh b/hosts/guard/install_pacman_hooks.sh index c4b63c5..4a1169c 100755 --- a/hosts/guard/install_pacman_hooks.sh +++ b/hosts/guard/install_pacman_hooks.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash set -euo pipefail -require_root() { if [[ $EUID -ne 0 ]]; then exec sudo -E bash "$0" "$@"; fi } +require_root() { if [[ $EUID -ne 0 ]]; then exec sudo -E bash "$0" "$@"; fi; } require_root "$@" SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" @@ -10,7 +10,7 @@ HOOKS_DIR="/etc/pacman.d/hooks" install -d -m 755 "$HOOKS_DIR" # Pre-transaction hook -cat >"$HOOKS_DIR/10-unlock-etc-hosts.hook" <<'HOOK' +cat > "$HOOKS_DIR/10-unlock-etc-hosts.hook" << 'HOOK' [Trigger] Operation = Upgrade Operation = Install @@ -26,7 +26,7 @@ NeedsTargets HOOK # Post-transaction hook -cat >"$HOOKS_DIR/90-relock-etc-hosts.hook" <<'HOOK' +cat > "$HOOKS_DIR/90-relock-etc-hosts.hook" << 'HOOK' [Trigger] Operation = Upgrade Operation = Install diff --git a/hosts/guard/pacman-hooks/pacman-post-relock-hosts.sh b/hosts/guard/pacman-hooks/pacman-post-relock-hosts.sh index 450f4e7..e2f2a8b 100644 --- a/hosts/guard/pacman-hooks/pacman-post-relock-hosts.sh +++ b/hosts/guard/pacman-hooks/pacman-post-relock-hosts.sh @@ -5,22 +5,22 @@ TARGET=/etc/hosts ENFORCE=/usr/local/sbin/enforce-hosts.sh LOGTAG=hosts-guard-hook -mount_layers_count() { awk '$5=="/etc/hosts"{c++} END{print c+0}' /proc/self/mountinfo 2>/dev/null || echo 0; } +mount_layers_count() { awk '$5=="/etc/hosts"{c++} END{print c+0}' /proc/self/mountinfo 2> /dev/null || echo 0; } collapse_mounts() { local i=0 - if command -v mountpoint >/devnull 2>&1; then + if command -v mountpoint > /devnull 2>&1; then while mountpoint -q "$TARGET"; do - umount -l "$TARGET" >/dev/null 2>&1 || break - i=$((i+1)) - (( i > 20 )) && break + umount -l "$TARGET" > /dev/null 2>&1 || break + i=$((i + 1)) + ((i > 20)) && break done else local cnt cnt=$(mount_layers_count) - while (( cnt > 1 )); do - umount -l "$TARGET" >/dev/null 2>&1 || break - i=$((i+1)) - (( i > 20 )) && break + while ((cnt > 1)); do + umount -l "$TARGET" > /dev/null 2>&1 || break + i=$((i + 1)) + ((i > 20)) && break cnt=$(mount_layers_count) done fi @@ -28,23 +28,23 @@ collapse_mounts() { # Ensure we end with a single read-only bind mount layer logger -t "$LOGTAG" "post: relocking /etc/hosts (starting)" -echo "$(date -Is) post-relock(start)" >> /run/hosts-guard-hook.log 2>/dev/null || true +echo "$(date -Is) post-relock(start)" >> /run/hosts-guard-hook.log 2> /dev/null || true collapse_mounts -if [[ -x "$ENFORCE" ]]; then - "$ENFORCE" >/dev/null 2>&1 || true +if [[ -x $ENFORCE ]]; then + "$ENFORCE" > /dev/null 2>&1 || true fi # Apply exactly one ro bind layer -mount --bind "$TARGET" "$TARGET" >/dev/null 2>&1 || true -mount -o remount,ro,bind "$TARGET" >/dev/null 2>&1 || true +mount --bind "$TARGET" "$TARGET" > /dev/null 2>&1 || true +mount -o remount,ro,bind "$TARGET" > /dev/null 2>&1 || true # Start only the path watcher; avoid bind-mount service (we already bound once) -if command -v systemctl >/dev/null 2>&1; then - systemctl start hosts-guard.path >/dev/null 2>&1 || true +if command -v systemctl > /dev/null 2>&1; then + systemctl start hosts-guard.path > /dev/null 2>&1 || true fi logger -t "$LOGTAG" "post: relocking /etc/hosts (done)" -echo "$(date -Is) post-relock(done)" >> /run/hosts-guard-hook.log 2>/dev/null || true +echo "$(date -Is) post-relock(done)" >> /run/hosts-guard-hook.log 2> /dev/null || true exit 0 diff --git a/hosts/guard/pacman-hooks/pacman-pre-unlock-hosts.sh b/hosts/guard/pacman-hooks/pacman-pre-unlock-hosts.sh index e44866a..8edd96b 100644 --- a/hosts/guard/pacman-hooks/pacman-pre-unlock-hosts.sh +++ b/hosts/guard/pacman-hooks/pacman-pre-unlock-hosts.sh @@ -7,58 +7,57 @@ LOGTAG=hosts-guard-hook stop_units_if_present() { local units=(hosts-bind-mount.service hosts-guard.path) for u in "${units[@]}"; do - if command -v systemctl >/dev/null 2>&1; then - if systemctl list-unit-files 2>/dev/null | grep -q "^$u"; then - systemctl stop "$u" >/dev/null 2>&1 || true + if command -v systemctl > /dev/null 2>&1; then + if systemctl list-unit-files 2> /dev/null | grep -q "^$u"; then + systemctl stop "$u" > /dev/null 2>&1 || true fi fi done } -is_ro_mount() { findmnt -no OPTIONS -T "$TARGET" 2>/dev/null | grep -qw ro; } -is_bind_mount() { findmnt -no OPTIONS -T "$TARGET" 2>/dev/null | grep -qw bind; } +is_ro_mount() { findmnt -no OPTIONS -T "$TARGET" 2> /dev/null | grep -qw ro; } -mount_layers_count() { awk '$5=="/etc/hosts"{c++} END{print c+0}' /proc/self/mountinfo 2>/dev/null || echo 0; } +mount_layers_count() { awk '$5=="/etc/hosts"{c++} END{print c+0}' /proc/self/mountinfo 2> /dev/null || echo 0; } cleanup_mount_stacks() { local i=0 # Unmount bind layers until /etc/hosts is no longer a mountpoint - if command -v mountpoint >/dev/null 2>&1; then + if command -v mountpoint > /dev/null 2>&1; then while mountpoint -q "$TARGET"; do - umount -l "$TARGET" >/dev/null 2>&1 || break - i=$((i+1)) - (( i > 20 )) && break + umount -l "$TARGET" > /dev/null 2>&1 || break + i=$((i + 1)) + ((i > 20)) && break done else # Fallback to best-effort using mountinfo count local cnt cnt=$(mount_layers_count) - while (( cnt > 1 )); do - umount -l "$TARGET" >/dev/null 2>&1 || break - i=$((i+1)) - (( i > 20 )) && break + while ((cnt > 1)); do + umount -l "$TARGET" > /dev/null 2>&1 || break + i=$((i + 1)) + ((i > 20)) && break cnt=$(mount_layers_count) done fi } # Drop protective attributes if present -if command -v lsattr >/dev/null 2>&1; then - attrs=$(lsattr -d "$TARGET" 2>/dev/null || true) - echo "$attrs" | grep -q " i " && chattr -i "$TARGET" >/dev/null 2>&1 || true - echo "$attrs" | grep -q " a " && chattr -a "$TARGET" >/dev/null 2>&1 || true +if command -v lsattr > /dev/null 2>&1; then + attrs=$(lsattr -d "$TARGET" 2> /dev/null || true) + echo "$attrs" | grep -q " i " && chattr -i "$TARGET" > /dev/null 2>&1 || true + echo "$attrs" | grep -q " a " && chattr -a "$TARGET" > /dev/null 2>&1 || true fi stop_units_if_present logger -t "$LOGTAG" "pre: unlocking /etc/hosts (starting)" -echo "$(date -Is) pre-unlock" >> /run/hosts-guard-hook.log 2>/dev/null || true +echo "$(date -Is) pre-unlock" >> /run/hosts-guard-hook.log 2> /dev/null || true # Always collapse any existing layers; we'll operate on the plain file cleanup_mount_stacks # If someone managed a ro single-layer mount, ensure rw by remounting or collapsing again if is_ro_mount; then - mount -o remount,rw "$TARGET" >/dev/null 2>&1 || cleanup_mount_stacks + mount -o remount,rw "$TARGET" > /dev/null 2>&1 || cleanup_mount_stacks fi logger -t "$LOGTAG" "pre: unlocking /etc/hosts (done)" diff --git a/hosts/guard/psychological/unlock-hosts.sh b/hosts/guard/psychological/unlock-hosts.sh index 4b82faf..959ae0e 100644 --- a/hosts/guard/psychological/unlock-hosts.sh +++ b/hosts/guard/psychological/unlock-hosts.sh @@ -11,36 +11,36 @@ DELAY_SECONDS=45 log() { printf '%s - %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$*" | tee -a "$LOG" >&2; } -require_root() { if [[ $EUID -ne 0 ]]; then exec sudo -E bash "$0" "$@"; fi } +require_root() { if [[ $EUID -ne 0 ]]; then exec sudo -E bash "$0" "$@"; fi; } require_root "$@" echo "Reason for editing /etc/hosts (will be logged):" >&2 read -r -p "Enter reason: " REASON -if [[ -z ${REASON// } ]]; then - echo "Empty reason not allowed. Aborting." >&2 - exit 1 +if [[ -z ${REASON// /} ]]; then + echo "Empty reason not allowed. Aborting." >&2 + exit 1 fi log "Requested intentional /etc/hosts modification session. Reason: $REASON" logger -t "$SYSLOG_TAG" "session_start user=${SUDO_USER:-$USER} reason='$REASON'" echo "This action is logged. A cooling-off delay of $DELAY_SECONDS seconds applies." >&2 for s in hosts-bind-mount.service hosts-guard.path; do - if systemctl is-active --quiet "$s"; then - log "Stopping $s" - systemctl stop "$s" || true - fi - if systemctl is-enabled --quiet "$s"; then - log "(Will re-enable later)" - fi + if systemctl is-active --quiet "$s"; then + log "Stopping $s" + systemctl stop "$s" || true + fi + if systemctl is-enabled --quiet "$s"; then + log "(Will re-enable later)" + fi done # Remove attributes to allow edit -chattr -i -a "$TARGET" 2>/dev/null || true +chattr -i -a "$TARGET" 2> /dev/null || true echo "Countdown:" >&2 -for ((i=DELAY_SECONDS; i>0; i--)); do - printf '\rEdit window opens in %2d seconds... Press Ctrl+C to abort.' "$i" >&2 - sleep 1 +for ((i = DELAY_SECONDS; i > 0; i--)); do + printf '\rEdit window opens in %2d seconds... Press Ctrl+C to abort.' "$i" >&2 + sleep 1 done echo >&2 @@ -49,13 +49,13 @@ sha_before=$(sha256sum "$TARGET" | awk '{print $1}') "$EDITOR_CMD" "$TARGET" sha_after=$(sha256sum "$TARGET" | awk '{print $1}') -if [[ "$sha_before" == "$sha_after" ]]; then - log "No changes made to $TARGET. Reason: $REASON" - logger -t "$SYSLOG_TAG" "no_change user=${SUDO_USER:-$USER} reason='$REASON'" +if [[ $sha_before == "$sha_after" ]]; then + log "No changes made to $TARGET. Reason: $REASON" + logger -t "$SYSLOG_TAG" "no_change user=${SUDO_USER:-$USER} reason='$REASON'" else - log "Changes detected. Updating canonical copy and re-enforcing. Reason: $REASON" - logger -t "$SYSLOG_TAG" "modified user=${SUDO_USER:-$USER} reason='$REASON'" - cp "$TARGET" "$CANON" + log "Changes detected. Updating canonical copy and re-enforcing. Reason: $REASON" + logger -t "$SYSLOG_TAG" "modified user=${SUDO_USER:-$USER} reason='$REASON'" + cp "$TARGET" "$CANON" fi # Re-run enforcement diff --git a/hosts/guard/setup_hosts_guard.sh b/hosts/guard/setup_hosts_guard.sh index 7da6cdc..19da91c 100755 --- a/hosts/guard/setup_hosts_guard.sh +++ b/hosts/guard/setup_hosts_guard.sh @@ -46,10 +46,20 @@ ADD_ALIAS_STUB=1 msg() { printf '\e[1;32m[+]\e[0m %s\n' "$*"; } note() { printf '\e[1;34m[i]\e[0m %s\n' "$*"; } warn() { printf '\e[1;33m[!]\e[0m %s\n' "$*"; } -err() { printf '\e[1;31m[x]\e[0m %s\n' "$*" >&2; } -run() { if [[ $DRY_RUN -eq 1 ]]; then printf 'DRY-RUN: %s\n' "$*"; else eval "$@"; fi } +err() { printf '\e[1;31m[x]\e[0m %s\n' "$*" >&2; } +run() { + if [[ $DRY_RUN -eq 1 ]]; then + printf 'DRY-RUN:' + if [ "$#" -gt 0 ]; then + printf ' %q' "$@" + fi + printf '\n' + else + "$@" + fi +} -require_root() { if [[ $EUID -ne 0 ]]; then exec sudo -E bash "$0" "$@"; fi } +require_root() { if [[ $EUID -ne 0 ]]; then exec sudo -E bash "$0" "$@"; fi; } usage() { sed -n '1,/^set -euo pipefail/p' "$0" | sed 's/^# \{0,1\}//'; } @@ -58,21 +68,71 @@ usage() { sed -n '1,/^set -euo pipefail/p' "$0" | sed 's/^# \{0,1\}//'; } ###################################################################### while [[ $# -gt 0 ]]; do case "$1" in - --force-snapshot) FORCE_SNAPSHOT=1 ; shift ;; - --no-snapshot) DO_SNAPSHOT=0 ; shift ;; - --skip-bind) ENABLE_BIND=0 ; shift ;; - --skip-path-watch) ENABLE_PATH=0 ; shift ;; - --delay) DELAY=${2:-} ; [[ -z ${DELAY} ]] && { err '--delay requires value'; exit 2; } ; shift 2 ;; - --dry-run) DRY_RUN=1 ; shift ;; - --no-shell-hooks) INSTALL_SHELL_HOOKS=0 ; shift ;; - --shell-hooks) INSTALL_SHELL_HOOKS=1 ; shift ;; - --no-audit) INSTALL_AUDIT_RULE=0 ; shift ;; - --audit) INSTALL_AUDIT_RULE=1 ; shift ;; - --no-alias-stub) ADD_ALIAS_STUB=0 ; shift ;; - --alias-stub) ADD_ALIAS_STUB=1 ; shift ;; - --uninstall) UNINSTALL=1 ; shift ;; - -h|--help) usage; exit 0 ;; - *) err "Unknown argument: $1"; usage; exit 2 ;; + --force-snapshot) + FORCE_SNAPSHOT=1 + shift + ;; + --no-snapshot) + DO_SNAPSHOT=0 + shift + ;; + --skip-bind) + ENABLE_BIND=0 + shift + ;; + --skip-path-watch) + ENABLE_PATH=0 + shift + ;; + --delay) + DELAY=${2:-} + [[ -z ${DELAY} ]] && { + err '--delay requires value' + exit 2 + } + shift 2 + ;; + --dry-run) + DRY_RUN=1 + shift + ;; + --no-shell-hooks) + INSTALL_SHELL_HOOKS=0 + shift + ;; + --shell-hooks) + INSTALL_SHELL_HOOKS=1 + shift + ;; + --no-audit) + INSTALL_AUDIT_RULE=0 + shift + ;; + --audit) + INSTALL_AUDIT_RULE=1 + shift + ;; + --no-alias-stub) + ADD_ALIAS_STUB=0 + shift + ;; + --alias-stub) + ADD_ALIAS_STUB=1 + shift + ;; + --uninstall) + UNINSTALL=1 + shift + ;; + -h | --help) + usage + exit 0 + ;; + *) + err "Unknown argument: $1" + usage + exit 2 + ;; esac done @@ -119,7 +179,7 @@ if [[ $UNINSTALL -eq 1 ]]; then "$SYSTEMD_DIR/hosts-bind-mount.service" \ "$ZSH_FILTER_SNIPPET" \ "$BASH_FILTER_SNIPPET"; do - if [[ -e $f ]]; then run rm -f "$f"; fi + if [[ -e $f ]]; then run rm -f "$f"; fi done note "Leaving canonical snapshot at $CANON (remove manually if undesired)." if [[ $DRY_RUN -eq 0 ]]; then systemctl daemon-reload; fi @@ -134,10 +194,13 @@ note "Script directory: $SCRIPT_DIR" note "Repository root: $REPO_ROOT" for req in "$TEMPLATE_ENFORCE" "$TEMPLATE_UNLOCK" "$UNIT_GUARD_SERVICE"; do - [[ -f $req ]] || { err "Missing template: $req"; exit 1; } + [[ -f $req ]] || { + err "Missing template: $req" + exit 1 + } done -if [[ ! -f "$HOSTS" ]]; then +if [[ ! -f $HOSTS ]]; then err "$HOSTS does not exist. Run your hosts/install.sh first." exit 1 fi @@ -146,7 +209,7 @@ fi # Snapshot ###################################################################### if [[ $DO_SNAPSHOT -eq 1 ]]; then - if [[ -f "$CANON" && $FORCE_SNAPSHOT -eq 0 ]]; then + if [[ -f $CANON && $FORCE_SNAPSHOT -eq 0 ]]; then note "Canonical snapshot exists (use --force-snapshot to overwrite)" else msg "Creating canonical snapshot at $CANON" @@ -182,14 +245,12 @@ fi if [[ $INSTALL_SHELL_HOOKS -eq 1 ]]; then msg "Installing shell history suppression hooks for unlock command" # Pattern matches commands invoking unlock-hosts (with or without sudo) & setup script force snapshot - FILTER_PATTERN='(^|;|&&|\|\|)\s*(sudo\s+)?(/usr/local/sbin/)?unlock-hosts(\s|;|$)' - # Zsh: use zshaddhistory function - if command -v zsh >/dev/null 2>&1; then + if command -v zsh > /dev/null 2>&1; then if [[ $DRY_RUN -eq 1 ]]; then echo "DRY-RUN: would create $ZSH_FILTER_SNIPPET" else - cat > "$ZSH_FILTER_SNIPPET" <<'ZEOF' + cat > "$ZSH_FILTER_SNIPPET" << 'ZEOF' # Added by hosts guard setup – suppress unlock-hosts commands from Zsh history autoload -Uz add-zsh-hook 2>/dev/null || true _hosts_guard_history_filter() { @@ -213,11 +274,11 @@ ZEOF fi # Bash: rely on HISTCONTROL and PROMPT_COMMAND filter - if command -v bash >/dev/null 2>&1; then + if command -v bash > /dev/null 2>&1; then if [[ $DRY_RUN -eq 1 ]]; then echo "DRY-RUN: would create $BASH_FILTER_SNIPPET" else - cat > "$BASH_FILTER_SNIPPET" <<'BEOF' + cat > "$BASH_FILTER_SNIPPET" << 'BEOF' # Added by hosts guard setup – suppress unlock-hosts commands from Bash history export HISTCONTROL=ignoredups:erasedups _hosts_guard_hist_filter() { @@ -253,7 +314,7 @@ if [[ $ADD_ALIAS_STUB -eq 1 ]]; then if [[ $DRY_RUN -eq 1 ]]; then echo "DRY-RUN: would create $PROFILE_STUB" else - cat > "$PROFILE_STUB" <<'ASTUB' + cat > "$PROFILE_STUB" << 'ASTUB' # Added by hosts guard setup – discourages casual use of unlock-hosts name if command -v unlock-hosts >/dev/null 2>&1; then alias unlock-hosts='command_not_found_handle 2>/dev/null || echo "Use: sudo /usr/local/sbin/unlock-hosts (logged & delayed)"' @@ -267,16 +328,17 @@ fi # Audit rule to record executions (requires auditd) ###################################################################### if [[ $INSTALL_AUDIT_RULE -eq 1 ]]; then - if command -v auditctl >/dev/null 2>&1; then - AUDIT_RULE="-w /usr/local/sbin/unlock-hosts -p x -k hosts_unlock" - if auditctl -l 2>/dev/null | grep -Fq "/usr/local/sbin/unlock-hosts"; then + if command -v auditctl > /dev/null 2>&1; then + audit_rule_str="-w /usr/local/sbin/unlock-hosts -p x -k hosts_unlock" + audit_rule_args=(-w /usr/local/sbin/unlock-hosts -p x -k hosts_unlock) + if auditctl -l 2> /dev/null | grep -Fq "/usr/local/sbin/unlock-hosts"; then note "Audit rule already present" else - run auditctl $AUDIT_RULE || warn "Failed to add audit rule (runtime)" + run auditctl "${audit_rule_args[@]}" || warn "Failed to add audit rule (runtime)" if [[ $DRY_RUN -eq 1 ]]; then echo "DRY-RUN: would create /etc/audit/rules.d/hosts_unlock.rules" else - echo "$AUDIT_RULE" > /etc/audit/rules.d/hosts_unlock.rules + echo "$audit_rule_str" > /etc/audit/rules.d/hosts_unlock.rules fi fi else @@ -323,22 +385,22 @@ fi ###################################################################### echo msg "Hosts guard setup complete" -echo "Canonical copy: $CANON" -echo "Enforce script: $INSTALL_ENFORCE" -echo "Unlock command: sudo $INSTALL_UNLOCK" -echo "Delay (seconds): $DELAY" -echo "Auto-revert path watch: $([[ $ENABLE_PATH -eq 1 ]] && echo enabled || echo disabled)" -echo "Read-only bind mount: $([[ $ENABLE_BIND -eq 1 ]] && echo enabled || echo disabled)" +echo "Canonical copy: $CANON" +echo "Enforce script: $INSTALL_ENFORCE" +echo "Unlock command: sudo $INSTALL_UNLOCK" +echo "Delay (seconds): $DELAY" +echo "Auto-revert path watch: $([[ $ENABLE_PATH -eq 1 ]] && echo enabled || echo disabled)" +echo "Read-only bind mount: $([[ $ENABLE_BIND -eq 1 ]] && echo enabled || echo disabled)" echo "Shell history suppression: $([[ $INSTALL_SHELL_HOOKS -eq 1 ]] && echo enabled || echo disabled)" echo "Audit rule: $([[ $INSTALL_AUDIT_RULE -eq 1 ]] && echo enabled || echo disabled)" echo "Alias stub: $([[ $ADD_ALIAS_STUB -eq 1 ]] && echo enabled || echo disabled)" echo -echo "Test flow:" -echo " sudo sed -i '1s/.*/# tamper test/' /etc/hosts # Should revert automatically" -echo " sudo $INSTALL_UNLOCK # Intentional edit workflow" +echo "Test flow:" +echo " sudo sed -i '1s/.*/# tamper test/' /etc/hosts # Should revert automatically" +echo " sudo $INSTALL_UNLOCK # Intentional edit workflow" echo -echo "Uninstall:" -echo " sudo $0 --uninstall" -echo "(Optional) Skip shell history hooks: --no-shell-hooks" +echo "Uninstall:" +echo " sudo $0 --uninstall" +echo "(Optional) Skip shell history hooks: --no-shell-hooks" echo exit 0