diff --git a/tests/tools/test_voice_mode_playback_env_scrub.py b/tests/tools/test_voice_mode_playback_env_scrub.py new file mode 100644 index 0000000000000..75cc385845526 --- /dev/null +++ b/tests/tools/test_voice_mode_playback_env_scrub.py @@ -0,0 +1,32 @@ +"""Voice-mode system playback must scrub credential env (sibling of #70342).""" + +from unittest.mock import MagicMock, patch + + +def test_play_audio_file_scrubbed_env(tmp_path, monkeypatch): + audio = tmp_path / "t.mp3" + audio.write_bytes(b"ID3fake") + + monkeypatch.setenv("TELEGRAM_BOT_TOKEN", "secret-token") + monkeypatch.setenv("OPENAI_API_KEY", "sk-test") + + captured = {} + + def fake_popen(cmd, **kwargs): + captured["env"] = kwargs.get("env") + proc = MagicMock() + proc.wait.return_value = 0 + return proc + + import tools.voice_mode as vm + + with patch.object(vm, "platform") as plat, patch.object( + vm.shutil, "which", return_value="/usr/bin/ffplay" + ), patch.object(vm.subprocess, "Popen", side_effect=fake_popen): + plat.system.return_value = "Linux" + ok = vm.play_audio_file(str(audio)) + + assert ok is True + assert captured.get("env") is not None + assert "TELEGRAM_BOT_TOKEN" not in captured["env"] + assert "OPENAI_API_KEY" not in captured["env"] diff --git a/tools/voice_mode.py b/tools/voice_mode.py index 9f9636234cf17..68eb59e7c96ac 100644 --- a/tools/voice_mode.py +++ b/tools/voice_mode.py @@ -1510,7 +1510,17 @@ def play_audio_file(file_path: str) -> bool: exe = shutil.which(cmd[0]) if exe: try: - proc = subprocess.Popen(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, stdin=subprocess.DEVNULL) + # Sibling of TTS/STT credential scrub (#70342 / #56332): system + # audio players must not inherit gateway tokens / API keys. + from tools.environments.local import hermes_subprocess_env + + proc = subprocess.Popen( + cmd, + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + stdin=subprocess.DEVNULL, + env=hermes_subprocess_env(inherit_credentials=False), + ) with _playback_lock: _active_playback = proc proc.wait(timeout=300)