fix(security): keep stream failures out of logs
This commit is contained in:
@@ -1448,7 +1448,7 @@ async def dub_transcribe_stream(
|
||||
async for ev in _gen_body():
|
||||
yield ev
|
||||
except Exception: # noqa: BLE001 — last-resort stream finalizer
|
||||
logger.exception("transcribe stream crashed (job=%r)", job_id)
|
||||
logger.error("Transcription stream failed unexpectedly")
|
||||
from core.public_errors import stream_failure
|
||||
yield _sse_event("error", stream_failure("transcription_failed"))
|
||||
yield _sse_event("done", {})
|
||||
|
||||
@@ -1548,7 +1548,7 @@ async def generate_speech(
|
||||
from core.public_errors import stream_failure
|
||||
yield _line({"type": "error", **stream_failure("invalid_request")})
|
||||
except Exception:
|
||||
logger.exception("Streaming generation failed")
|
||||
logger.error("Streaming generation failed unexpectedly")
|
||||
from core.public_errors import stream_failure
|
||||
yield _line({"type": "error", **stream_failure("generation_failed")})
|
||||
finally:
|
||||
|
||||
@@ -160,7 +160,9 @@ def test_transcribe_stream_surfaces_model_load_failure(tmp_path, monkeypatch):
|
||||
assert "CUDA driver init failed: simulated" in body, body
|
||||
|
||||
|
||||
def test_transcribe_stream_never_closes_without_terminal_event(tmp_path, monkeypatch):
|
||||
def test_transcribe_stream_never_closes_without_terminal_event(
|
||||
tmp_path, monkeypatch, caplog
|
||||
):
|
||||
"""Regression #516: an unanticipated exception INSIDE the stream body (one
|
||||
that escapes the per-chunk handler, e.g. segmentation blowing up) must still
|
||||
end the stream with a terminal `error` then `done` — never a silent
|
||||
@@ -203,7 +205,7 @@ def test_transcribe_stream_never_closes_without_terminal_event(tmp_path, monkeyp
|
||||
# Make the post-chunk segmentation (outside the per-chunk try/except) blow
|
||||
# up — the exact class of "unanticipated escape" the guard must catch.
|
||||
def _boom_segment(*a, **k):
|
||||
raise RuntimeError("segmentation exploded: simulated")
|
||||
raise RuntimeError("API_KEY=dub-secret /home/alice/private-video.mp4")
|
||||
monkeypatch.setattr(dc, "segment_transcript", _boom_segment)
|
||||
# Don't touch the GPU/TTS during the test.
|
||||
monkeypatch.setattr(dc, "offload_tts_for_asr", lambda *a, **k: None)
|
||||
@@ -224,8 +226,10 @@ def test_transcribe_stream_never_closes_without_terminal_event(tmp_path, monkeyp
|
||||
assert "event: error" in body, body
|
||||
assert "transcription_failed" in body, body
|
||||
assert "Transcription failed. Check the selected ASR engine and try again." in body, body
|
||||
assert "segmentation exploded: simulated" not in body, body
|
||||
assert "dub-secret" not in body, body
|
||||
assert "Traceback" not in body, body
|
||||
assert "dub-secret" not in caplog.text
|
||||
assert "/home/alice/private-video.mp4" not in caplog.text
|
||||
err_idx = body.rfind("event: error")
|
||||
done_idx = body.rfind("event: done")
|
||||
assert done_idx > err_idx >= 0, f"error must precede the terminal done: {body}"
|
||||
|
||||
@@ -111,7 +111,9 @@ def _make_deterministic_engine(engine_id="stream-fake", *, delay_s=0.0,
|
||||
def generate(self, text, **kw) -> torch.Tensor:
|
||||
type(self).calls.append((text, time.monotonic()))
|
||||
if fail_on_call is not None and len(type(self).calls) == fail_on_call:
|
||||
raise RuntimeError("engine exploded mid-stream (test)")
|
||||
raise RuntimeError(
|
||||
"TOKEN=stream-secret /home/alice/private-reference.wav"
|
||||
)
|
||||
if delay_s:
|
||||
time.sleep(delay_s)
|
||||
# Deterministic, text-dependent waveform (crc-seeded sine-ish ramp).
|
||||
@@ -277,8 +279,9 @@ def test_stream_final_file_identical_to_classic_output(client, monkeypatch,
|
||||
assert done["duration"] > 0
|
||||
|
||||
|
||||
def test_stream_midstream_error_yields_error_event(client, monkeypatch,
|
||||
no_omnivoice_model):
|
||||
def test_stream_midstream_error_yields_error_event(
|
||||
client, monkeypatch, no_omnivoice_model, caplog
|
||||
):
|
||||
"""Chunk 2 blowing up must surface as an in-band error event AFTER the
|
||||
already-delivered chunk — no done, no history row, no saved file."""
|
||||
fake = _make_deterministic_engine(fail_on_call=2)
|
||||
@@ -297,8 +300,10 @@ def test_stream_midstream_error_yields_error_event(client, monkeypatch,
|
||||
error = events[-1][0]
|
||||
assert error["code"] == "generation_failed"
|
||||
assert error["detail"] == "Generation failed. Check the selected engine and try again."
|
||||
assert "engine exploded" not in repr(error)
|
||||
assert "stream-secret" not in repr(error)
|
||||
assert "Traceback" not in repr(error)
|
||||
assert "stream-secret" not in caplog.text
|
||||
assert "/home/alice/private-reference.wav" not in caplog.text
|
||||
|
||||
after_ids = {h["id"] for h in client.get("/history").json()}
|
||||
assert after_ids == before_ids # nothing was recorded for the failure
|
||||
|
||||
Reference in New Issue
Block a user