From aed3c117878371c9f8311bf82a45f17ba2c6c993 Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Sun, 20 Sep 2026 03:56:29 +0000 Subject: [PATCH 01/18] =?UTF-8?q?=F0=9F=9B=A1=EF=B8=8F=20Sentinel:=20[CRIT?= =?UTF-8?q?ICAL]=20=ED=8C=8C=EC=9D=B4=EC=8D=AC=20=EB=A1=9C=EA=B9=85?= =?UTF-8?q?=EC=9D=98=20f-string=EC=97=90=EC=84=9C=20=EB=B0=9C=EC=83=9D?= =?UTF-8?q?=ED=95=98=EB=8A=94=20=EB=A1=9C=EA=B7=B8=20=EC=9D=B8=EC=A0=9D?= =?UTF-8?q?=EC=85=98(CWE-117)=20=EC=B7=A8=EC=95=BD=EC=A0=90=20=ED=95=B4?= =?UTF-8?q?=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .jules/sentinel.md | 6 ++++++ services/analysis-engine/src/bandscope_analysis/cli.py | 2 +- .../src/bandscope_analysis/temporal/analyzer.py | 6 +++--- services/analysis-engine/tests/test_supply_chain_policy.py | 4 +--- 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/.jules/sentinel.md b/.jules/sentinel.md index 34122c2b4..89571646b 100644 --- a/.jules/sentinel.md +++ b/.jules/sentinel.md @@ -28,3 +28,9 @@ **Vulnerability:** The Rust backend (`apps/desktop/src-tauri/src/main.rs`) did not enforce a maximum URL length limit when processing YouTube URLs via `import_youtube_url`. While the frontend enforced `MAX_YOUTUBE_URL_LENGTH = 2000` via the input element, this could be bypassed by an attacker sending requests directly to the Tauri backend API, potentially causing a Denial of Service (DoS) due to unbounded URL parsing and regex matching. **Learning:** Input validation must occur at the entry point of untrusted data on the backend, even if it is also validated on the frontend. Relying solely on frontend validation for constraints like string length can expose the backend to resource exhaustion vulnerabilities. **Prevention:** Always enforce constraints like maximum length, format validation, and sanitization at the earliest possible point on the backend, typically at the API boundary, regardless of frontend safeguards. + +## 2026-09-20 - Prevent Log Forging / Log Injection (CWE-117) via Unsanitized Input in Python Logging +**Vulnerability:** Found unsanitized untrusted user input (`path_str`) logged directly via f-strings (`logger.info(f"Loading and decoding audio: {path_str}")`), allowing attackers to inject newline characters (` +`) to forge fake log entries or exploit log viewers. +**Learning:** Python logging standard practices require using deferred string interpolation (e.g. `logger.info("msg %s", var)`) rather than f-strings to prevent interpolation performance overhead and align with log parsers. However, using `%s` alone does not escape control characters. To prevent Log Forging / CWE-117, untrusted input must be wrapped in `repr()` before passing to the logger. +**Prevention:** When logging untrusted user input in Python, always use deferred interpolation (`%s`) AND wrap the untrusted input in `repr()` (e.g., `logger.info("msg %s", repr(untrusted_input))`) to escape control characters. diff --git a/services/analysis-engine/src/bandscope_analysis/cli.py b/services/analysis-engine/src/bandscope_analysis/cli.py index 6838ee711..91ad3fa77 100644 --- a/services/analysis-engine/src/bandscope_analysis/cli.py +++ b/services/analysis-engine/src/bandscope_analysis/cli.py @@ -90,7 +90,7 @@ def main() -> int: try: temporal_analyzer = TemporalAnalyzer() features = temporal_analyzer.analyze(audio_path) - logging.info(f"Extracted BPM: {features['bpm']}") + logging.info("Extracted BPM: %s", features["bpm"]) except Exception: logging.warning( "Temporal analysis failed for %s; continuing with safe fallback.", diff --git a/services/analysis-engine/src/bandscope_analysis/temporal/analyzer.py b/services/analysis-engine/src/bandscope_analysis/temporal/analyzer.py index 7fe5ae6f7..4a590f57e 100644 --- a/services/analysis-engine/src/bandscope_analysis/temporal/analyzer.py +++ b/services/analysis-engine/src/bandscope_analysis/temporal/analyzer.py @@ -73,7 +73,7 @@ def analyze(self, audio_path: str | Path) -> TemporalFeatures: if not path.exists() or not path.is_file(): raise FileNotFoundError(f"Audio file not found: {path_str}") - logger.info(f"Loading and decoding audio: {path_str}") + logger.info("Loading and decoding audio: %s", repr(path_str)) try: with path.open("rb") as fileobj: @@ -128,7 +128,7 @@ def analyze(self, audio_path: str | Path) -> TemporalFeatures: bpm_val = float(tempo[0]) if isinstance(tempo, np.ndarray) else float(tempo) - logger.info(f"Analysis complete: {bpm_val:.1f} BPM, {len(beat_times)} beats detected.") + logger.info("Analysis complete: %.1f BPM, %d beats detected.", bpm_val, len(beat_times)) return { "bpm": bpm_val, @@ -140,5 +140,5 @@ def analyze(self, audio_path: str | Path) -> TemporalFeatures: } except Exception as e: - logger.error(f"Failed to analyze audio {path_str}: {e}") + logger.error("Failed to analyze audio %s: %s", repr(path_str), e) raise ValueError(f"Temporal analysis failed: {e}") from e diff --git a/services/analysis-engine/tests/test_supply_chain_policy.py b/services/analysis-engine/tests/test_supply_chain_policy.py index 1d8224c5a..6a0853944 100644 --- a/services/analysis-engine/tests/test_supply_chain_policy.py +++ b/services/analysis-engine/tests/test_supply_chain_policy.py @@ -1275,9 +1275,7 @@ def test_workflow_concurrency_cancels_only_superseded_pr_heads() -> None: workflow = (workflows_dir / workflow_name).read_text(encoding="utf-8") assert "concurrency:" in workflow, workflow_name assert "cancel-in-progress: false" in workflow, workflow_name - assert "contents: read" in workflow or "permissions: read-all" in workflow, ( - workflow_name - ) + assert "contents: read" in workflow or "permissions: read-all" in workflow, workflow_name assert "pull_request:" not in (workflows_dir / "release.yml").read_text(encoding="utf-8") From c80c322d87113725db776b2c96b753c1b484847b Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Sun, 20 Sep 2026 23:52:52 +0000 Subject: [PATCH 02/18] =?UTF-8?q?=F0=9F=9B=A1=EF=B8=8F=20Sentinel:=20[CRIT?= =?UTF-8?q?ICAL]=20=ED=8C=8C=EC=9D=B4=EC=8D=AC=20=EB=A1=9C=EA=B9=85?= =?UTF-8?q?=EC=9D=98=20f-string=EC=97=90=EC=84=9C=20=EB=B0=9C=EC=83=9D?= =?UTF-8?q?=ED=95=98=EB=8A=94=20=EB=A1=9C=EA=B7=B8=20=EC=9D=B8=EC=A0=9D?= =?UTF-8?q?=EC=85=98(CWE-117)=20=EC=B7=A8=EC=95=BD=EC=A0=90=20=ED=95=B4?= =?UTF-8?q?=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit From e05d051cb91a94b8ff77883b19f5c68813a9862b Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 23 Sep 2026 07:16:06 +0900 Subject: [PATCH 03/18] repair(security): restore formatter-owner boundary --- services/analysis-engine/tests/test_supply_chain_policy.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/services/analysis-engine/tests/test_supply_chain_policy.py b/services/analysis-engine/tests/test_supply_chain_policy.py index 6a0853944..1d8224c5a 100644 --- a/services/analysis-engine/tests/test_supply_chain_policy.py +++ b/services/analysis-engine/tests/test_supply_chain_policy.py @@ -1275,7 +1275,9 @@ def test_workflow_concurrency_cancels_only_superseded_pr_heads() -> None: workflow = (workflows_dir / workflow_name).read_text(encoding="utf-8") assert "concurrency:" in workflow, workflow_name assert "cancel-in-progress: false" in workflow, workflow_name - assert "contents: read" in workflow or "permissions: read-all" in workflow, workflow_name + assert "contents: read" in workflow or "permissions: read-all" in workflow, ( + workflow_name + ) assert "pull_request:" not in (workflows_dir / "release.yml").read_text(encoding="utf-8") From 68e3f8dcca908002a8176c481206c354abbb7000 Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Wed, 23 Sep 2026 03:09:00 +0000 Subject: [PATCH 04/18] =?UTF-8?q?=F0=9F=9B=A1=EF=B8=8F=20Sentinel:=20[CRIT?= =?UTF-8?q?ICAL]=20=ED=8C=8C=EC=9D=B4=EC=8D=AC=20=EB=A1=9C=EA=B9=85?= =?UTF-8?q?=EC=9D=98=20f-string=EC=97=90=EC=84=9C=20=EB=B0=9C=EC=83=9D?= =?UTF-8?q?=ED=95=98=EB=8A=94=20=EB=A1=9C=EA=B7=B8=20=EC=9D=B8=EC=A0=9D?= =?UTF-8?q?=EC=85=98(CWE-117)=20=EC=B7=A8=EC=95=BD=EC=A0=90=20=ED=95=B4?= =?UTF-8?q?=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- services/analysis-engine/tests/test_supply_chain_policy.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/services/analysis-engine/tests/test_supply_chain_policy.py b/services/analysis-engine/tests/test_supply_chain_policy.py index 1d8224c5a..6a0853944 100644 --- a/services/analysis-engine/tests/test_supply_chain_policy.py +++ b/services/analysis-engine/tests/test_supply_chain_policy.py @@ -1275,9 +1275,7 @@ def test_workflow_concurrency_cancels_only_superseded_pr_heads() -> None: workflow = (workflows_dir / workflow_name).read_text(encoding="utf-8") assert "concurrency:" in workflow, workflow_name assert "cancel-in-progress: false" in workflow, workflow_name - assert "contents: read" in workflow or "permissions: read-all" in workflow, ( - workflow_name - ) + assert "contents: read" in workflow or "permissions: read-all" in workflow, workflow_name assert "pull_request:" not in (workflows_dir / "release.yml").read_text(encoding="utf-8") From a993ac92a8f4729d68e54cf2a926c84eb5dad96b Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 23 Sep 2026 13:05:19 +0900 Subject: [PATCH 05/18] repair(security): preserve temporal owner boundary after formatter drift --- services/analysis-engine/tests/test_supply_chain_policy.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/services/analysis-engine/tests/test_supply_chain_policy.py b/services/analysis-engine/tests/test_supply_chain_policy.py index 6a0853944..1d8224c5a 100644 --- a/services/analysis-engine/tests/test_supply_chain_policy.py +++ b/services/analysis-engine/tests/test_supply_chain_policy.py @@ -1275,7 +1275,9 @@ def test_workflow_concurrency_cancels_only_superseded_pr_heads() -> None: workflow = (workflows_dir / workflow_name).read_text(encoding="utf-8") assert "concurrency:" in workflow, workflow_name assert "cancel-in-progress: false" in workflow, workflow_name - assert "contents: read" in workflow or "permissions: read-all" in workflow, workflow_name + assert "contents: read" in workflow or "permissions: read-all" in workflow, ( + workflow_name + ) assert "pull_request:" not in (workflows_dir / "release.yml").read_text(encoding="utf-8") From 5279fab353899839ab7a9274c074133ee228be28 Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Wed, 23 Sep 2026 09:15:55 +0000 Subject: [PATCH 06/18] =?UTF-8?q?=F0=9F=9B=A1=EF=B8=8F=20Sentinel:=20[CRIT?= =?UTF-8?q?ICAL]=20=ED=8C=8C=EC=9D=B4=EC=8D=AC=20=EB=A1=9C=EA=B9=85?= =?UTF-8?q?=EC=9D=98=20f-string=EC=97=90=EC=84=9C=20=EB=B0=9C=EC=83=9D?= =?UTF-8?q?=ED=95=98=EB=8A=94=20=EB=A1=9C=EA=B7=B8=20=EC=9D=B8=EC=A0=9D?= =?UTF-8?q?=EC=85=98(CWE-117)=20=EC=B7=A8=EC=95=BD=EC=A0=90=20=ED=95=B4?= =?UTF-8?q?=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- services/analysis-engine/tests/test_supply_chain_policy.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/services/analysis-engine/tests/test_supply_chain_policy.py b/services/analysis-engine/tests/test_supply_chain_policy.py index 1d8224c5a..6a0853944 100644 --- a/services/analysis-engine/tests/test_supply_chain_policy.py +++ b/services/analysis-engine/tests/test_supply_chain_policy.py @@ -1275,9 +1275,7 @@ def test_workflow_concurrency_cancels_only_superseded_pr_heads() -> None: workflow = (workflows_dir / workflow_name).read_text(encoding="utf-8") assert "concurrency:" in workflow, workflow_name assert "cancel-in-progress: false" in workflow, workflow_name - assert "contents: read" in workflow or "permissions: read-all" in workflow, ( - workflow_name - ) + assert "contents: read" in workflow or "permissions: read-all" in workflow, workflow_name assert "pull_request:" not in (workflows_dir / "release.yml").read_text(encoding="utf-8") From bfd3ef5804506eeced6f13624561fcab6c524012 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 23 Sep 2026 19:05:02 +0900 Subject: [PATCH 07/18] repair(security): restore temporal preservation owner boundary --- services/analysis-engine/tests/test_supply_chain_policy.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/services/analysis-engine/tests/test_supply_chain_policy.py b/services/analysis-engine/tests/test_supply_chain_policy.py index 6a0853944..1d8224c5a 100644 --- a/services/analysis-engine/tests/test_supply_chain_policy.py +++ b/services/analysis-engine/tests/test_supply_chain_policy.py @@ -1275,7 +1275,9 @@ def test_workflow_concurrency_cancels_only_superseded_pr_heads() -> None: workflow = (workflows_dir / workflow_name).read_text(encoding="utf-8") assert "concurrency:" in workflow, workflow_name assert "cancel-in-progress: false" in workflow, workflow_name - assert "contents: read" in workflow or "permissions: read-all" in workflow, workflow_name + assert "contents: read" in workflow or "permissions: read-all" in workflow, ( + workflow_name + ) assert "pull_request:" not in (workflows_dir / "release.yml").read_text(encoding="utf-8") From 0aaa5beefc63142f61598d3a5f7bf39d3a99949b Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Wed, 23 Sep 2026 15:20:51 +0000 Subject: [PATCH 08/18] =?UTF-8?q?=F0=9F=9B=A1=EF=B8=8F=20Sentinel:=20[CRIT?= =?UTF-8?q?ICAL]=20=ED=8C=8C=EC=9D=B4=EC=8D=AC=20=EB=A1=9C=EA=B9=85?= =?UTF-8?q?=EC=9D=98=20f-string=EC=97=90=EC=84=9C=20=EB=B0=9C=EC=83=9D?= =?UTF-8?q?=ED=95=98=EB=8A=94=20=EB=A1=9C=EA=B7=B8=20=EC=9D=B8=EC=A0=9D?= =?UTF-8?q?=EC=85=98(CWE-117)=20=EC=B7=A8=EC=95=BD=EC=A0=90=20=ED=95=B4?= =?UTF-8?q?=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- services/analysis-engine/tests/test_supply_chain_policy.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/services/analysis-engine/tests/test_supply_chain_policy.py b/services/analysis-engine/tests/test_supply_chain_policy.py index 1d8224c5a..6a0853944 100644 --- a/services/analysis-engine/tests/test_supply_chain_policy.py +++ b/services/analysis-engine/tests/test_supply_chain_policy.py @@ -1275,9 +1275,7 @@ def test_workflow_concurrency_cancels_only_superseded_pr_heads() -> None: workflow = (workflows_dir / workflow_name).read_text(encoding="utf-8") assert "concurrency:" in workflow, workflow_name assert "cancel-in-progress: false" in workflow, workflow_name - assert "contents: read" in workflow or "permissions: read-all" in workflow, ( - workflow_name - ) + assert "contents: read" in workflow or "permissions: read-all" in workflow, workflow_name assert "pull_request:" not in (workflows_dir / "release.yml").read_text(encoding="utf-8") From eedfbc09c41935437098d6b6885be9ac9fda1a62 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 24 Sep 2026 01:00:37 +0900 Subject: [PATCH 09/18] repair(preservation): remove repeated formatter drift from #1243 --- services/analysis-engine/tests/test_supply_chain_policy.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/services/analysis-engine/tests/test_supply_chain_policy.py b/services/analysis-engine/tests/test_supply_chain_policy.py index 6a0853944..1d8224c5a 100644 --- a/services/analysis-engine/tests/test_supply_chain_policy.py +++ b/services/analysis-engine/tests/test_supply_chain_policy.py @@ -1275,7 +1275,9 @@ def test_workflow_concurrency_cancels_only_superseded_pr_heads() -> None: workflow = (workflows_dir / workflow_name).read_text(encoding="utf-8") assert "concurrency:" in workflow, workflow_name assert "cancel-in-progress: false" in workflow, workflow_name - assert "contents: read" in workflow or "permissions: read-all" in workflow, workflow_name + assert "contents: read" in workflow or "permissions: read-all" in workflow, ( + workflow_name + ) assert "pull_request:" not in (workflows_dir / "release.yml").read_text(encoding="utf-8") From 2703e56b46735048e971b0b7fd887b575ea7b703 Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Thu, 24 Sep 2026 07:47:57 +0000 Subject: [PATCH 10/18] =?UTF-8?q?=F0=9F=9B=A1=EF=B8=8F=20Sentinel:=20[CRIT?= =?UTF-8?q?ICAL]=20=ED=8C=8C=EC=9D=B4=EC=8D=AC=20=EB=A1=9C=EA=B9=85?= =?UTF-8?q?=EC=9D=98=20f-string=EC=97=90=EC=84=9C=20=EB=B0=9C=EC=83=9D?= =?UTF-8?q?=ED=95=98=EB=8A=94=20=EB=A1=9C=EA=B7=B8=20=EC=9D=B8=EC=A0=9D?= =?UTF-8?q?=EC=85=98(CWE-117)=20=EC=B7=A8=EC=95=BD=EC=A0=90=20=ED=95=B4?= =?UTF-8?q?=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- services/analysis-engine/tests/test_supply_chain_policy.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/services/analysis-engine/tests/test_supply_chain_policy.py b/services/analysis-engine/tests/test_supply_chain_policy.py index 1d8224c5a..6a0853944 100644 --- a/services/analysis-engine/tests/test_supply_chain_policy.py +++ b/services/analysis-engine/tests/test_supply_chain_policy.py @@ -1275,9 +1275,7 @@ def test_workflow_concurrency_cancels_only_superseded_pr_heads() -> None: workflow = (workflows_dir / workflow_name).read_text(encoding="utf-8") assert "concurrency:" in workflow, workflow_name assert "cancel-in-progress: false" in workflow, workflow_name - assert "contents: read" in workflow or "permissions: read-all" in workflow, ( - workflow_name - ) + assert "contents: read" in workflow or "permissions: read-all" in workflow, workflow_name assert "pull_request:" not in (workflows_dir / "release.yml").read_text(encoding="utf-8") From e28de2359008f764155d37fcab9f04d014646aaf Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 24 Sep 2026 17:02:01 +0900 Subject: [PATCH 11/18] repair(preservation): remove repeated formatter drift from #1243 --- services/analysis-engine/tests/test_supply_chain_policy.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/services/analysis-engine/tests/test_supply_chain_policy.py b/services/analysis-engine/tests/test_supply_chain_policy.py index 6a0853944..1d8224c5a 100644 --- a/services/analysis-engine/tests/test_supply_chain_policy.py +++ b/services/analysis-engine/tests/test_supply_chain_policy.py @@ -1275,7 +1275,9 @@ def test_workflow_concurrency_cancels_only_superseded_pr_heads() -> None: workflow = (workflows_dir / workflow_name).read_text(encoding="utf-8") assert "concurrency:" in workflow, workflow_name assert "cancel-in-progress: false" in workflow, workflow_name - assert "contents: read" in workflow or "permissions: read-all" in workflow, workflow_name + assert "contents: read" in workflow or "permissions: read-all" in workflow, ( + workflow_name + ) assert "pull_request:" not in (workflows_dir / "release.yml").read_text(encoding="utf-8") From 2836bb7bea3cde629cfe0e1c6f44ff94d01a7a7f Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Thu, 24 Sep 2026 08:10:42 +0000 Subject: [PATCH 12/18] =?UTF-8?q?=F0=9F=9B=A1=EF=B8=8F=20Sentinel:=20[CRIT?= =?UTF-8?q?ICAL]=20=ED=8C=8C=EC=9D=B4=EC=8D=AC=20=EB=A1=9C=EA=B9=85?= =?UTF-8?q?=EC=9D=98=20f-string=EC=97=90=EC=84=9C=20=EB=B0=9C=EC=83=9D?= =?UTF-8?q?=ED=95=98=EB=8A=94=20=EB=A1=9C=EA=B7=B8=20=EC=9D=B8=EC=A0=9D?= =?UTF-8?q?=EC=85=98(CWE-117)=20=EC=B7=A8=EC=95=BD=EC=A0=90=20=ED=95=B4?= =?UTF-8?q?=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- services/analysis-engine/tests/test_supply_chain_policy.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/services/analysis-engine/tests/test_supply_chain_policy.py b/services/analysis-engine/tests/test_supply_chain_policy.py index 1d8224c5a..6a0853944 100644 --- a/services/analysis-engine/tests/test_supply_chain_policy.py +++ b/services/analysis-engine/tests/test_supply_chain_policy.py @@ -1275,9 +1275,7 @@ def test_workflow_concurrency_cancels_only_superseded_pr_heads() -> None: workflow = (workflows_dir / workflow_name).read_text(encoding="utf-8") assert "concurrency:" in workflow, workflow_name assert "cancel-in-progress: false" in workflow, workflow_name - assert "contents: read" in workflow or "permissions: read-all" in workflow, ( - workflow_name - ) + assert "contents: read" in workflow or "permissions: read-all" in workflow, workflow_name assert "pull_request:" not in (workflows_dir / "release.yml").read_text(encoding="utf-8") From 77b1e057405a041583ade3affcd8868ee584c0cc Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 24 Sep 2026 17:20:21 +0900 Subject: [PATCH 13/18] repair(preservation): remove repeated formatter drift from #1243 --- services/analysis-engine/tests/test_supply_chain_policy.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/services/analysis-engine/tests/test_supply_chain_policy.py b/services/analysis-engine/tests/test_supply_chain_policy.py index 6a0853944..1d8224c5a 100644 --- a/services/analysis-engine/tests/test_supply_chain_policy.py +++ b/services/analysis-engine/tests/test_supply_chain_policy.py @@ -1275,7 +1275,9 @@ def test_workflow_concurrency_cancels_only_superseded_pr_heads() -> None: workflow = (workflows_dir / workflow_name).read_text(encoding="utf-8") assert "concurrency:" in workflow, workflow_name assert "cancel-in-progress: false" in workflow, workflow_name - assert "contents: read" in workflow or "permissions: read-all" in workflow, workflow_name + assert "contents: read" in workflow or "permissions: read-all" in workflow, ( + workflow_name + ) assert "pull_request:" not in (workflows_dir / "release.yml").read_text(encoding="utf-8") From 2483865a28e56f5a5777bf641dc85fc8a772fb24 Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Thu, 24 Sep 2026 20:01:35 +0000 Subject: [PATCH 14/18] =?UTF-8?q?=F0=9F=9B=A1=EF=B8=8F=20Sentinel:=20[CRIT?= =?UTF-8?q?ICAL]=20=ED=8C=8C=EC=9D=B4=EC=8D=AC=20=EB=A1=9C=EA=B9=85?= =?UTF-8?q?=EC=9D=98=20f-string=EC=97=90=EC=84=9C=20=EB=B0=9C=EC=83=9D?= =?UTF-8?q?=ED=95=98=EB=8A=94=20=EB=A1=9C=EA=B7=B8=20=EC=9D=B8=EC=A0=9D?= =?UTF-8?q?=EC=85=98(CWE-117)=20=EC=B7=A8=EC=95=BD=EC=A0=90=20=ED=95=B4?= =?UTF-8?q?=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- services/analysis-engine/tests/test_supply_chain_policy.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/services/analysis-engine/tests/test_supply_chain_policy.py b/services/analysis-engine/tests/test_supply_chain_policy.py index 1d8224c5a..6a0853944 100644 --- a/services/analysis-engine/tests/test_supply_chain_policy.py +++ b/services/analysis-engine/tests/test_supply_chain_policy.py @@ -1275,9 +1275,7 @@ def test_workflow_concurrency_cancels_only_superseded_pr_heads() -> None: workflow = (workflows_dir / workflow_name).read_text(encoding="utf-8") assert "concurrency:" in workflow, workflow_name assert "cancel-in-progress: false" in workflow, workflow_name - assert "contents: read" in workflow or "permissions: read-all" in workflow, ( - workflow_name - ) + assert "contents: read" in workflow or "permissions: read-all" in workflow, workflow_name assert "pull_request:" not in (workflows_dir / "release.yml").read_text(encoding="utf-8") From b2cf3d232e50d4b14f38bba430c0a52c9f595903 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 25 Sep 2026 05:21:15 +0900 Subject: [PATCH 15/18] preserve(security): remove repeated formatter-owner drift --- services/analysis-engine/tests/test_supply_chain_policy.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/services/analysis-engine/tests/test_supply_chain_policy.py b/services/analysis-engine/tests/test_supply_chain_policy.py index 6a0853944..1d8224c5a 100644 --- a/services/analysis-engine/tests/test_supply_chain_policy.py +++ b/services/analysis-engine/tests/test_supply_chain_policy.py @@ -1275,7 +1275,9 @@ def test_workflow_concurrency_cancels_only_superseded_pr_heads() -> None: workflow = (workflows_dir / workflow_name).read_text(encoding="utf-8") assert "concurrency:" in workflow, workflow_name assert "cancel-in-progress: false" in workflow, workflow_name - assert "contents: read" in workflow or "permissions: read-all" in workflow, workflow_name + assert "contents: read" in workflow or "permissions: read-all" in workflow, ( + workflow_name + ) assert "pull_request:" not in (workflows_dir / "release.yml").read_text(encoding="utf-8") From 7400e79b6718d7a798517b5edba00141bff5d1e8 Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Fri, 25 Sep 2026 08:49:25 +0000 Subject: [PATCH 16/18] =?UTF-8?q?=F0=9F=9B=A1=EF=B8=8F=20Sentinel:=20[CRIT?= =?UTF-8?q?ICAL]=20=ED=8C=8C=EC=9D=B4=EC=8D=AC=20=EB=A1=9C=EA=B9=85?= =?UTF-8?q?=EC=9D=98=20f-string=EC=97=90=EC=84=9C=20=EB=B0=9C=EC=83=9D?= =?UTF-8?q?=ED=95=98=EB=8A=94=20=EB=A1=9C=EA=B7=B8=20=EC=9D=B8=EC=A0=9D?= =?UTF-8?q?=EC=85=98(CWE-117)=20=EC=B7=A8=EC=95=BD=EC=A0=90=20=ED=95=B4?= =?UTF-8?q?=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- services/analysis-engine/tests/test_supply_chain_policy.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/services/analysis-engine/tests/test_supply_chain_policy.py b/services/analysis-engine/tests/test_supply_chain_policy.py index 1d8224c5a..6a0853944 100644 --- a/services/analysis-engine/tests/test_supply_chain_policy.py +++ b/services/analysis-engine/tests/test_supply_chain_policy.py @@ -1275,9 +1275,7 @@ def test_workflow_concurrency_cancels_only_superseded_pr_heads() -> None: workflow = (workflows_dir / workflow_name).read_text(encoding="utf-8") assert "concurrency:" in workflow, workflow_name assert "cancel-in-progress: false" in workflow, workflow_name - assert "contents: read" in workflow or "permissions: read-all" in workflow, ( - workflow_name - ) + assert "contents: read" in workflow or "permissions: read-all" in workflow, workflow_name assert "pull_request:" not in (workflows_dir / "release.yml").read_text(encoding="utf-8") From e66626681d129ce40eafda8ba82baf978bd6c556 Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Fri, 25 Sep 2026 16:49:54 +0000 Subject: [PATCH 17/18] =?UTF-8?q?=F0=9F=9B=A1=EF=B8=8F=20Sentinel:=20[CRIT?= =?UTF-8?q?ICAL]=20=ED=8C=8C=EC=9D=B4=EC=8D=AC=20=EB=A1=9C=EA=B9=85?= =?UTF-8?q?=EC=9D=98=20f-string=EC=97=90=EC=84=9C=20=EB=B0=9C=EC=83=9D?= =?UTF-8?q?=ED=95=98=EB=8A=94=20=EB=A1=9C=EA=B7=B8=20=EC=9D=B8=EC=A0=9D?= =?UTF-8?q?=EC=85=98(CWE-117)=20=EC=B7=A8=EC=95=BD=EC=A0=90=20=ED=95=B4?= =?UTF-8?q?=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit From 5506ca0e6d4c123521933b87a3bf52796f551148 Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Sat, 26 Sep 2026 03:05:48 +0000 Subject: [PATCH 18/18] =?UTF-8?q?=F0=9F=9B=A1=EF=B8=8F=20Sentinel:=20[CRIT?= =?UTF-8?q?ICAL]=20=ED=8C=8C=EC=9D=B4=EC=8D=AC=20=EB=A1=9C=EA=B9=85?= =?UTF-8?q?=EC=9D=98=20f-string=EC=97=90=EC=84=9C=20=EB=B0=9C=EC=83=9D?= =?UTF-8?q?=ED=95=98=EB=8A=94=20=EB=A1=9C=EA=B7=B8=20=EC=9D=B8=EC=A0=9D?= =?UTF-8?q?=EC=85=98(CWE-117)=20=EC=B7=A8=EC=95=BD=EC=A0=90=20=ED=95=B4?= =?UTF-8?q?=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/bandscope_analysis/temporal/analyzer.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/services/analysis-engine/src/bandscope_analysis/temporal/analyzer.py b/services/analysis-engine/src/bandscope_analysis/temporal/analyzer.py index 4a590f57e..eea3f9ce7 100644 --- a/services/analysis-engine/src/bandscope_analysis/temporal/analyzer.py +++ b/services/analysis-engine/src/bandscope_analysis/temporal/analyzer.py @@ -128,7 +128,11 @@ def analyze(self, audio_path: str | Path) -> TemporalFeatures: bpm_val = float(tempo[0]) if isinstance(tempo, np.ndarray) else float(tempo) - logger.info("Analysis complete: %.1f BPM, %d beats detected.", bpm_val, len(beat_times)) + logger.info( + "Analysis complete: %.1f BPM, %d beats detected.", + bpm_val, + len(beat_times), + ) return { "bpm": bpm_val,