/** * Hardened guarded-shell for zsh (ZDOTDIR approach) * * The guarded-shell is a minimal launcher that sets ZDOTDIR to a root-owned * config directory, then execs /bin/zsh. The actual restrictions live in * .zshenv and .zshrc inside that ZDOTDIR, which zsh reads on startup. * * This avoids the previous bug where `exec /bin/zsh -f` discarded all * shell-level restrictions (RESTRICTED, preexec hooks, disabled builtins) * and macOS /etc/zshenv restored the full system PATH via path_helper. * * ZDOTDIR approach guarantees: * - .zshenv runs AFTER /etc/zshenv, so we override path_helper's PATH * - .zshrc applies RESTRICTED mode, hooks, and builtin lockdown * - Both files are root-owned (0644) so the agent cannot modify them */ export declare const GUARDED_SHELL_PATH = "/usr/local/bin/guarded-shell"; export declare const ZDOT_DIR = "/etc/agenshield/zdot"; /** * Guarded shell launcher — minimal, just sets ZDOTDIR and execs zsh. * Restrictions are applied by ZDOT_ZSHENV_CONTENT and ZDOT_ZSHRC_CONTENT. */ export declare const GUARDED_SHELL_CONTENT = "#!/bin/zsh\n# guarded-shell: launcher for restricted agent shell.\n# All restrictions live in ZDOTDIR files (root-owned, immutable to agent).\nemulate -LR zsh\n\n# Prevent inherited env tricks before handing off to zsh\nunset HOME\nunset DYLD_LIBRARY_PATH DYLD_FALLBACK_LIBRARY_PATH DYLD_INSERT_LIBRARIES\nunset PYTHONPATH NODE_PATH RUBYLIB PERL5LIB\nunset SSH_ASKPASS LD_PRELOAD\n\n# Point zsh at our restricted config directory\nexport ZDOTDIR=\"/etc/agenshield/zdot\"\n\n# Start zsh \u2014 it will read ZDOTDIR/.zshenv then ZDOTDIR/.zshrc\nexec /bin/zsh \"$@\"\n"; /** * ZDOTDIR .zshenv — runs after /etc/zshenv (which calls path_helper on macOS). * Overrides PATH to only include $HOME/bin. */ export declare const ZDOT_ZSHENV_CONTENT = "# AgenShield restricted .zshenv\n# Runs AFTER /etc/zshenv \u2014 overrides path_helper's full system PATH.\n\n# ALWAYS set HOME based on actual user, never inherit\nexport HOME=\"/Users/$(id -un)\"\nexport HISTFILE=\"$HOME/.zsh_history\"\n\n# Suppress locale to prevent /etc/zshrc from calling locale command\nexport LC_ALL=C LANG=C\n\nexport PATH=\"$HOME/bin:$HOME/homebrew/bin\"\nexport SHELL=\"/usr/local/bin/guarded-shell\"\n\n# NVM initialization\nexport NVM_DIR=\"$HOME/.nvm\"\n[ -s \"$NVM_DIR/nvm.sh\" ] && \\. \"$NVM_DIR/nvm.sh\"\n\n# Clear any leftover env tricks\nunset DYLD_LIBRARY_PATH DYLD_FALLBACK_LIBRARY_PATH DYLD_INSERT_LIBRARIES\nunset PYTHONPATH NODE_PATH RUBYLIB PERL5LIB\nunset SSH_ASKPASS LD_PRELOAD\n\n# Skip system rc files (/etc/zprofile, /etc/zshrc, /etc/zlogin)\n# They may call commands not in our restricted PATH (e.g. locale).\n# ZDOTDIR files (.zshrc) are still read.\nsetopt NO_GLOBAL_RCS\n"; /** * ZDOTDIR .zshrc — interactive shell restrictions. * Applies RESTRICTED mode, locks variables, disables builtins, installs hooks. */ export declare const ZDOT_ZSHRC_CONTENT = "# AgenShield restricted .zshrc\n# Applied to every interactive shell for the agent user.\n\nemulate -LR zsh\n\n# Re-set HISTFILE (safety: ensure it points to agent's home, not ZDOTDIR)\nHISTFILE=\"$HOME/.zsh_history\"\n\n# Re-set PATH (~/bin + ~/homebrew/bin \u2014 override anything that may have been added)\nPATH=\"$HOME/bin:$HOME/homebrew/bin\"\n\n# NVM re-source for interactive shell\nexport NVM_DIR=\"$HOME/.nvm\"\n[ -s \"$NVM_DIR/nvm.sh\" ] && \\. \"$NVM_DIR/nvm.sh\"\n\n# ---- Shell options ----\n# Note: NOT using setopt RESTRICTED as it disables cd entirely.\n# Instead we use preexec hooks and builtin disable for enforcement.\nsetopt NO_CASE_GLOB\nsetopt NO_BEEP\n\n# ---- Lock critical variables (readonly) ----\ntypeset -r PATH HOME SHELL HISTFILE NVM_DIR\n\n# ---- Enforcement helpers ----\ndeny() {\n print -r -- \"Denied by policy\"\n return 126\n}\n\nis_allowed_cmd() {\n local cmd=\"$1\"\n\n # Allow zsh reserved words (if, for, while, [[, case, etc.)\n [[ \"$(whence -w \"$cmd\" 2>/dev/null)\" == *\": reserved\" ]] && return 0\n\n # Allow shell builtins we explicitly permit\n case \"$cmd\" in\n cd|pwd|echo|printf|test|true|false|exit|return|break|continue|shift|set|unset|export|typeset|local|declare|readonly|let|read|print|pushd|popd|dirs|jobs|fg|bg|kill|wait|times|ulimit|umask|history|fc|type|whence|which|where|rehash)\n return 0\n ;;\n esac\n\n # Deny path execution outright\n [[ \"$cmd\" == */* ]] && return 1\n\n # Resolve command path\n local resolved\n resolved=\"$(whence -p -- \"$cmd\" 2>/dev/null)\" || return 1\n\n # Must live under HOME/bin, HOME/homebrew/bin, or HOME/.nvm\n [[ \"$resolved\" == \"$HOME/bin/\"* ]] && return 0\n [[ \"$resolved\" == \"$HOME/homebrew/bin/\"* ]] && return 0\n [[ \"$resolved\" == \"$HOME/.nvm/\"* ]] && return 0\n return 1\n}\n\n# ---- Block dangerous builtins ----\ndisable -r builtin command exec eval hash nohup setopt source unfunction functions alias unalias 2>/dev/null || true\n\n# ---- Intercept every interactive command before execution ----\npreexec() {\n # Enforcement handled by TRAPDEBUG (which can cancel execution via return 126).\n # preexec cannot prevent execution, so we don't enforce here.\n return 0\n}\n\n# ---- Also intercept non-interactive \\`zsh -c\\` cases ----\ntypeset -gi __ash_guard=0\n\nTRAPDEBUG() {\n # Prevent recursion when our own checks invoke whence/is_allowed_cmd\n (( __ash_guard )) && return 0\n\n local line=\"${ZSH_DEBUG_CMD:-$1}\"\n local cmd=\"${line%%[[:space:]]*}\"\n [[ -z \"$cmd\" ]] && return 0\n\n # Skip variable assignments (e.g. resolved=\"$(whence ...)\")\n [[ \"$cmd\" == *=* ]] && return 0\n\n # Skip zsh reserved words ([[, if, for, while, case, etc.)\n __ash_guard=1\n [[ \"$(whence -w \"$cmd\" 2>/dev/null)\" == *\": reserved\" ]] && { __ash_guard=0; return 0; }\n\n [[ \"$cmd\" == */* ]] && { __ash_guard=0; print -r -- \"Denied: direct path execution\"; return 126; }\n is_allowed_cmd \"$cmd\" || { __ash_guard=0; print -r -- \"Denied: $cmd\"; return 126; }\n __ash_guard=0\n return 0\n}\n\n# ---- Ensure accessible working directory ----\ncd \"$HOME\" 2>/dev/null || cd /\n"; //# sourceMappingURL=guarded-shell.d.ts.map