diff --git a/gateway/run.py b/gateway/run.py index 8c884307c1f4..3f4205769e16 100644 --- a/gateway/run.py +++ b/gateway/run.py @@ -5371,6 +5371,7 @@ def _is_user_authorized(self, source: SessionSource) -> bool: user_id = source.user_id if not user_id: return False + team_id = (source.guild_id or "").strip() platform_env_map = { Platform.TELEGRAM: "TELEGRAM_ALLOWED_USERS", diff --git a/hermes_cli/main.py b/hermes_cli/main.py index ee01e81cbbb9..74e4e7e36722 100644 --- a/hermes_cli/main.py +++ b/hermes_cli/main.py @@ -7189,19 +7189,22 @@ def _ensure_fhs_path_guard() -> None: if not fhs_link.is_symlink() and not fhs_link.exists(): return - # Probe a fresh non-login interactive bash the way the user will use it. - # ``bash -i -c`` sources ~/.bashrc but NOT ~/.bash_profile or /etc/profile, - # which is the exact scenario where RHEL root loses /usr/local/bin. - home = os.environ.get("HOME") or "/root" + # Resolve root's real home from passwd instead of trusting HOME. + try: + import pwd + + home = pwd.getpwuid(0).pw_dir or "/root" + except Exception: + home = "/root" try: probe = subprocess.run( [ "env", "-i", f"HOME={home}", + "PATH=/usr/local/bin:/usr/bin:/bin", f"TERM={os.environ.get('TERM', 'dumb')}", "bash", - "-i", "-c", "command -v hermes", ], @@ -7221,7 +7224,14 @@ def _ensure_fhs_path_guard() -> None: wrote_any = False for candidate in (".bashrc", ".bash_profile"): cfg = Path(home) / candidate - if not cfg.is_file(): + if not cfg.is_file() or cfg.is_symlink(): + continue + try: + st = cfg.stat() + except OSError: + continue + # Root-only: refuse files not owned by root or writable by group/world. + if st.st_uid != 0 or (st.st_mode & 0o022): continue try: existing = cfg.read_text(errors="replace") diff --git a/scripts/install.sh b/scripts/install.sh index bc391eee43c7..d0e99d71f996 100755 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -1145,11 +1145,15 @@ EOF # /root/.bash_profile doesn't either. So verify with `command -v` and # fall back to writing a PATH guard into /root/.bashrc when needed. if [ "$ROOT_FHS_LAYOUT" = true ]; then + ROOT_HOME="$(getent passwd root 2>/dev/null | cut -d: -f6)" + [ -n "$ROOT_HOME" ] || ROOT_HOME="/root" + if [ ! -d "$ROOT_HOME" ]; then + ROOT_HOME="/root" + fi export PATH="$command_link_dir:$PATH" - # Probe a fresh non-login interactive bash the way the user will use it. - # `bash -i -c` sources ~/.bashrc but NOT ~/.bash_profile or /etc/profile, - # which is the exact scenario where RHEL root loses /usr/local/bin. - if env -i HOME="$HOME" TERM="${TERM:-dumb}" bash -i -c 'command -v hermes' \ + # Probe a minimal shell environment without sourcing user startup files. + if env -i PATH="/usr/local/bin:/usr/bin:/bin" HOME="$ROOT_HOME" TERM="${TERM:-dumb}" \ + bash -c 'command -v hermes' \ >/dev/null 2>&1; then log_info "/usr/local/bin is already on PATH for all shells" log_success "hermes command ready" @@ -1159,8 +1163,17 @@ EOF log_info "hermes not on PATH in non-login shells (common on RHEL-family)" PATH_LINE='export PATH="/usr/local/bin:$PATH"' PATH_COMMENT='# Hermes Agent — ensure /usr/local/bin is on PATH (RHEL non-login shells)' - for SHELL_CONFIG in "$HOME/.bashrc" "$HOME/.bash_profile"; do + for SHELL_CONFIG in "$ROOT_HOME/.bashrc" "$ROOT_HOME/.bash_profile"; do [ -f "$SHELL_CONFIG" ] || continue + [ ! -L "$SHELL_CONFIG" ] || continue + if ! stat_out="$(stat -Lc '%u %a' "$SHELL_CONFIG" 2>/dev/null)"; then + continue + fi + shell_uid="${stat_out% *}" + shell_mode="${stat_out#* }" + # Must be root-owned and not group/world writable. + [ "$shell_uid" = "0" ] || continue + [ $((8#$shell_mode & 022)) -eq 0 ] || continue if ! grep -v '^[[:space:]]*#' "$SHELL_CONFIG" 2>/dev/null \ | grep -qE 'PATH=.*(/usr/local/bin|\$command_link_dir)'; then echo "" >> "$SHELL_CONFIG"