From f9ba9357487ce93b10abcc123107c4e3068385b3 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 19 Aug 2026 17:49:10 -0700 Subject: [PATCH 1/4] test(security): reproduce range-analysis log leakage --- .../tests/test_range_logging_privacy.py | 94 +++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 services/analysis-engine/tests/test_range_logging_privacy.py diff --git a/services/analysis-engine/tests/test_range_logging_privacy.py b/services/analysis-engine/tests/test_range_logging_privacy.py new file mode 100644 index 000000000..b618d9931 --- /dev/null +++ b/services/analysis-engine/tests/test_range_logging_privacy.py @@ -0,0 +1,94 @@ +"""Privacy regressions for range-analysis safe-failure logging.""" + +from unittest.mock import patch + +import librosa +import numpy as np +import pytest + +from bandscope_analysis.ranges.pitch_tracker import PitchTracker +from bandscope_analysis.ranges.pressure import ( + analyze_range_pressure, + analyze_range_pressure_from_audio, +) + +_DEFAULT_PRESSURE = { + "range_semitones": 0, + "tessitura_center": "", + "time_in_top_range": 0.0, + "longest_high_sustain_seconds": 0.0, + "pressure_level": "low", +} + + +def _assert_payload_safe_log( + caplog: pytest.LogCaptureFixture, + operation: str, + filename: str, +) -> None: + """Require the operation while rejecting path, file, and secret payloads.""" + assert operation in caplog.text + assert "/Users/Alice" not in caplog.text + assert filename not in caplog.text + assert "super-secret" not in caplog.text + + +def test_pitch_tracker_parameter_error_log_is_payload_safe( + caplog: pytest.LogCaptureFixture, +) -> None: + """pYIN parameter failures keep dependency payloads out of tracker logs.""" + tracker = PitchTracker() + audio = np.ones(2048, dtype=np.float64) + detail = "/Users/Alice/private-pitch.wav token=super-secret" + + with patch( + "bandscope_analysis.ranges.pitch_tracker.librosa.pyin", + side_effect=librosa.util.exceptions.ParameterError(detail), + ): + result = tracker.track(audio, sr=22050) + + assert result == {"lowest_note": None, "highest_note": None, "confidence": "low"} + _assert_payload_safe_log(caplog, "pYIN failed", "private-pitch.wav") + + +def test_range_pressure_internal_failure_log_is_payload_safe( + monkeypatch: pytest.MonkeyPatch, + caplog: pytest.LogCaptureFixture, +) -> None: + """Generic range-pressure failures retain no traceback or message payload.""" + detail = "/Users/Alice/private-pressure.wav token=super-secret" + + def _boom(*_args: object, **_kwargs: object) -> dict[str, object]: + raise RuntimeError(detail) + + monkeypatch.setattr("bandscope_analysis.ranges.pressure._analyze", _boom) + values = np.ones(1, dtype=np.float64) + result = analyze_range_pressure(values, np.ones(1, dtype=bool), values) + + assert result == _DEFAULT_PRESSURE + _assert_payload_safe_log( + caplog, + "Range-pressure analysis failed; returning default", + "private-pressure.wav", + ) + + +def test_range_pressure_pyin_failure_log_is_payload_safe( + caplog: pytest.LogCaptureFixture, +) -> None: + """Audio-wrapper pYIN failures keep dependency payloads out of routine logs.""" + audio = np.ones(2048, dtype=np.float64) + detail = "/Users/Alice/private-pressure-audio.wav token=super-secret" + + with patch( + "bandscope_analysis.ranges.pressure.librosa.pyin", + side_effect=librosa.util.exceptions.ParameterError(detail), + ): + result = analyze_range_pressure_from_audio(audio, sr=22050) + + assert result == _DEFAULT_PRESSURE + _assert_payload_safe_log( + caplog, + "pYIN failed during range-pressure analysis", + "private-pressure-audio.wav", + ) From 4283683d746d91fbaf79ad93cc6c04f1bc7686d3 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 19 Aug 2026 17:50:23 -0700 Subject: [PATCH 2/4] fix(security): bound pitch tracker failure logs --- .../src/bandscope_analysis/ranges/pitch_tracker.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/services/analysis-engine/src/bandscope_analysis/ranges/pitch_tracker.py b/services/analysis-engine/src/bandscope_analysis/ranges/pitch_tracker.py index 51f9f6526..b111a0d9a 100644 --- a/services/analysis-engine/src/bandscope_analysis/ranges/pitch_tracker.py +++ b/services/analysis-engine/src/bandscope_analysis/ranges/pitch_tracker.py @@ -25,6 +25,8 @@ class PitchTracker: - No file I/O, network access, or shell execution. - Bounded computation: frame count capped by input duration. - Safe failure: exceptions in pYIN return empty range with low confidence. + - pYIN failures log only the operation and exception class; dependency + messages and traceback payloads stay out of routine logs. """ def track(self, y: np.ndarray, sr: int = 22050) -> TrackedPitchRange: @@ -50,8 +52,8 @@ def track(self, y: np.ndarray, sr: int = 22050) -> TrackedPitchRange: try: f0, voiced_flag, voiced_probs = librosa.pyin(y, fmin=fmin, fmax=fmax, sr=sr) - except librosa.util.exceptions.ParameterError as e: - logger.warning("pYIN failed: %s", e) + except librosa.util.exceptions.ParameterError as error: + logger.warning("pYIN failed during pitch tracking: %s", type(error).__name__) return {"lowest_note": None, "highest_note": None, "confidence": "low"} # Filter f0 to only keep voiced frames From eab8ff66718539d20aaaefac98a943e4f6d48ced Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 19 Aug 2026 18:04:42 -0700 Subject: [PATCH 3/4] fix(security): redact range-pressure failure logs --- .../src/bandscope_analysis/ranges/pressure.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/services/analysis-engine/src/bandscope_analysis/ranges/pressure.py b/services/analysis-engine/src/bandscope_analysis/ranges/pressure.py index 373f9fea5..eaa013ab5 100644 --- a/services/analysis-engine/src/bandscope_analysis/ranges/pressure.py +++ b/services/analysis-engine/src/bandscope_analysis/ranges/pressure.py @@ -23,6 +23,8 @@ - Bounded computation: all work is linear in the number of input frames. - Safe failure: malformed, empty, or fully-unvoiced input yields a neutral default result. No exceptions escape the public functions. +- Unexpected failures log only the operation and exception class; dependency + messages and traceback payloads stay out of routine logs. """ import logging @@ -184,8 +186,11 @@ def analyze_range_pressure( """ try: return _analyze(f0_hz, voiced_flag, times) - except Exception: - logger.warning("Range-pressure analysis failed; returning default", exc_info=True) + except Exception as error: + logger.warning( + "Range-pressure analysis failed; returning default: %s", + type(error).__name__, + ) return _empty_result() @@ -212,8 +217,11 @@ def analyze_range_pressure_from_audio(audio: np.ndarray, sr: int = 22050) -> Ran fmax = float(librosa.note_to_hz("C8")) try: f0, voiced_flag, _voiced_probs = librosa.pyin(audio, fmin=fmin, fmax=fmax, sr=sr) - except librosa.util.exceptions.ParameterError: - logger.warning("pYIN failed during range-pressure analysis", exc_info=True) + except librosa.util.exceptions.ParameterError as error: + logger.warning( + "pYIN failed during range-pressure analysis: %s", + type(error).__name__, + ) return _empty_result() times = librosa.times_like(f0, sr=sr) From 9c64ca3cd3bd2669d012f86b7f3611e7f2463583 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 19 Aug 2026 18:05:16 -0700 Subject: [PATCH 4/4] docs(changelog): record range log redaction --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index eea696893..40aa7dc7e 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 + +- Kept dependency-controlled pYIN and range-pressure failure payloads and traceback paths out of routine range-analysis logs while preserving bounded operation and exception-class diagnostics. + ## [0.1.3] - 2026-04-29 ### Fixed