mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-29 18:46:59 +00:00
fix(stt): route transcription inputs through the shared read guard
`transcribe_audio` reads a local file and hands it to the configured STT provider — for the hosted providers (Groq, OpenAI, Mistral, xAI, ElevenLabs) that ships the file's bytes to a third-party API. The same local-input read guard was added to image-gen (587be5b5b) and xAI video-gen (104232979) to keep the agent from feeding credential/secret stores to a provider, but STT was missed. Call `get_read_block_error(file_path)` at the top of `transcribe_audio`, before validation/dispatch, so a `.env`, `auth.json`, `.anthropic_oauth.json`, `mcp-tokens/`, etc. is refused up front instead of being transcribed (and, for hosted providers, exfiltrated). This is defense-in-depth, not a security boundary — the guard's own message says so — but it restores parity with the image/video-gen tools. Regression test: a `.env` file is refused with the shared read-guard message before any provider dispatch (mutation-verified).
This commit is contained in:
parent
37d0b6c81a
commit
38bb193f38
2 changed files with 36 additions and 1 deletions
|
|
@ -2317,7 +2317,9 @@ class TestLocalBaseUrlNoApiKey:
|
|||
assert not _is_local_or_private_url("")
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# =====================================================================
|
||||
|
||||
|
||||
# CAF (iMessage voice note) conversion tests
|
||||
# ============================================================================
|
||||
|
||||
|
|
@ -2456,3 +2458,27 @@ class TestCafConversion:
|
|||
|
||||
assert result["success"] is True
|
||||
mock_convert.assert_not_called()
|
||||
=======
|
||||
class TestTranscribeCredentialReadGuard:
|
||||
"""transcribe_audio must refuse credential/secret stores before dispatch."""
|
||||
|
||||
def test_transcribe_audio_blocks_credential_read(self, tmp_path):
|
||||
"""A ``.env`` (secret-bearing) file is refused up front, so its
|
||||
plaintext is never shipped to an external STT provider — mirroring the
|
||||
read guard added to image-gen (587be5b5b) and xAI video-gen
|
||||
(104232979)."""
|
||||
from tools.transcription_tools import transcribe_audio
|
||||
from agent.file_safety import get_read_block_error
|
||||
|
||||
env_file = tmp_path / ".env"
|
||||
env_file.write_text("OPENAI_API_KEY=sk-secret\n")
|
||||
|
||||
expected = get_read_block_error(str(env_file))
|
||||
assert expected, "test setup: a .env file should be read-blocked"
|
||||
|
||||
result = transcribe_audio(str(env_file))
|
||||
|
||||
assert result["success"] is False
|
||||
# The error is the shared read-guard message, not an audio-validation
|
||||
# or provider error — proving the guard fired before dispatch.
|
||||
assert result["error"] == expected
|
||||
|
|
|
|||
|
|
@ -2144,6 +2144,15 @@ def _transcribe_prepared_audio(file_path: str, model: Optional[str] = None) -> D
|
|||
- "error" (str, optional): Error message if success is False
|
||||
- "provider" (str, optional): Which provider was used
|
||||
"""
|
||||
# Refuse to feed a credential / secret store (auth.json, .env, OAuth
|
||||
# tokens, mcp-tokens/, ...) to an STT provider: an external provider would
|
||||
# ship its plaintext contents to a third-party API. Mirrors the local-input
|
||||
# read guard added to image-gen (587be5b5b) and xAI video-gen (104232979).
|
||||
from agent.file_safety import get_read_block_error
|
||||
blocked = get_read_block_error(file_path)
|
||||
if blocked:
|
||||
return {"success": False, "transcript": "", "error": blocked}
|
||||
|
||||
# Apply common path validation before provider resolution so invalid files
|
||||
# cannot trigger provider setup or lazy installation. The remote-upload
|
||||
# size cap is enforced separately below, only for non-local providers.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue