From fa0d98726332170ce24e54fb1f70e37434b08310 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 23 Jun 2026 17:23:42 +0000 Subject: [PATCH 1/6] build(deps): bump time from 0.3.49 to 0.3.51 in /apps/desktop/src-tauri Bumps [time](https://github.com/time-rs/time) from 0.3.49 to 0.3.51. - [Release notes](https://github.com/time-rs/time/releases) - [Changelog](https://github.com/time-rs/time/blob/main/CHANGELOG.md) - [Commits](https://github.com/time-rs/time/compare/v0.3.49...v0.3.51) --- updated-dependencies: - dependency-name: time dependency-version: 0.3.51 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- apps/desktop/src-tauri/Cargo.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/desktop/src-tauri/Cargo.lock b/apps/desktop/src-tauri/Cargo.lock index a030d9a0..1938a7c1 100644 --- a/apps/desktop/src-tauri/Cargo.lock +++ b/apps/desktop/src-tauri/Cargo.lock @@ -3175,9 +3175,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.49" +version = "0.3.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "711a53c2d47bbd818258c498c8dbfe186a2526c631495cfe7e078567f86b8469" +checksum = "85c17d80feb7334b40c484e45ed1a5273dfd8bfda537c3be2e74a06a6686f327" dependencies = [ "deranged", "num-conv", @@ -3195,9 +3195,9 @@ checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109" [[package]] name = "time-macros" -version = "0.2.29" +version = "0.2.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71c652a3727a9cbb9a02f707f530b618ce00d0ccd762009c8c23bd191df3c17d" +checksum = "dcef1a61bdb119096e153208ec5cbec23944ce8bca13be5c7f60c634f7403935" dependencies = [ "num-conv", "time-core", From 6727d6e2aad05466663ccaf2d425e082a8783999 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Mon, 29 Jun 2026 07:59:52 +0900 Subject: [PATCH 2/6] chore: refresh stale coverage check From e67c0b5499c648086c3157d513cacefcf069bfba Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Mon, 29 Jun 2026 08:44:45 +0900 Subject: [PATCH 3/6] test: stabilize analysis status updates --- apps/desktop/src/App.test.tsx | 36 +++++++++++++++++++++++++---------- apps/desktop/src/App.tsx | 7 +++++-- 2 files changed, 31 insertions(+), 12 deletions(-) diff --git a/apps/desktop/src/App.test.tsx b/apps/desktop/src/App.test.tsx index 62c12242..ae34f0f9 100644 --- a/apps/desktop/src/App.test.tsx +++ b/apps/desktop/src/App.test.tsx @@ -41,7 +41,7 @@ vi.mock("./lib/analysis", async (importActual) => { }; }); -function succeededResult() { +function succeededResult(overrides: Record = {}) { return { jobId: "job-1", state: "succeeded", @@ -123,7 +123,8 @@ function succeededResult() { headline: "Start with verse entrances before the chorus lift.", focusSections: ["verse"] } - } + }, + ...overrides }; } @@ -172,6 +173,14 @@ function failedJobStatus(jobId: string, message: string) { }); } +async function waitForLatestStatusSubscription() { + await waitFor(() => { + expect(latestStatusSubscription).not.toBeNull(); + }); + + return latestStatusSubscription as NonNullable; +} + describe("App", () => { beforeEach(() => { tauriInvoke.mockReset(); @@ -487,10 +496,11 @@ describe("App", () => { expect(screen.getAllByRole("status").some((status) => /queued for analysis/i.test(status.textContent ?? ""))).toBe(true); }); - const completed = succeededResult(); + const completed = succeededResult({ jobId: "job-unlabeled-status" }); delete (completed as { progressLabel?: string }).progressLabel; + const pushStatus = await waitForLatestStatusSubscription(); act(() => { - latestStatusSubscription?.(completed); + pushStatus(completed); }); await waitFor(() => { @@ -521,8 +531,9 @@ describe("App", () => { ); }); + const pushStatus = await waitForLatestStatusSubscription(); act(() => { - latestStatusSubscription?.(jobStatusResponse({ + pushStatus(jobStatusResponse({ jobId: "job-unlabeled-failure", state: "failed", progressLabel: undefined @@ -558,8 +569,9 @@ describe("App", () => { ); }); + const pushStatus = await waitForLatestStatusSubscription(); act(() => { - latestStatusSubscription?.(jobStatusResponse({ + pushStatus(jobStatusResponse({ jobId: "job-terminal-progress", state: "failed", progressLabel: undefined, @@ -668,8 +680,9 @@ describe("App", () => { fireEvent.click(screen.getByRole("button", { name: /start analysis/i })); await waitFor(() => expect(tauriInvoke).toHaveBeenCalledTimes(3)); + const pushStatus = await waitForLatestStatusSubscription(); act(() => { - latestStatusSubscription?.(succeededResult()); + pushStatus(succeededResult({ jobId: "job-stale-invalid-poll" })); }); await waitFor(() => { expect(screen.getByRole("heading", { name: /Late Night Set/i })).toBeTruthy(); @@ -703,8 +716,9 @@ describe("App", () => { fireEvent.click(screen.getByRole("button", { name: /start analysis/i })); await waitFor(() => expect(tauriInvoke).toHaveBeenCalledTimes(3)); + const pushStatus = await waitForLatestStatusSubscription(); act(() => { - latestStatusSubscription?.(succeededResult()); + pushStatus(succeededResult({ jobId: "job-stale-transport-poll" })); }); await act(async () => { rejectPoll?.(new Error("transport down")); @@ -742,8 +756,9 @@ describe("App", () => { ); }); + const pushStatus = await waitForLatestStatusSubscription(); act(() => { - latestStatusSubscription?.(jobStatusResponse({ + pushStatus(jobStatusResponse({ jobId: "job-push-1", state: "running", progressLabel: "Separating stems... (45%)", @@ -755,8 +770,9 @@ describe("App", () => { expect(screen.getByText(/separating stems/i)).toBeTruthy(); }); + const completeStatus = await waitForLatestStatusSubscription(); act(() => { - latestStatusSubscription?.(succeededResult()); + completeStatus(succeededResult({ jobId: "job-push-1" })); }); await waitFor(() => { expect(screen.getByRole("heading", { name: /Late Night Set/i })).toBeTruthy(); diff --git a/apps/desktop/src/App.tsx b/apps/desktop/src/App.tsx index c965e47a..89099dee 100644 --- a/apps/desktop/src/App.tsx +++ b/apps/desktop/src/App.tsx @@ -198,8 +198,11 @@ export function App() { : defaultRequest; useEffect(() => { - activeJobIdRef.current = jobStatus?.jobId ?? null; - }, [jobStatus?.jobId]); + activeJobIdRef.current = + jobStatus?.state === "queued" || jobStatus?.state === "running" + ? jobStatus.jobId + : null; + }, [jobStatus?.jobId, jobStatus?.state]); /** Documented. */ const applyJobStatus = useCallback((nextStatus: AnalysisJobStatus) => { From 3e626361fba4e5df7aabc8ccd4c45f43cb6a5c4b Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 1 Jul 2026 17:12:17 +0900 Subject: [PATCH 4/6] chore: keep time bump scoped to Cargo lock --- apps/desktop/src/App.test.tsx | 36 ++++++++++------------------------- apps/desktop/src/App.tsx | 7 ++----- 2 files changed, 12 insertions(+), 31 deletions(-) diff --git a/apps/desktop/src/App.test.tsx b/apps/desktop/src/App.test.tsx index a5ab4c94..c039dfba 100644 --- a/apps/desktop/src/App.test.tsx +++ b/apps/desktop/src/App.test.tsx @@ -41,7 +41,7 @@ vi.mock("./lib/analysis", async (importActual) => { }; }); -function succeededResult(overrides: Record = {}) { +function succeededResult() { return { jobId: "job-1", state: "succeeded", @@ -123,8 +123,7 @@ function succeededResult(overrides: Record = {}) { headline: "Start with verse entrances before the chorus lift.", focusSections: ["verse"] } - }, - ...overrides + } }; } @@ -173,14 +172,6 @@ function failedJobStatus(jobId: string, message: string) { }); } -async function waitForLatestStatusSubscription() { - await waitFor(() => { - expect(latestStatusSubscription).not.toBeNull(); - }); - - return latestStatusSubscription as NonNullable; -} - describe("App", () => { beforeEach(() => { tauriInvoke.mockReset(); @@ -513,11 +504,10 @@ describe("App", () => { ); }); - const completed = succeededResult({ jobId: "job-unlabeled-status" }); + const completed = succeededResult(); delete (completed as { progressLabel?: string }).progressLabel; - const pushStatus = await waitForLatestStatusSubscription(); act(() => { - pushStatus(completed); + latestStatusSubscription?.(completed); }); await waitFor(() => { @@ -548,9 +538,8 @@ describe("App", () => { ); }); - const pushStatus = await waitForLatestStatusSubscription(); act(() => { - pushStatus(jobStatusResponse({ + latestStatusSubscription?.(jobStatusResponse({ jobId: "job-unlabeled-failure", state: "failed", progressLabel: undefined @@ -586,9 +575,8 @@ describe("App", () => { ); }); - const pushStatus = await waitForLatestStatusSubscription(); act(() => { - pushStatus(jobStatusResponse({ + latestStatusSubscription?.(jobStatusResponse({ jobId: "job-terminal-progress", state: "failed", progressLabel: undefined, @@ -697,9 +685,8 @@ describe("App", () => { fireEvent.click(screen.getByRole("button", { name: /start analysis/i })); await waitFor(() => expect(tauriInvoke).toHaveBeenCalledTimes(3)); - const pushStatus = await waitForLatestStatusSubscription(); act(() => { - pushStatus(succeededResult({ jobId: "job-stale-invalid-poll" })); + latestStatusSubscription?.(succeededResult()); }); await waitFor(() => { expect(screen.getByRole("heading", { name: /Late Night Set/i })).toBeTruthy(); @@ -733,9 +720,8 @@ describe("App", () => { fireEvent.click(screen.getByRole("button", { name: /start analysis/i })); await waitFor(() => expect(tauriInvoke).toHaveBeenCalledTimes(3)); - const pushStatus = await waitForLatestStatusSubscription(); act(() => { - pushStatus(succeededResult({ jobId: "job-stale-transport-poll" })); + latestStatusSubscription?.(succeededResult()); }); await act(async () => { rejectPoll?.(new Error("transport down")); @@ -773,9 +759,8 @@ describe("App", () => { ); }); - const pushStatus = await waitForLatestStatusSubscription(); act(() => { - pushStatus(jobStatusResponse({ + latestStatusSubscription?.(jobStatusResponse({ jobId: "job-push-1", state: "running", progressLabel: "Separating stems... (45%)", @@ -787,9 +772,8 @@ describe("App", () => { expect(screen.getByText(/separating stems/i)).toBeTruthy(); }); - const completeStatus = await waitForLatestStatusSubscription(); act(() => { - completeStatus(succeededResult({ jobId: "job-push-1" })); + latestStatusSubscription?.(succeededResult()); }); await waitFor(() => { expect(screen.getByRole("heading", { name: /Late Night Set/i })).toBeTruthy(); diff --git a/apps/desktop/src/App.tsx b/apps/desktop/src/App.tsx index 7e6a3672..24f5fb09 100644 --- a/apps/desktop/src/App.tsx +++ b/apps/desktop/src/App.tsx @@ -240,11 +240,8 @@ export function App() { : defaultRequest; useEffect(() => { - activeJobIdRef.current = - jobStatus?.state === "queued" || jobStatus?.state === "running" - ? jobStatus.jobId - : null; - }, [jobStatus?.jobId, jobStatus?.state]); + activeJobIdRef.current = jobStatus?.jobId ?? null; + }, [jobStatus?.jobId]); /** Documented. */ const applyJobStatus = useCallback((nextStatus: AnalysisJobStatus) => { From 5bb0fbcfec6db0d1116bfbd7177f6263213b138f Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 2 Jul 2026 15:20:17 +0900 Subject: [PATCH 5/6] fix: update anyhow for RustSec 2026-0190 --- apps/desktop/src-tauri/Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/desktop/src-tauri/Cargo.lock b/apps/desktop/src-tauri/Cargo.lock index 3a489c97..e02a8898 100644 --- a/apps/desktop/src-tauri/Cargo.lock +++ b/apps/desktop/src-tauri/Cargo.lock @@ -28,9 +28,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.102" +version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" +checksum = "2a4385e2e34eb35d6b3efe798b9eb88096925d87726c0798709bf56d9ed84af3" [[package]] name = "atk" From 28bf79fb568aeb5d50347bbe74cf460c48c21cad Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 2 Jul 2026 19:45:56 +0900 Subject: [PATCH 6/6] fix: document quick-xml advisory exceptions --- apps/desktop/src-tauri/.cargo/audit.toml | 2 ++ apps/desktop/src-tauri/osv-scanner.toml | 8 ++++++++ docs/security/dependency-policy.md | 1 + 3 files changed, 11 insertions(+) diff --git a/apps/desktop/src-tauri/.cargo/audit.toml b/apps/desktop/src-tauri/.cargo/audit.toml index 9fc2a4f3..861e0aa5 100644 --- a/apps/desktop/src-tauri/.cargo/audit.toml +++ b/apps/desktop/src-tauri/.cargo/audit.toml @@ -17,4 +17,6 @@ ignore = [ "RUSTSEC-2025-0100", # unic-ucd-ident: unmaintained "RUSTSEC-2025-0098", # unic-ucd-version: unmaintained "RUSTSEC-2024-0429", # glib 0.18.5: VariantStrIter unsoundness, transitive via Tauri/wry/webkit2gtk/gtk GTK3 stack; remove when upstream drops or patches the chain + "RUSTSEC-2026-0194", # quick-xml 0.39.4: inherited via Tauri/plist and rfd/wayland-scanner; no compatible upstream release has moved both chains to quick-xml >=0.41.0 yet + "RUSTSEC-2026-0195", # quick-xml 0.39.4: same owner chain and removal condition as RUSTSEC-2026-0194 ] diff --git a/apps/desktop/src-tauri/osv-scanner.toml b/apps/desktop/src-tauri/osv-scanner.toml index 16b3b20e..c8fc5e44 100644 --- a/apps/desktop/src-tauri/osv-scanner.toml +++ b/apps/desktop/src-tauri/osv-scanner.toml @@ -65,3 +65,11 @@ reason = "Inherited through the current Tauri GTK3 owner chain and already track [[IgnoredVulns]] id = "RUSTSEC-2024-0429" reason = "glib 0.18.5 VariantStrIter advisory inherited through Tauri/wry/webkit2gtk/gtk; allowed only until upstream drops or patches the chain, with scope guarded by scripts/checks/verify_supply_chain.py." + +[[IgnoredVulns]] +id = "RUSTSEC-2026-0194" +reason = "quick-xml 0.39.4 duplicate-attribute advisory is inherited through Tauri/plist and rfd/wayland-scanner; current compatible upstream crates do not yet allow quick-xml >=0.41.0, and this app does not expose those XML parser paths to untrusted user XML." + +[[IgnoredVulns]] +id = "RUSTSEC-2026-0195" +reason = "quick-xml 0.39.4 namespace-allocation advisory is inherited through the same Tauri/plist and rfd/wayland-scanner owner chain as RUSTSEC-2026-0194; remove once compatible upstream crates move to quick-xml >=0.41.0." diff --git a/docs/security/dependency-policy.md b/docs/security/dependency-policy.md index d3a9680e..d7c7acad 100644 --- a/docs/security/dependency-policy.md +++ b/docs/security/dependency-policy.md @@ -104,6 +104,7 @@ Current controlled exceptions: - No Python vulnerability exceptions are active. `GHSA-5239-wwwm-4pmq` (`Pygments <2.20.0`) was removed by locking `Pygments` to `2.20.0`; the CI `security-audit` workflow must run `pip-audit --local --strict` against the synced `uv` environment without a targeted ignore for that advisory. - Cargo audit warnings for legacy `gtk3` vulnerabilities (e.g. `RUSTSEC-2024-0413`) inherited through Tauri v2 `wry`/`webkit2gtk` integration are explicitly allowed. These are deep framework dependencies with no alternative, so they are documented exceptions and ignored by default. - `RUSTSEC-2024-0429` for `glib 0.18.5` is allowed only for the `VariantStrIter` advisory inherited through the Tauri/wry/webkit2gtk/gtk GTK3 stack. A compatible lockfile refresh can move the desktop stack to `tauri 2.11.3`, `wry 0.55.1`, `tao 0.35.3`, `muda 0.19.3`, and related transitive patches, but it still does not move this stack to patched `glib >=0.20.0`; the exception must remain encoded in repo-controlled audit configuration and guarded by `scripts/checks/verify_supply_chain.py`, and it must be removed when upstream drops or patches the chain. +- `RUSTSEC-2026-0194` and `RUSTSEC-2026-0195` for `quick-xml 0.39.4` are allowed only while the current compatible upstream owner chains still require vulnerable `quick-xml`: `plist 1.9.0` through Tauri, and `wayland-scanner 0.31.10` through Linux `rfd`/Wayland dependencies. `quick-xml >=0.41.0` is patched, but `plist 1.9.0` requires `quick-xml ^0.39.2` and the current `wayland-scanner` release also has no compatible patched path. BandScope does not expose either owner chain as a user-controlled XML ingestion surface; the exception must stay encoded in repo-controlled cargo-audit and OSV configuration, and must be removed once compatible upstream crates publish a patched dependency path. Retired third-party deprecation and advisory signal: