hermes - ✅(Solved) Fix Secret redaction breaks functional credential use in terminal commands [4 pull requests, 1 comments, 2 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
NousResearch/hermes-agent#16843Fetched 2026-04-29 06:38:35
View on GitHub
Comments
1
Participants
2
Timeline
9
Reactions
0
Author
Timeline (top)
cross-referenced ×4labeled ×4commented ×1

Fix Action

Fixed

PR fix notes

PR #16849: fix: add security.display_redaction_only to keep secrets usable in tools

Description (problem / solution / changelog)

Summary

security.redact_secrets was redacting credential values in **both** tool results (file reads, terminal output) and display/logging paths. When the LLM received partially-masked values (sk-a***c12) from tool results, it could not use them in subsequent commands — breaking:

  • htpasswd and similar tools that pass passwords via command-line arguments (#16843)
  • Bitwarden CLI workflows where Hermes retrieves a secret and passes it to a script (#16700)

Changes

New config: security.display_redaction_only

SettingEffect
false (default)Current behavior — redact everywhere (backward compatible)
trueRedact only in chat display + logs; real values pass through to tools
security:
  redact_secrets: true
  display_redaction_only: true

How it works

  • redact_sensitive_text() — used by tool results (file reads, terminal output, code execution). Respects the new flag: skips patterns when display_redaction_only=true so the LLM gets real values.
  • redact_for_display() — new function that **always** redacts, used by chat output (send_message), logs (RedactingFormatter), and context summaries. Never skips even when the flag is active.
  • All entry points — CLI, gateway, and hermes_cli all bridge the new config to the env var.

Testing

  • tests/agent/test_redact.py: 75 tests, all passing ✅

Closes #16843 Closes #16700

Changed files

  • .github/workflows/contributor-check.yml (removed, +0/-73)
  • .github/workflows/deploy-site.yml (removed, +0/-87)
  • .github/workflows/docker-publish.yml (removed, +0/-99)
  • .github/workflows/docs-site-checks.yml (removed, +0/-48)
  • .github/workflows/nix-lockfile-check.yml (removed, +0/-68)
  • .github/workflows/nix-lockfile-fix.yml (removed, +0/-149)
  • .github/workflows/nix.yml (removed, +0/-33)
  • .github/workflows/skills-index.yml (removed, +0/-101)
  • .github/workflows/supply-chain-audit.yml (removed, +0/-139)
  • .github/workflows/tests.yml (removed, +0/-82)
  • agent/context_compressor.py (modified, +4/-4)
  • agent/redact.py (modified, +55/-16)
  • cli.py (modified, +3/-0)
  • cron/scheduler.py (modified, +3/-3)
  • gateway/run.py (modified, +3/-0)
  • hermes_cli/config.py (modified, +3/-0)
  • hermes_cli/main.py (modified, +3/-0)
  • tools/send_message_tool.py (modified, +1/-1)

PR #16851: fix(file-safety): refuse read_file on credential paths (SSH keys, .env, …) (#16809)

Description (problem / solution / changelog)

Summary

  • Add a read-side counterpart to build_write_denied_paths() in agent/file_safety.py and have read_file_tool consult it before performing the read.
  • Blocks SSH private keys, the active HERMES_HOME .env, shell history, and credential directories (.ssh, .aws, .gnupg, .kube, .docker, .azure, .config/gh) from being read by the model.

The bug

read_file had no sensitive-path deny list. The reproduction in #16809 — and on a fresh checkout of main — succeeds today:

read_file("~/.ssh/id_ed25519")  # → full SSH private key
read_file("~/.hermes/.env")     # → full API keys
read_file("~/.zsh_history")     # → shell history

The only defense was pattern-based output redaction via redact_sensitive_text(), and that path was made off by default in 73753417 ("feat(security): make secret redaction off by default", #16794) and uses a module-level _REDACT_ENABLED snapshotted at import time — so config-driven opt-in is fragile, and well-known credential prefixes are the only thing recognised even when redaction is on.

Write-side protection exists (build_write_denied_paths + _check_sensitive_path) but has no read-side counterpart.

The fix

Add an access-control floor for high-value credential files where leakage is unrecoverable.

agent/file_safety.py gains three new helpers:

  • build_read_denied_paths(home) — exact paths: SSH keys (id_rsa/id_ed25519/id_ecdsa/id_dsa/authorized_keys/config), .netrc, .pgpass, .npmrc, .pypirc, the active HERMES_HOME / .env (resolved through get_hermes_home() so profile-aware), and shell history (.bash_history, .zsh_history, .psql_history).
  • build_read_denied_prefixes(home) — directory prefixes: ~/.ssh, ~/.aws, ~/.gnupg, ~/.kube, ~/.docker, ~/.azure, ~/.config/gh.
  • is_read_denied(path) — symmetric helper. Resolves through os.path.realpath before checking, so symlink shims are caught.

tools/file_tools.pyread_file_tool calls is_read_denied(path) after the existing device / binary / Hermes-internal guards (so denied paths can't return cached content from the dedup tracker either) and returns a structured error message pointing the model at the terminal tool when a credential read is genuinely intentional.

Intentional scope decisions

  • /etc/passwd is not blocked — world-readable user metadata, not a secret. /etc/shadow is already unreadable to non-root callers, so blocking it would be cosmetic.
  • Shell config (.bashrc, .zshrc, .profile, …) is write-denied but read-allowed: debugging PATH issues / alias setup is a legitimate case, and inline export FOO=key strings remain covered by redact_sensitive_text when security.redact_secrets is on.
  • The escape hatch for genuinely intentional reads (e.g. an agent that wants to inspect its own SSH config) is the existing terminal tool, which gates on user approval — exactly the layer this guard is designed to defer to.

Test plan

  • Focusedtests/tools/test_read_deny.py (new): 26 tests covering exact paths, prefixes, allowed paths, and end-to-end read_file_tool denial. All pass.
  • Adjacenttests/tools/test_write_deny.py, tests/tools/test_file_read_guards.py, tests/tools/test_file_operations.py, tests/tools/test_read_loop_detection.py, tests/tools/test_file_write_safety.py, tests/agent/test_copilot_acp_client.py. The 6 test_file_read_guards.py::TestFileDedup/TestWriteInvalidatesDedup failures and the 1 test_copilot_acp_client.py::test_read_text_file_redacts_sensitive_content failure reproduce on clean origin/main (8081425a1) — they're pre-existing macOS /private/var write-deny collision and the redaction-off-by-default regression respectively, unrelated to this change.
  • Regression guard — verified directly:
    Without guard, contents readable: True
    With guard, error returned: True
    With guard, secret leaked: False

Related

  • Fixes #16809.
  • Companion to the read-side concerns surfaced in #7826 (v0.8.0 audit) and adjacent to the redaction work in #16794 / #16700 / #16843 / #16849 — those address output-side redaction; this is the access-control floor underneath.

Changed files

  • agent/file_safety.py (modified, +81/-0)
  • tests/tools/test_read_deny.py (added, +206/-0)
  • tools/file_tools.py (modified, +24/-1)

PR #1: fix: resolve 7 identified issues [automated]

Description (problem / solution / changelog)

Resumo

Este PR corrige 7 issues identificados no repositório NousResearch/hermes-agent.


Issues Corrigidos

1. #17086 - custom endpoint com api_mode=anthropic_messages falhava com 404

Arquivo: agent/auxiliary_client.py Problema: Quando provider=custom com api_mode=anthropic_messages e base_url terminando em /anthropic, a funcao _resolve_provider_client() convertia a URL para o formato /v1/messages, causando 404 em provedores Anthropic-compatíveis de terceiros. Correcao: Mantem o path /anthropic quando api_mode=anthropic_messages e o base_url ja termina em /anthropic.


2. #17076 - kimi-coding vision quebrado (404 em analise de imagem)

Arquivo: agent/auxiliary_client.py Problema: kimi-coding nao estava listado em _PROVIDER_VISION_MODELS, entao auxiliary.vision.provider: auto nao conseguia detectar o modelo de visao disponivel para Kimi. Correcao: Adicionado kimi-coding e kimi-coding-cn ao mapa de modelos de visao.


3. #17080 - hermes profile create --clone copiava credenciais exclusivas de plataforma

Arquivo: hermes_cli/profiles.py Problema: O clone de perfil copiava TELEGRAM_BOT_TOKEN, DISCORD_BOT_TOKEN, WEIXIN_TOKEN verbatim. Quando dois perfis iniciam simultaneamente com o mesmo token, o adaptador de plataforma falha durante a aquisicao de lock. Correcao: Define _EXCLUSIVE_PLATFORM_KEYS e _EXCLUSIVE_PLATFORM_CONFIG_PATHS. Credenciais exclusivas sao comentadas em .env e entradas de plataforma sao enabled: false em config.yaml apos clone.


4. #17054 - Slack manifest rejeitava nomes com underscore

Arquivo: hermes_cli/commands.py Problema: _sanitize_slack_name() convertia nomes de comandos como _reload_mcp para Slack mas nao removia o prefixo underscore, causando rejeicao do manifest. Correcao: Adicionada verificacao para pular nomes que começam com underscore antes de adicionar a lista de slash commands.


5. #17057 - custom Kimi-compatible endpoint falhava apos tool call com thinking habilitado

Arquivo: run_agent.py Problema: _needs_kimi_tool_reasoning() só verificava hostnames oficiais (api.kimi.com, moonshot.ai, moonshot.cn). Endpoints Kimi-compatíveis customizados nao eram detectados. Correcao: Ampliada a verificacao para detectar endpoints customizados pela familia do modelo (nome contem "kimi" ou "k2") alem do hostname.


6. #17049 - UnicodeDecodeError no scan de processos Windows (wmic)

Arquivo: hermes_cli/gateway.py Problema: wmic emitia saida em encoding local do Windows (cp1252/utf-16), causando UnicodeDecodeError e AttributeError durante parsing. Correcao: O parsing agora usa errors=ignore no decode, tratando bytes invalidos como despreziveis.


7. #17052 - stale reasoning reutilizado quando turn atual nao tem reasoning_content

Arquivo: run_agent.py Problema: Mensagens de assistant com tool_calls e reasoning_content eram reutilizadas indevidamente em turns que nao tinham reasoning_content, causando confabulacoes em provedores como Qwen3.6:27b via Ollama. Correcao: O loop de replay agora detecta quando a mensagem atual e um assistant com tool_calls mas sem reasoning_content, e limpa msg[reasoning_content] e msg[reasoning] para evitar propagacao de estado de reasoning de turns anteriores.


Arquivos Modificados

  • agent/auxiliary_client.py - #17086, #17076
  • hermes_cli/profiles.py - #17080
  • hermes_cli/commands.py - #17054
  • run_agent.py - #17057, #17052
  • hermes_cli/gateway.py - #17049

Notas

  • Este PR contem 8 commits (7 issues + 1 fix de seguranca do upstream relacionado a redaction de secrets)
  • Todos os commits foram feitos na branch fix-7-issues-clean
  • Nenhum push intermediario foi feito - push unico ao final
  • Commits do upstream incluidos para manter o historico completo: #16843, #17041, #17039

Changed files

  • .gitignore (modified, +1/-0)
  • Dockerfile (modified, +6/-2)
  • acp_adapter/entry.py (modified, +11/-0)
  • acp_adapter/server.py (modified, +28/-1)
  • agent/anthropic_adapter.py (modified, +134/-74)
  • agent/auxiliary_client.py (modified, +325/-53)
  • agent/bedrock_adapter.py (modified, +41/-3)
  • agent/context_compressor.py (modified, +113/-5)
  • agent/credential_pool.py (modified, +82/-4)
  • agent/credential_sources.py (modified, +0/-1)
  • agent/error_classifier.py (modified, +32/-0)
  • agent/gemini_cloudcode_adapter.py (modified, +0/-2)
  • agent/gemini_schema.py (modified, +1/-1)
  • agent/google_code_assist.py (modified, +0/-1)
  • agent/google_oauth.py (modified, +3/-3)
  • agent/image_routing.py (added, +236/-0)
  • agent/memory_manager.py (modified, +113/-5)
  • agent/model_metadata.py (modified, +56/-21)
  • agent/nous_rate_guard.py (modified, +144/-1)
  • agent/onboarding.py (added, +191/-0)
  • agent/prompt_builder.py (modified, +38/-0)
  • agent/redact.py (modified, +13/-6)
  • agent/shell_hooks.py (modified, +7/-2)
  • agent/skill_commands.py (modified, +2/-2)
  • agent/title_generator.py (modified, +39/-5)
  • agent/transports/anthropic.py (modified, +1/-7)
  • agent/transports/chat_completions.py (modified, +74/-0)
  • agent/transports/codex.py (modified, +1/-3)
  • cli-config.yaml.example (modified, +28/-8)
  • cli.py (modified, +522/-195)
  • cron/jobs.py (modified, +34/-5)
  • cron/scheduler.py (modified, +39/-5)
  • docker/entrypoint.sh (modified, +9/-7)
  • flake.nix (modified, +1/-0)
  • gateway/channel_directory.py (modified, +67/-14)
  • gateway/config.py (modified, +84/-3)
  • gateway/display_config.py (modified, +3/-1)
  • gateway/mirror.py (modified, +57/-11)
  • gateway/pairing.py (modified, +2/-1)
  • gateway/platforms/__init__.py (modified, +2/-0)
  • gateway/platforms/base.py (modified, +233/-16)
  • gateway/platforms/discord.py (modified, +18/-24)
  • gateway/platforms/email.py (modified, +3/-0)
  • gateway/platforms/feishu_comment.py (modified, +0/-1)
  • gateway/platforms/helpers.py (modified, +11/-2)
  • gateway/platforms/matrix.py (modified, +493/-47)
  • gateway/platforms/mattermost.py (modified, +0/-1)
  • gateway/platforms/qqbot/adapter.py (modified, +2/-7)
  • gateway/platforms/slack.py (modified, +753/-70)
  • gateway/platforms/telegram.py (modified, +138/-14)
  • gateway/platforms/weixin.py (modified, +26/-3)
  • gateway/platforms/yuanbao.py (added, +4754/-0)
  • gateway/platforms/yuanbao_media.py (added, +645/-0)
  • gateway/platforms/yuanbao_proto.py (added, +1209/-0)
  • gateway/platforms/yuanbao_sticker.py (added, +558/-0)
  • gateway/run.py (modified, +1143/-283)
  • gateway/runtime_footer.py (added, +150/-0)
  • gateway/session.py (modified, +16/-21)
  • gateway/stream_consumer.py (modified, +110/-0)
  • gateway/whatsapp_identity.py (modified, +21/-1)
  • hermes_cli/auth.py (modified, +40/-4)
  • hermes_cli/azure_detect.py (modified, +1/-1)
  • hermes_cli/backup.py (modified, +272/-1)
  • hermes_cli/banner.py (modified, +0/-1)
  • hermes_cli/claw.py (modified, +67/-6)
  • hermes_cli/commands.py (modified, +119/-5)
  • hermes_cli/config.py (modified, +322/-29)
  • hermes_cli/debug.py (modified, +13/-7)
  • hermes_cli/dingtalk_auth.py (modified, +0/-1)
  • hermes_cli/doctor.py (modified, +11/-1)
  • hermes_cli/env_loader.py (modified, +2/-1)
  • hermes_cli/fallback_cmd.py (added, +361/-0)
  • hermes_cli/gateway.py (modified, +47/-12)
  • hermes_cli/hooks.py (modified, +1/-2)
  • hermes_cli/main.py (modified, +691/-58)
  • hermes_cli/model_catalog.py (added, +329/-0)
  • hermes_cli/model_switch.py (modified, +55/-6)
  • hermes_cli/models.py (modified, +251/-43)
  • hermes_cli/nous_subscription.py (modified, +16/-8)
  • hermes_cli/oneshot.py (modified, +28/-11)
  • hermes_cli/platforms.py (modified, +1/-0)
  • hermes_cli/plugins.py (modified, +14/-0)
  • hermes_cli/plugins_cmd.py (modified, +0/-1)
  • hermes_cli/profiles.py (modified, +199/-4)
  • hermes_cli/providers.py (modified, +26/-0)
  • hermes_cli/runtime_provider.py (modified, +100/-14)
  • hermes_cli/setup.py (modified, +69/-16)
  • hermes_cli/skills_hub.py (modified, +230/-20)
  • hermes_cli/slack_cli.py (added, +152/-0)
  • hermes_cli/status.py (modified, +3/-2)
  • hermes_cli/timeouts.py (modified, +4/-4)
  • hermes_cli/tips.py (modified, +2/-3)
  • hermes_cli/tools_config.py (modified, +173/-4)
  • hermes_cli/web_server.py (modified, +11/-14)
  • hermes_cli/webhook.py (modified, +2/-2)
  • hermes_logging.py (modified, +3/-4)
  • hermes_state.py (modified, +578/-164)
  • model_tools.py (modified, +45/-10)
  • nix/checks.nix (modified, +30/-3)
  • nix/hermes-agent.nix (added, +186/-0)

PR #17090: fix: resolve 7 identified issues [automated]

Description (problem / solution / changelog)

Resumo

Este PR corrige 7 issues identificados no repositório NousResearch/hermes-agent.


Issues Corrigidos

1. #17086 - custom endpoint com api_mode=anthropic_messages falhava com 404

Arquivo: agent/auxiliary_client.py Problema: Quando provider=custom com api_mode=anthropic_messages e base_url terminando em /anthropic, a funcao _resolve_provider_client() convertia a URL para o formato /v1/messages, causando 404 em provedores Anthropic-compatíveis de terceiros. Correcao: Mantem o path /anthropic quando api_mode=anthropic_messages e o base_url ja termina em /anthropic.


2. #17076 - kimi-coding vision quebrado (404 em analise de imagem)

Arquivo: agent/auxiliary_client.py Problema: kimi-coding nao estava listado em _PROVIDER_VISION_MODELS, entao auxiliary.vision.provider: auto nao conseguia detectar o modelo de visao disponivel para Kimi. Correcao: Adicionado kimi-coding e kimi-coding-cn ao mapa de modelos de visao.


3. #17080 - hermes profile create --clone copiava credenciais exclusivas de plataforma

Arquivo: hermes_cli/profiles.py Problema: O clone de perfil copiava TELEGRAM_BOT_TOKEN, DISCORD_BOT_TOKEN, WEIXIN_TOKEN verbatim. Quando dois perfis iniciam simultaneamente com o mesmo token, o adaptador de plataforma falha durante a aquisicao de lock. Correcao: Define _EXCLUSIVE_PLATFORM_KEYS e _EXCLUSIVE_PLATFORM_CONFIG_PATHS. Credenciais exclusivas sao comentadas em .env e entradas de plataforma sao enabled: false em config.yaml apos clone.


4. #17054 - Slack manifest rejeitava nomes com underscore

Arquivo: hermes_cli/commands.py Problema: _sanitize_slack_name() convertia nomes de comandos como _reload_mcp para Slack mas nao removia o prefixo underscore, causando rejeicao do manifest. Correcao: Adicionada verificacao para pular nomes que começam com underscore antes de adicionar a lista de slash commands.


5. #17057 - custom Kimi-compatible endpoint falhava apos tool call com thinking habilitado

Arquivo: run_agent.py Problema: _needs_kimi_tool_reasoning() só verificava hostnames oficiais (api.kimi.com, moonshot.ai, moonshot.cn). Endpoints Kimi-compatíveis customizados nao eram detectados. Correcao: Ampliada a verificacao para detectar endpoints customizados pela familia do modelo (nome contem "kimi" ou "k2") alem do hostname.


6. #17049 - UnicodeDecodeError no scan de processos Windows (wmic)

Arquivo: hermes_cli/gateway.py Problema: wmic emitia saida em encoding local do Windows (cp1252/utf-16), causando UnicodeDecodeError e AttributeError durante parsing. Correcao: O parsing agora usa errors=ignore no decode, tratando bytes invalidos como despreziveis.


7. #17052 - stale reasoning reutilizado quando turn atual nao tem reasoning_content

Arquivo: run_agent.py Problema: Mensagens de assistant com tool_calls e reasoning_content eram reutilizadas indevidamente em turns que nao tinham reasoning_content, causando confabulacoes em provedores como Qwen3.6:27b via Ollama. Correcao: O loop de replay agora detecta quando a mensagem atual e um assistant com tool_calls mas sem reasoning_content, e limpa msg[reasoning_content] e msg[reasoning] para evitar propagacao de estado de reasoning de turns anteriores.


Arquivos Modificados

  • agent/auxiliary_client.py - #17086, #17076
  • hermes_cli/profiles.py - #17080
  • hermes_cli/commands.py - #17054
  • run_agent.py - #17057, #17052
  • hermes_cli/gateway.py - #17049

Notas

  • Este PR contem 8 commits (7 issues + 1 fix de seguranca do upstream relacionado a redaction de secrets)
  • Todos os commits foram feitos na branch fix-7-issues-clean
  • Nenhum push intermediario foi feito - push unico ao final
  • Commits do upstream incluidos para manter o historico completo: #16843, #17041, #17039

Changed files

  • agent/auxiliary_client.py (modified, +36/-0)
  • agent/redact.py (modified, +6/-3)
  • gateway/run.py (modified, +10/-4)
  • hermes_cli/commands.py (modified, +3/-0)
  • hermes_cli/gateway.py (modified, +22/-8)
  • hermes_cli/main.py (modified, +18/-7)
  • hermes_cli/profiles.py (modified, +141/-2)
  • run_agent.py (modified, +33/-9)
  • tools/terminal_tool.py (modified, +10/-2)
RAW_BUFFERClick to expand / collapse

Problem

The security.redact_secrets feature replaces credential strings with asterisks in both tool output AND command execution contexts. This creates a functional breakage: when the agent needs to use a password in a terminal command (e.g., htpasswd -nb user Passw0rd!), the redaction either:

  1. Masks the password with **** in the command itself, breaking actual command execution
  2. Or the raw password appears in chat output, exposing credentials

Expected Behavior

Secret redaction should protect credentials in:

  • Display output / chat messages (masked)
  • Session logs / transcripts (masked)

But should NOT interfere with:

  • Actual command execution (the real value must be used)
  • File writes to credential files / .env files

Suggested Fix

  1. Add a security.display_redaction_only flag — redact secrets in output/logs but pass real values to subprocesses
  2. At minimum, add clear documentation about how to handle credentials in terminal commands without exposure

extent analysis

TL;DR

Implement a flag to differentiate between display redaction and actual command execution to prevent credential exposure.

Guidance

  • Introduce a security.display_redaction_only flag to control whether secrets are redacted only in display output and logs, or also in command execution.
  • Update the security.redact_secrets feature to respect this new flag, ensuring real values are used in subprocesses.
  • Consider adding documentation on handling credentials in terminal commands securely, as suggested.
  • Review existing use cases for security.redact_secrets to ensure the new flag does not introduce unintended behavior.

Example

No code example is provided due to the lack of specific implementation details in the issue.

Notes

The suggested fix assumes that the current implementation of security.redact_secrets can be modified to respect the new flag. Additional testing may be required to ensure the fix does not introduce regressions.

Recommendation

Apply workaround by implementing the security.display_redaction_only flag, as it directly addresses the issue of credential exposure during command execution while maintaining the security benefits of secret redaction in display output and logs.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING