Skip to content

feat(secrets): Phase 2 — Environment Scoping + Terminal Integration #3628

@vulcan-artivus

Description

@vulcan-artivus

Parent Issue

Sub-issue of #410 (Secure Secrets Management Tool). This is Phase 2 of the phased rollout. Depends on Phase 1 (#3627).

Scope

Replace the current "pass entire os.environ to subprocesses" behavior with a tiered environment model, and add entropy-based secret detection in output.

1. Tiered environment model

Tier Description Example
SAFE Always available to subprocesses PATH, HOME, USER, LANG, TERM, SHELL, TMPDIR, XDG_*
TOOL-MANAGED Available only when a tool declares it needs them via injects_env in tool registration FIRECRAWL_API_KEY for web_tools, BROWSERBASE_* for browser_tool
USER-REQUESTED Available only when the agent explicitly calls secrets(action="inject") before a terminal command TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN

2. Terminal tool env_keys parameter

New optional parameter on the terminal tool:

terminal(
    command="curl -X POST https://api.twilio.com/... -u $TWILIO_ACCOUNT_SID:$TWILIO_AUTH_TOKEN",
    env_keys=["TWILIO_ACCOUNT_SID", "TWILIO_AUTH_TOKEN"]
)

The terminal tool:

  1. Starts with only SAFE tier env vars
  2. Adds only the explicitly listed env_keys from the secret store
  3. Shell $VAR expansion works normally inside the command
  4. Output is redacted via redact_sensitive_text() as usual
  5. Actual secret values never appear in tool call arguments or response

This extends the pattern already used by tools/mcp_tool.py (_SAFE_ENV_KEYS) to the terminal tool.

3. Tool registry injects_env field

Tool registration gains an optional injects_env field declaring which env vars the tool needs:

registry.register(
    name="web_search",
    toolset="web",
    injects_env=["FIRECRAWL_API_KEY"],
    ...
)

These are automatically available when that tool's subprocess runs, without the agent needing to call secrets(action="inject").

4. Shannon entropy-based secret detection

Inspired by PII-Shield:

  • Detect high-entropy strings in tool output that may be leaked secrets
  • Use Shannon entropy + bigram analysis (not just regex) to catch novel key formats
  • Deterministic HMAC redaction: [HIDDEN:8f2a1b] preserves referential integrity for debugging
  • Configurable threshold to balance false positives vs coverage

5. Migration path

The transition from "pass everything" to "pass only safe vars" is a breaking change for existing users whose commands depend on env vars being available. Proposed approach:

  1. Opt-in period: Add terminal.env_mode: legacy (default) vs terminal.env_mode: scoped in config.yaml
  2. Warning mode: When legacy, log a warning if a subprocess accesses an env var that wouldn't be available in scoped mode
  3. Future default: After one release cycle, switch default to scoped

Files to Create/Modify

  • Modify: tools/terminal_tool.pyenv_keys parameter, SAFE_ENV filtering
  • Modify: tools/environments/local.py — tiered env filtering (currently passes all os.environ)
  • Modify: tools/environments/*.py — apply same tiering to Docker, SSH, Modal, Daytona, Singularity backends
  • Modify: tools/registry.pyinjects_env field on tool registration
  • Modify: agent/redact.py — entropy-based detection
  • Modify: hermes_cli/config.pyterminal.env_mode config key

Acceptance Criteria

  • Terminal subprocesses get only SAFE tier env vars by default (when opted in)
  • env_keys parameter works on terminal tool
  • injects_env on tool registration automatically scopes env vars for that tool
  • Shannon entropy detector catches high-entropy strings in output
  • terminal.env_mode config key with legacy/scoped options
  • All 6 terminal backends (local, Docker, SSH, Modal, Daytona, Singularity) support tiered env
  • Backward-compatible migration path documented

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    P3Low — cosmetic, nice to havearea/authAuthentication, OAuth, credential poolstool/terminalTerminal execution and process managementtype/featureNew feature or request

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions