feat: embedder environment-hint hook for the system prompt (#34574)
* fix(security): block AWS SDK creds from subprocess env * fix(security): narrow Bedrock subprocess strip to inference bearer token only Scopes the AWS_SDK subprocess strip down from the full AWS credential chain to just AWS_BEARER_TOKEN_BEDROCK — the only Hermes-managed *inference* secret (analogous to OPENAI_API_KEY). The general AWS credential chain (AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY / AWS_SESSION_TOKEN / AWS_PROFILE / config + role pointers) is intentionally left inheritable. Why: per SECURITY.md §3.2 the local terminal is the user's trusted operator shell. Hard-blocklisting the general chain would (a) regress *every* user who runs aws/terraform/cdk/boto3 in the agent terminal — not just Bedrock users, since PROVIDER_REGISTRY is iterated unconditionally at import — and (b) be unrecoverable, because env_passthrough.py refuses to re-allow anything in _HERMES_PROVIDER_ENV_BLOCKLIST (GHSA-rhgp-j443-p4rf). The narrow strip closes the reported leak (opencode enumerating the Bedrock catalog off the leaked bearer token) with no capability loss. Keeps zapabob's self-healing auth_type=="aws_sdk" mechanism so any future SDK-cred provider is covered automatically. Tests: bearer token stripped + general chain preserved (no-regression guard), on both the runtime strip path and the blocklist-membership path. Co-authored-by: zapabob <1920071390@campus.ouj.ac.jp> * feat: embedder environment-hint hook for the system prompt Adds HERMES_ENVIRONMENT_HINT env var (and config.yaml agent.environment_hint) so a host wrapping Hermes (sandbox runner, managed platform) can describe the runtime environment — proxy, credential handling, mount layout — in the system prompt's environment-hints block, without editing the identity slot (SOUL.md). Read once at prompt-build time, so it lands in the stable, cache-safe portion of the system prompt. Env var overrides the config key (build-time/container mechanism). Empty by default — no behavior change for existing installs. --------- Co-authored-by: zapabob <1920071390@campus.ouj.ac.jp>
This commit is contained in:
@@ -947,6 +947,58 @@ class TestEnvironmentHints:
|
||||
f"info is suppressed in the system prompt"
|
||||
)
|
||||
|
||||
def test_environment_hint_from_env_var_is_appended(self, monkeypatch):
|
||||
"""HERMES_ENVIRONMENT_HINT lets an embedder describe the runtime env."""
|
||||
import agent.prompt_builder as _pb
|
||||
monkeypatch.setattr(_pb, "is_wsl", lambda: False)
|
||||
monkeypatch.delenv("TERMINAL_ENV", raising=False)
|
||||
monkeypatch.setenv("HERMES_ENVIRONMENT_HINT", "Running inside an OpenShell sandbox.")
|
||||
_pb._clear_backend_probe_cache()
|
||||
result = _pb.build_environment_hints()
|
||||
assert "Running inside an OpenShell sandbox." in result
|
||||
# The factual host block must still come first.
|
||||
assert result.index("Host:") < result.index("OpenShell")
|
||||
|
||||
def test_environment_hint_env_var_overrides_config(self, monkeypatch):
|
||||
"""Env var wins over config.yaml agent.environment_hint."""
|
||||
import agent.prompt_builder as _pb
|
||||
monkeypatch.setattr(_pb, "is_wsl", lambda: False)
|
||||
monkeypatch.delenv("TERMINAL_ENV", raising=False)
|
||||
monkeypatch.setenv("HERMES_ENVIRONMENT_HINT", "ENV-WINS")
|
||||
monkeypatch.setattr(
|
||||
"hermes_cli.config.load_config",
|
||||
lambda: {"agent": {"environment_hint": "CONFIG-VALUE"}},
|
||||
)
|
||||
_pb._clear_backend_probe_cache()
|
||||
result = _pb.build_environment_hints()
|
||||
assert "ENV-WINS" in result
|
||||
assert "CONFIG-VALUE" not in result
|
||||
|
||||
def test_environment_hint_falls_back_to_config(self, monkeypatch):
|
||||
"""With no env var, the config.yaml value is used."""
|
||||
import agent.prompt_builder as _pb
|
||||
monkeypatch.setattr(_pb, "is_wsl", lambda: False)
|
||||
monkeypatch.delenv("TERMINAL_ENV", raising=False)
|
||||
monkeypatch.delenv("HERMES_ENVIRONMENT_HINT", raising=False)
|
||||
monkeypatch.setattr(
|
||||
"hermes_cli.config.load_config",
|
||||
lambda: {"agent": {"environment_hint": "CONFIG-VALUE"}},
|
||||
)
|
||||
_pb._clear_backend_probe_cache()
|
||||
result = _pb.build_environment_hints()
|
||||
assert "CONFIG-VALUE" in result
|
||||
|
||||
def test_environment_hint_empty_by_default(self, monkeypatch):
|
||||
"""No hint configured anywhere → no embedder text, host block intact."""
|
||||
import agent.prompt_builder as _pb
|
||||
monkeypatch.setattr(_pb, "is_wsl", lambda: False)
|
||||
monkeypatch.delenv("TERMINAL_ENV", raising=False)
|
||||
monkeypatch.delenv("HERMES_ENVIRONMENT_HINT", raising=False)
|
||||
monkeypatch.setattr("hermes_cli.config.load_config", lambda: {"agent": {}})
|
||||
_pb._clear_backend_probe_cache()
|
||||
result = _pb.build_environment_hints()
|
||||
assert "Host:" in result
|
||||
|
||||
|
||||
# =========================================================================
|
||||
# Conditional skill activation
|
||||
|
||||
Reference in New Issue
Block a user