#!/usr/bin/env bash
# paths.sh — path safety validators shared by Bash hook and Stop hook.
#
# Mirrors the "三档校验" rules from .claude/agents/lingxi-evo.md L35-47.
# Detects three classes of accidents commonly hit by LLM-generated bash:
#   B1: command uses ${VAR}/* or ${VAR}/... as a path argument and VAR is empty
#   B2: ${VAR} resolves to "/" or "$HOME"
#   (B3 — writing outside EVO_DIR — is a Stop-hook responsibility, not here)

if [[ -n "${LOOP_HOOK_PATHS_SOURCED:-}" ]]; then
    return 0
fi
LOOP_HOOK_PATHS_SOURCED=1

# Risky command patterns. Match the leading token after the command.
# Matches: cp [-rR]*, mv, rm [-rR]*f*
_PATH_SAFETY_RISKY_COMMANDS_RE='^(cp|mv|rm)\b'

# Tokens that signal a dangerous root path
_PATH_SAFETY_ROOT_TOKENS=("/" "/*" "//" "/home" "/root")

# Given a single bash command string, return:
#   0 if safe (or not a risky command)
#   1 if a B1/B2 violation is found; echoes a one-line reason to stderr.
#
# This is intentionally conservative — only blocks when a variable name is
# explicit (${VAR} or $VAR) AND the variable is unset/empty in the current env,
# or when a literal "/" / "/*" is used as the first path arg of rm -rf.
check_bash_path_safety() {
    local cmd="$1"
    [[ -z "$cmd" ]] && return 0

    # Quick exit if the command doesn't start with a risky command
    local first_word
    first_word="$(echo "$cmd" | awk '{print $1}')"
    if ! [[ "${first_word}" =~ ${_PATH_SAFETY_RISKY_COMMANDS_RE} ]]; then
        return 0
    fi

    # Extract all $VAR / ${VAR} references in the command
    # Use grep -oE to pull them out, then check each
    local vars
    vars="$(echo "$cmd" | grep -oE '\$\{?[A-Za-z_][A-Za-z0-9_]*\}?' | sort -u)"

    local var_name
    while IFS= read -r raw; do
        [[ -z "${raw}" ]] && continue
        # strip $ and {} to get the bare name
        var_name="${raw#\$}"
        var_name="${var_name#\{}"
        var_name="${var_name%\}}"

        # Use indirect expansion to read the var from env
        local var_value
        var_value="${!var_name:-}"

        # B1: VAR is empty AND command uses VAR-relative path expansion
        # Pattern: $VAR/* OR ${VAR}/* OR $VAR/<word> OR ${VAR}/<word>
        if [[ -z "${var_value}" ]]; then
            if echo "$cmd" | grep -qE "\\\$\\{?${var_name}\\}?(/|$)"; then
                echo "B1: variable \$${var_name} is empty but used as a path in '${first_word}'" >&2
                return 1
            fi
        fi

        # B2: VAR resolves to a dangerous root
        if [[ "${var_value}" == "/" || "${var_value}" == "${HOME}" || "${var_value}" == "/root" || "${var_value}" == "/home" ]]; then
            echo "B2: variable \$${var_name}=${var_value} resolves to a protected root path" >&2
            return 1
        fi
    done <<< "${vars}"

    # Literal root usage with destructive commands
    # rm -rf / or rm -rf /*
    if [[ "${first_word}" == "rm" ]]; then
        # Match "rm -rf /" with optional trailing /* and require word boundary
        if echo "$cmd" | grep -qE 'rm[[:space:]]+-[rRfF]+[[:space:]]+/(\*|[[:space:]]|$)'; then
            echo "B2: literal 'rm -rf /' detected" >&2
            return 1
        fi
        # rm -rf $HOME or rm -rf /root
        if echo "$cmd" | grep -qE "rm[[:space:]]+-[rRfF]+[[:space:]]+(/root|/home)(/\*|/[[:space:]]|[[:space:]]|$)"; then
            echo "B2: literal 'rm -rf /root' or '/home' detected" >&2
            return 1
        fi
    fi

    return 0
}

export -f check_bash_path_safety