#!/usr/bin/env bash # ───────────────────────────────────────────────────────────── # himalaya-ro — Read-only wrapper for himalaya # # Blocks destructive commands and logs audit trail. # Pass-through for read-only commands (list, read, search). # # Usage: himalaya-ro [options] [args...] # # Install: place in PATH before the real himalaya, or use # `ln -sf himalaya-ro /usr/local/bin/himalaya` # ───────────────────────────────────────────────────────────── set -o pipefail # ── Configuration ─────────────────────────────────────────── HIMALAYA_BIN="${HIMALAYA_BIN:-/usr/local/bin/himalaya}" AUDIT_LOG="${HIMALAYA_AUDIT_LOG:-/var/log/himalaya-audit.log}" # ── Destructive commands we block ────────────────────────── BLOCKED_CMDS=( "message move" "message delete" "message copy" "flag add" "flag remove" "folder create" "folder delete" "folder rename" "template send" "account configure" "account delete" ) # ── Determine the subcommand being invoked ───────────────── # Strip leading options (--account, --output, etc.) to find the verb ARGS=() SKIP_NEXT=false for arg in "$@"; do if $SKIP_NEXT; then SKIP_NEXT=false continue fi if [[ "$arg" == --* ]]; then case "$arg" in --account|--output|--page|--page-size|--folder|--color|--format) SKIP_NEXT=true ;; esac continue fi ARGS+=("$arg") done # Build subcommand string and check against blocklist CMD_STR="" for ((i=0; i<${#ARGS[@]}; i++)); do if [ -z "$CMD_STR" ]; then CMD_STR="${ARGS[$i]}" else CMD_STR="$CMD_STR ${ARGS[$i]}" fi for blocked in "${BLOCKED_CMDS[@]}"; do if [[ "$CMD_STR" == "$blocked" ]]; then TS=$(date '+%Y-%m-%d %H:%M:%S') echo "[AUDIT] $TS BLOCKED: himalaya $*" >> "$AUDIT_LOG" echo "ERROR: Command 'himalaya $CMD_STR ...' is blocked by read-only policy." >&2 echo " Audit log: $AUDIT_LOG" >&2 exit 100 fi done done # ── Allow pass-through ───────────────────────────────────── exec "$HIMALAYA_BIN" "$@"