fix(gateway): isolate approval session key per turn

This commit is contained in:
Tranquil-Flow 2026-04-03 01:09:45 +00:00 committed by Teknium
parent 5359921199
commit 3bfb39a25f
4 changed files with 136 additions and 5 deletions

View file

@ -8,6 +8,7 @@ This module is the single source of truth for the dangerous command system:
- Permanent allowlist persistence (config.yaml)
"""
import contextvars
import logging
import os
import re
@ -18,6 +19,33 @@ from typing import Optional
logger = logging.getLogger(__name__)
# Per-thread/per-task gateway session identity.
# Gateway runs agent turns concurrently in executor threads, so reading a
# process-global env var for session identity is racy. Keep env fallback for
# legacy single-threaded callers, but prefer the context-local value when set.
_approval_session_key: contextvars.ContextVar[str] = contextvars.ContextVar(
"approval_session_key",
default="",
)
def set_current_session_key(session_key: str):
"""Bind the active approval session key to the current context."""
return _approval_session_key.set(session_key or "")
def reset_current_session_key(token) -> None:
"""Restore the prior approval session key context."""
_approval_session_key.reset(token)
def get_current_session_key(default: str = "default") -> str:
"""Return the active session key, preferring context-local state."""
session_key = _approval_session_key.get()
if session_key:
return session_key
return os.getenv("HERMES_SESSION_KEY", default)
# Sensitive write targets that should trigger approval even when referenced
# via shell expansions like $HOME or $HERMES_HOME.
_SSH_SENSITIVE_PATH = r'(?:~|\$home|\$\{home\})/\.ssh(?:/|$)'
@ -534,7 +562,7 @@ def check_dangerous_command(command: str, env_type: str,
if not is_dangerous:
return {"approved": True, "message": None}
session_key = os.getenv("HERMES_SESSION_KEY", "default")
session_key = get_current_session_key()
if is_approved(session_key, pattern_key):
return {"approved": True, "message": None}
@ -660,7 +688,7 @@ def check_all_command_guards(command: str, env_type: str,
# Collect warnings that need approval
warnings = [] # list of (pattern_key, description, is_tirith)
session_key = os.getenv("HERMES_SESSION_KEY", "default")
session_key = get_current_session_key()
# Tirith block/warn → approvable warning with rich findings.
# Previously, tirith "block" was a hard block with no approval prompt.