From f13b6e149f274551a78ec1cae81184021533c11e Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 19 Aug 2026 22:51:25 -0700 Subject: [PATCH 1/4] test(security): reproduce articulation log leakage --- .../tests/test_articulation.py | 25 ++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/services/analysis-engine/tests/test_articulation.py b/services/analysis-engine/tests/test_articulation.py index 620bb8104..2de3a879e 100644 --- a/services/analysis-engine/tests/test_articulation.py +++ b/services/analysis-engine/tests/test_articulation.py @@ -1,5 +1,6 @@ """Tests for sustained-versus-choppy articulation detection.""" +import logging from typing import Any import numpy as np @@ -99,14 +100,32 @@ def _zero_rms(**_kwargs: Any) -> NDArray[np.float32]: assert analyze_articulation(_sine(1.0), SR) == SAFE_DEFAULT -def test_internal_failure_returns_safe_default(monkeypatch: pytest.MonkeyPatch) -> None: - """No exception escapes: analysis failures return the safe default.""" +def test_internal_failure_returns_payload_safe_default( + monkeypatch: pytest.MonkeyPatch, + caplog: pytest.LogCaptureFixture, +) -> None: + """Unexpected dependency failures stay out of routine articulation logs.""" + sensitive_detail = "/Users/Alice/private-articulation.wav token=super-secret" def _boom(**_kwargs: Any) -> NDArray[np.float32]: - raise RuntimeError("synthetic failure") + raise RuntimeError(sensitive_detail) monkeypatch.setattr(articulation.librosa.onset, "onset_strength", _boom) + caplog.set_level(logging.WARNING, logger=articulation.__name__) + assert analyze_articulation(_sine(1.0), SR) == SAFE_DEFAULT + assert "Articulation analysis failed; returning safe default" in caplog.text + assert "/Users/Alice" not in caplog.text + assert "private-articulation.wav" not in caplog.text + assert "super-secret" not in caplog.text + matching_records = [ + record + for record in caplog.records + if record.name == articulation.__name__ + and record.getMessage().startswith("Articulation analysis failed; returning safe default") + ] + assert len(matching_records) == 1 + assert matching_records[0].exc_info is None def test_empty_stems_dict_returns_empty() -> None: From c514ba1f37a1a4e3da3afb2159e3062760490a31 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 19 Aug 2026 23:13:08 -0700 Subject: [PATCH 2/4] fix(security): redact articulation failure logs --- .../src/bandscope_analysis/roles/articulation.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/services/analysis-engine/src/bandscope_analysis/roles/articulation.py b/services/analysis-engine/src/bandscope_analysis/roles/articulation.py index 7fe118fda..5ce2abff9 100644 --- a/services/analysis-engine/src/bandscope_analysis/roles/articulation.py +++ b/services/analysis-engine/src/bandscope_analysis/roles/articulation.py @@ -18,6 +18,9 @@ - All computations are bounded by the input array sizes. - Fails safe: invalid, empty, or silent audio yields a neutral "mixed" result with zeroed metrics, and no exceptions escape the public API. +- Unexpected dependency failures are logged with only the BandScope-owned + operation and exception class; dependency messages and tracebacks are not + retained in routine logs. """ from __future__ import annotations @@ -138,8 +141,11 @@ def analyze_articulation( "onset_density_per_s": round(onset_density, 3), "duty_cycle": round(duty_cycle, 3), } - except Exception: - logger.warning("Articulation analysis failed; returning safe default", exc_info=True) + except Exception as error: + logger.warning( + "Articulation analysis failed; returning safe default (%s)", + type(error).__name__, + ) return dict(_SAFE_DEFAULT) From 2707ad39e0198857a9f5eedfddea8a36718257e8 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 20 Aug 2026 03:08:57 -0700 Subject: [PATCH 3/4] docs(changelog): record articulation log redaction --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index eea696893..3ea90ba28 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ - Display the analyzed song tempo (BPM) as a badge in the rehearsal workspace. - 각 합주 역할(Role)별 개인 연습 진행도를 0~100% 범위로 기록 및 시각화할 수 있는 연습 진척도(`practiceProgress`) 트래커 기능 추가. UI 컨트롤(슬라이더 및 +/- 버튼)과 한/영 다국어 지원 포함. +### Fixed + +- Redacted dependency-controlled exception messages and tracebacks from routine articulation-analysis failure logs while retaining the operation and exception class for bounded diagnostics. + ## [0.1.3] - 2026-04-29 ### Fixed From 68f337fe835f928b54e734b4442bee2c64634e01 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 26 Aug 2026 06:10:18 -0700 Subject: [PATCH 4/4] test(security): pin articulation failure class logging --- services/analysis-engine/tests/test_articulation.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/services/analysis-engine/tests/test_articulation.py b/services/analysis-engine/tests/test_articulation.py index 2de3a879e..65ced4113 100644 --- a/services/analysis-engine/tests/test_articulation.py +++ b/services/analysis-engine/tests/test_articulation.py @@ -125,6 +125,9 @@ def _boom(**_kwargs: Any) -> NDArray[np.float32]: and record.getMessage().startswith("Articulation analysis failed; returning safe default") ] assert len(matching_records) == 1 + assert matching_records[0].getMessage() == ( + "Articulation analysis failed; returning safe default (RuntimeError)" + ) assert matching_records[0].exc_info is None