Summary
SttEngine::transcribe_via_server still hand-rolls an HTTP response reader with unbounded read_line, no connect/request deadline, no status check, and JSON parse fail-open — the same class of bug fixed for LLM/HA outbound clients in #655/#656, but missed for the whisper-server STT path.
Evidence (crates/genie-core/src/voice/stt.rs)
TcpStream::connect with no timeout
- Header/body loop via
BufReader::read_line with no byte cap (endless line → OOM)
- HTTP status never inspected
serde_json::from_str(...).unwrap_or_else(|_| json!({text: response_body})) turns 5xx HTML / garbage into a spoken transcript
Impact
A hung or malicious/broken whisper-server can wedge the voice loop, OOM the process, or cause error pages to be spoken as user speech.
Proposed fix
Mirror HA/LLM outbound clients:
- Connect + full request under timeouts
- Read via
genie_common::http::read_response + HttpResponseLimits
- Require 2xx
- Strict JSON parse (fail closed)
Acceptance
- Oversized header/body rejected
- Non-2xx and malformed JSON error out (no fake transcript)
- Valid
{text:...} still works
- Focused regression tests
- No prompt / Jetson-4096 impact
Scope
Systems correctness (bounded I/O + fail-closed). Distinct from #808 (subprocess timeouts) and #790 (whisper-cli).
Summary
SttEngine::transcribe_via_serverstill hand-rolls an HTTP response reader with unboundedread_line, no connect/request deadline, no status check, and JSON parse fail-open — the same class of bug fixed for LLM/HA outbound clients in #655/#656, but missed for the whisper-server STT path.Evidence (
crates/genie-core/src/voice/stt.rs)TcpStream::connectwith no timeoutBufReader::read_linewith no byte cap (endless line → OOM)serde_json::from_str(...).unwrap_or_else(|_| json!({text: response_body}))turns 5xx HTML / garbage into a spoken transcriptImpact
A hung or malicious/broken whisper-server can wedge the voice loop, OOM the process, or cause error pages to be spoken as user speech.
Proposed fix
Mirror HA/LLM outbound clients:
genie_common::http::read_response+HttpResponseLimitsAcceptance
{text:...}still worksScope
Systems correctness (bounded I/O + fail-closed). Distinct from #808 (subprocess timeouts) and #790 (whisper-cli).