diff --git a/.Jules/sentinel.md b/.Jules/sentinel.md new file mode 100644 index 00000000..09338614 --- /dev/null +++ b/.Jules/sentinel.md @@ -0,0 +1,4 @@ +## 2023-10-25 - Prevent information leakage from uncaught exceptions +**Vulnerability:** In Python backend tasks (like stem separation workers), a general `except Exception as error` block leaked the raw stack trace or file path contents into the user-facing IPC `runtime_error` message string by calling `str(error)` directly. +**Learning:** Backend worker processes often fail on arbitrary external exceptions that can carry verbose inner details. Leaking these violates the 'Fail securely' rule. +**Prevention:** Replace broad exception casting (like `str(error)`) with generalized static strings (e.g., 'An unexpected error occurred...'). Ensure all explicitly caught errors are intentionally formatted for public reporting, rather than directly coercing exception bodies. diff --git a/services/analysis-engine/src/bandscope_analysis/api.py b/services/analysis-engine/src/bandscope_analysis/api.py index a193bce2..9e99521a 100644 --- a/services/analysis-engine/src/bandscope_analysis/api.py +++ b/services/analysis-engine/src/bandscope_analysis/api.py @@ -840,8 +840,8 @@ def _stem_separation_worker( result_queue.put(("value_error", str(error))) except RuntimeError as error: result_queue.put(("runtime_error", str(error))) - except Exception as error: - result_queue.put(("runtime_error", str(error))) + except Exception: + result_queue.put(("runtime_error", "An unexpected error occurred during stem separation.")) def _multiprocessing_context() -> mp.context.BaseContext: diff --git a/services/analysis-engine/tests/test_api.py b/services/analysis-engine/tests/test_api.py index ea55cba2..bfd6de56 100644 --- a/services/analysis-engine/tests/test_api.py +++ b/services/analysis-engine/tests/test_api.py @@ -848,18 +848,22 @@ def put(self, item: tuple[str, object]) -> None: self.items.append(item) cases = [ - (FileNotFoundError("missing"), "file_not_found"), - (ValueError("bad media"), "value_error"), - (RuntimeError("oom"), "runtime_error"), - (Exception("unexpected"), "runtime_error"), + (FileNotFoundError("missing"), "file_not_found", "missing"), + (ValueError("bad media"), "value_error", "bad media"), + (RuntimeError("oom"), "runtime_error", "oom"), + ( + Exception("unexpected"), + "runtime_error", + "An unexpected error occurred during stem separation.", + ), ] - for error, expected_kind in cases: + for error, expected_kind, expected_msg in cases: fake_queue = FakeQueue() with patch("bandscope_analysis.api.AudioStemSeparator") as separator_class: separator_class.return_value.separate.side_effect = error _stem_separation_worker("/tmp/audio.wav", fake_queue) - assert fake_queue.items == [(expected_kind, str(error))] + assert fake_queue.items == [(expected_kind, expected_msg)] fake_queue = FakeQueue() with patch("bandscope_analysis.api.AudioStemSeparator") as separator_class: