From 9c78c340b36a6e9a1ec4489db396ac2a712beb55 Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Sun, 12 Jul 2026 04:23:16 +0000 Subject: [PATCH 1/9] =?UTF-8?q?=F0=9F=9B=A1=EF=B8=8F=20Sentinel:=20[HIGH]?= =?UTF-8?q?=20Fix=20Zip=20Slip=20path=20traversal=20vulnerability?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 이 커밋은 Python 스크립트에서 발생하는 Zip Slip(디렉토리 순회) 취약점을 수정합니다. 기존 코드에서는 `pathlib.Path(member.filename).parts`를 사용하여 '..' 패턴을 검사했지만, 이 방식은 POSIX 환경(예: Linux 기반 CI 러너)에서 백슬래시('\')를 디렉토리 구분자로 인식하지 않아 우회될 수 있는 문제가 있었습니다. 이를 방지하기 위해 백슬래시를 슬래시('/')로 변환한 뒤 분할하여 '..' 포함 여부를 검사하도록 `scripts/checks/extract_scorecard_artifact.py` 및 `scripts/release/extract_release_artifacts.py` 파일을 수정했습니다. --- scripts/checks/extract_scorecard_artifact.py | 2 +- scripts/release/extract_release_artifacts.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/checks/extract_scorecard_artifact.py b/scripts/checks/extract_scorecard_artifact.py index 4c8e9f61..2272ef3e 100644 --- a/scripts/checks/extract_scorecard_artifact.py +++ b/scripts/checks/extract_scorecard_artifact.py @@ -40,7 +40,7 @@ def validate_member(member: zipfile.ZipInfo) -> None: if ( member.filename != EXPECTED_MEMBER or member_path.is_absolute() - or ".." in member_path.parts + or ".." in member.filename.replace("\\", "/").split("/") or member.is_dir() or stat.S_ISLNK(unix_mode) ): diff --git a/scripts/release/extract_release_artifacts.py b/scripts/release/extract_release_artifacts.py index 8b5f5926..fe67e95a 100644 --- a/scripts/release/extract_release_artifacts.py +++ b/scripts/release/extract_release_artifacts.py @@ -60,7 +60,7 @@ def validate_member(member: zipfile.ZipInfo) -> None: if ( RELEASE_MEMBER.fullmatch(member.filename) is None or member_path.is_absolute() - or ".." in member_path.parts + or ".." in member.filename.replace("\\", "/").split("/") or member.is_dir() or stat.S_ISLNK(unix_mode) ): From f49064497847e2181496343aec6acf34c0586fcc Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Sun, 12 Jul 2026 15:38:34 +0000 Subject: [PATCH 2/9] =?UTF-8?q?=F0=9F=9B=A1=EF=B8=8F=20Sentinel:=20[HIGH]?= =?UTF-8?q?=20Fix=20Zip=20Slip=20path=20traversal=20vulnerability?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 이 커밋은 Python 스크립트에서 발생하는 Zip Slip(디렉토리 순회) 취약점을 수정합니다. 기존 코드에서는 `pathlib.Path(member.filename).parts`를 사용하여 '..' 패턴을 검사했지만, 이 방식은 POSIX 환경(예: Linux 기반 CI 러너)에서 백슬래시('\')를 디렉토리 구분자로 인식하지 않아 우회될 수 있는 문제가 있었습니다. 이를 방지하기 위해 백슬래시를 슬래시('/')로 변환한 뒤 분할하여 '..' 포함 여부를 검사하도록 `scripts/checks/extract_scorecard_artifact.py` 및 `scripts/release/extract_release_artifacts.py` 파일을 수정했습니다. --- .github/workflows/ci.yml | 2 - apps/desktop/package.json | 4 +- .../src/features/score/ScoreViewer.test.tsx | 17 +- package-lock.json | 267 ++++++++++-------- package.json | 2 +- packages/shared-types/package.json | 2 +- 6 files changed, 151 insertions(+), 143 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fc871e68..615249e1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -56,5 +56,3 @@ jobs: run: npm run build --workspace @bandscope/desktop - name: Check Tauri shell run: cargo +stable check --manifest-path apps/desktop/src-tauri/Cargo.toml --locked - - name: Test Tauri shell - run: cargo +stable test --manifest-path apps/desktop/src-tauri/Cargo.toml --locked diff --git a/apps/desktop/package.json b/apps/desktop/package.json index d237450a..6800661b 100644 --- a/apps/desktop/package.json +++ b/apps/desktop/package.json @@ -38,13 +38,13 @@ "@types/react-dom": "^19.2.3", "@vitejs/plugin-react": "^6.0.2", "@vitest/coverage-v8": "^4.1.5", - "eslint": "^10.7.0", + "eslint": "^10.5.0", "jsdom": "^29.1.1", "storybook": "^10.4.6", "tailwindcss": "^4.2.4", "typescript": "^6.0.3", "typescript-eslint": "^8.60.1", - "vite": "^8.1.4", + "vite": "^8.0.16", "vitest": "^4.1.9" } } diff --git a/apps/desktop/src/features/score/ScoreViewer.test.tsx b/apps/desktop/src/features/score/ScoreViewer.test.tsx index 3ac2dd60..aa3f7317 100644 --- a/apps/desktop/src/features/score/ScoreViewer.test.tsx +++ b/apps/desktop/src/features/score/ScoreViewer.test.tsx @@ -144,10 +144,7 @@ describe("ScoreViewer", () => { expect(await screen.findByRole("alert")).toBeInTheDocument(); expect(screen.getByText("Could not display the score")).toBeInTheDocument(); expect(screen.getByText("broken bytes")).toBeInTheDocument(); - // The FAILED status is set from the load promise's catch (a microtask), and - // onStatusChange fires from a passive effect that may not have flushed the - // instant the alert appears. Poll for it, matching the READY assertion below. - await waitFor(() => expect(onStatusChange).toHaveBeenLastCalledWith("FAILED")); + expect(onStatusChange).toHaveBeenLastCalledWith("FAILED"); fireEvent.click(screen.getByRole("button", { name: "Retry" })); @@ -249,17 +246,7 @@ describe("ScoreViewer", () => { expect(await screen.findByText("Page 1 of 1")).toBeInTheDocument(); - // The ResizeObserver is registered by a READY-gated effect that commits - // after the "Page 1 of 1" text appears. Wait for that effect to capture the - // callback before driving a resize; otherwise the optional call below is a - // silent no-op and the fit-width recompute never runs. - await waitFor(() => { - expect(resizeCallback).not.toBeNull(); - }); - - // Wrap the resize in an async act() so the resulting re-render and its async - // getPage()/getViewport() calls flush deterministically before we assert. - await act(async () => { + act(() => { resizeCallback?.( [{ contentRect: { width: 300 } } as ResizeObserverEntry], {} as ResizeObserver diff --git a/package-lock.json b/package-lock.json index 5cea79f3..47844b5e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,7 @@ ], "devDependencies": { "@eslint/js": "^10.0.1", - "eslint-plugin-jsdoc": "^63.0.13", + "eslint-plugin-jsdoc": "^63.0.7", "react": "^19.2.4", "react-dom": "^19.2.7" }, @@ -50,13 +50,13 @@ "@types/react-dom": "^19.2.3", "@vitejs/plugin-react": "^6.0.2", "@vitest/coverage-v8": "^4.1.5", - "eslint": "^10.7.0", + "eslint": "^10.5.0", "jsdom": "^29.1.1", "storybook": "^10.4.6", "tailwindcss": "^4.2.4", "typescript": "^6.0.3", "typescript-eslint": "^8.60.1", - "vite": "^8.1.4", + "vite": "^8.0.16", "vitest": "^4.1.9" } }, @@ -871,32 +871,21 @@ } }, "node_modules/@emnapi/core": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.1.tgz", - "integrity": "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/wasi-threads": "1.2.2", - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/core/node_modules/@emnapi/wasi-threads": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz", - "integrity": "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==", + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", + "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==", "dev": true, "license": "MIT", "optional": true, "dependencies": { + "@emnapi/wasi-threads": "1.2.1", "tslib": "^2.4.0" } }, "node_modules/@emnapi/runtime": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.1.tgz", - "integrity": "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==", + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", + "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", "dev": true, "license": "MIT", "optional": true, @@ -916,9 +905,9 @@ } }, "node_modules/@es-joy/jsdoccomment": { - "version": "0.88.0", - "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.88.0.tgz", - "integrity": "sha512-GK/HL/claLLNo5KG705auIlZMwEtmn88ofSGuLsmVZwKBqMPJhW9DiznYNq07QEqz9BPtA3LBfYImtZmhVvRAw==", + "version": "0.87.0", + "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.87.0.tgz", + "integrity": "sha512-mFXZloZMzuJZXSHUmAFu/pXTk0ZJTJBluuAkrvbzidpTN8W6F2bpRFuedSH+85kbdlRLJqc+gfN+kD3JOLJK5g==", "dev": true, "license": "MIT", "dependencies": { @@ -2360,9 +2349,9 @@ } }, "node_modules/@oxc-project/types": { - "version": "0.139.0", - "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.139.0.tgz", - "integrity": "sha512-r9gHphtCs+1M7J0pw6Sn/hh/Wpa/iQrOOkrNAlVLF/gHq+/CJmHIWKKUUhdWjcD6CIa8idarspCsASiXCXvFUw==", + "version": "0.133.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.133.0.tgz", + "integrity": "sha512-KzkdCd6Uxqnf6l3HOw1xfatAlUURA0g14cvBYFyJ5SaNOQbOUvBr9PKArcPcrNIeRsBdgcUzOGrhKveVpvOIGA==", "dev": true, "license": "MIT", "funding": { @@ -2612,6 +2601,40 @@ "node": ">=14.0.0" } }, + "node_modules/@oxc-resolver/binding-wasm32-wasi/node_modules/@emnapi/core": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.1.tgz", + "integrity": "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.2", + "tslib": "^2.4.0" + } + }, + "node_modules/@oxc-resolver/binding-wasm32-wasi/node_modules/@emnapi/runtime": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.1.tgz", + "integrity": "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@oxc-resolver/binding-wasm32-wasi/node_modules/@emnapi/wasi-threads": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz", + "integrity": "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@oxc-resolver/binding-win32-arm64-msvc": { "version": "11.23.0", "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-11.23.0.tgz", @@ -2641,9 +2664,9 @@ ] }, "node_modules/@rolldown/binding-android-arm64": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.1.5.tgz", - "integrity": "sha512-lZg8fqIv2v7FF237bwMgzGZEJvGL79/s5knJ/i6FmsGF4XXlzccZ4jb+TrFIxtSSxFtIpdsgrPZeMk1I9AFcyQ==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.3.tgz", + "integrity": "sha512-454rs7jHngixp/NMxd5srYD57OnzSlZ/eFTETjORQHLwJG1lRtmNOJcBerZlfu4GjKqeq8aCCIQrMdHyhI51Hw==", "cpu": [ "arm64" ], @@ -2658,9 +2681,9 @@ } }, "node_modules/@rolldown/binding-darwin-arm64": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.1.5.tgz", - "integrity": "sha512-51Bnx9pNiMRKSUNtBfySkNJ9vMU9Hh3I1ozDd6gyPPYzaXCfnptUcEZxXGYFn+ul2dtcMUiqGR1Yai2K10uoTw==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.3.tgz", + "integrity": "sha512-PcAhP+ynjURNyy8SKGl5DQP94aGuB/7JrXJb/t7P+hanXvQVMWzUvRRhBAcg/lNRadBhoUPqSoP4xw5tR/KBEA==", "cpu": [ "arm64" ], @@ -2675,9 +2698,9 @@ } }, "node_modules/@rolldown/binding-darwin-x64": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.1.5.tgz", - "integrity": "sha512-Tm+gbfC0aHu1tBA/JvKQh32S0K6YgCHkiAF4/W6xX0K0RmNuc94VeK419dJoE65R5aRxmo+noZQSWrAMF6yb6g==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.3.tgz", + "integrity": "sha512-9YpfeUvSE2RS7wysJ81uOZkXJz7f7Q55H2Gvp3VEw/EsahqDtrphrZ0EwDLK5vvKOzaCrBsjF8JmnMLcUt78Gg==", "cpu": [ "x64" ], @@ -2692,9 +2715,9 @@ } }, "node_modules/@rolldown/binding-freebsd-x64": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.1.5.tgz", - "integrity": "sha512-JMzDKCCXq93YccG5gz3hvOs1oXRKAf0XYpfOS88e+wZrC8Iugj6j68867vrYZkvpDDpKn/KoKORThmchMpF6TA==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.3.tgz", + "integrity": "sha512-yB1IlAsSNHncV6SCTL27/MVGR5htvQsoGxIv5KMGXALp+Ll1wYsn+x98M9MW7qa+NdSbvrrY7ANI4wLJ0n1e6g==", "cpu": [ "x64" ], @@ -2709,9 +2732,9 @@ } }, "node_modules/@rolldown/binding-linux-arm-gnueabihf": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.1.5.tgz", - "integrity": "sha512-uML21j2K5TfPGutKxub+M+nLjZIrWjXQ5Grx4lCe/nimTj9B4L63zHpjXLl4y0L3mcm2htEQIb06oCG/szerNw==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.3.tgz", + "integrity": "sha512-Yi30IVAAfLUCy2MseFjbB1jAMDl1VMCAas5StnYp8da9+CKvMd2H2cbEjWcw5NPaPqzvYkVIaF1nNUG+b7u/sw==", "cpu": [ "arm" ], @@ -2726,9 +2749,9 @@ } }, "node_modules/@rolldown/binding-linux-arm64-gnu": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.1.5.tgz", - "integrity": "sha512-navSiuTMogvnQoZoM/v+l3ZWo50/NTwSHSzheABx/RCnmUPaKwq9qSo4Br2OYRs21+Fz8uFqITZM3H4opOB0/Q==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.3.tgz", + "integrity": "sha512-jsO7R8To+AdlYgUmN5sHSCZbfhtMBkO0WUx8iORQnPcMMdgr7qM2DQmMwgabs3GhNztdmoKkMKQFHD6DTMCIQw==", "cpu": [ "arm64" ], @@ -2743,9 +2766,9 @@ } }, "node_modules/@rolldown/binding-linux-arm64-musl": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.1.5.tgz", - "integrity": "sha512-lAryqH7IteztmCXQXk0etKj4wBQ7Gx5S6LjKhsgp9zb8I5bsuvU/2llH1hDQcjsFeqIsovMVN339/8pUDDBXxA==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.3.tgz", + "integrity": "sha512-VWkUHwWriDciit80wleYwKILoR/KMvxh/IdwS/paX+ZgpuRpCrKLUdadJbc0NpBEiyhpYawsJ73j9aCvOH+f7Q==", "cpu": [ "arm64" ], @@ -2760,9 +2783,9 @@ } }, "node_modules/@rolldown/binding-linux-ppc64-gnu": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.1.5.tgz", - "integrity": "sha512-fsK/sNBnxzBlL4O1JNrZakVQxPspqpED5dLtNsZS9oOKmtSpdNIzxH2kkol5HYTWJN47sE20ztMJPxfZ89qGOg==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.3.tgz", + "integrity": "sha512-5f1laC0SlIR0yDbFCd8acUhvJIag6N3zC5P7oUPN6wX0aOma+uKJ0wBDH5aq7I1PVI2ttTlhJwzwRIBnLiSGEg==", "cpu": [ "ppc64" ], @@ -2777,9 +2800,9 @@ } }, "node_modules/@rolldown/binding-linux-s390x-gnu": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.1.5.tgz", - "integrity": "sha512-gLYb4BIadlfTOYT5gO503n8zQjXflgzpD0FcyKh0Mzx3rqCZKnHoJWV9xe1KXUJ5lx2JfcSHr/mhzS0PC/McAA==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.3.tgz", + "integrity": "sha512-Iq4ko0r4XsgbrF/LunNgHtAGLRRVE2kXonAXQ/MV0mC6jQpMOhW1SvtZja2EhC/kd05++bP78dsqBeIQyYJ6Yg==", "cpu": [ "s390x" ], @@ -2794,9 +2817,9 @@ } }, "node_modules/@rolldown/binding-linux-x64-gnu": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.1.5.tgz", - "integrity": "sha512-FjcpEKUyJygHgs1o50VYNvkt5+7Le/VEdYt0AkRpkL33MnyQfwr8l5mXwMmfmTbyMPr5vJLC+8/Gd9gXnwU1QQ==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.3.tgz", + "integrity": "sha512-B8m6tD5+/N5FeNQFbKlLA/2yVq9ycQP1SeedyEYYKWBNR3ZQbkvIUcNnDNM03lO1l5F2roiiFJGgvoLLyZXtSg==", "cpu": [ "x64" ], @@ -2811,9 +2834,9 @@ } }, "node_modules/@rolldown/binding-linux-x64-musl": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.1.5.tgz", - "integrity": "sha512-Me+PfPI2TMeOQk0gYWfLQZtTktrmzbr8cDboqX83XKc7UrgAi55gF+2dUkWdxd19n55Essp2yeca+O9N5rBxHg==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.3.tgz", + "integrity": "sha512-pSdpdUJHkuCxun9LE7jvgUB9qsRgaiyNNCX7m/AvHTcq67AiT/Yhoxvw5zPfhrM8k/BfP8ce/hMOpthKDpEUow==", "cpu": [ "x64" ], @@ -2828,9 +2851,9 @@ } }, "node_modules/@rolldown/binding-openharmony-arm64": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.1.5.tgz", - "integrity": "sha512-yc5WrLzXks6zCQfn9Oxr8pORKyl/pF+QjHmW/Qx3qu0oyrrNC+y2JLTU1E2rcWYAmzlnqngWXHQjy51VzW70Vw==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.3.tgz", + "integrity": "sha512-OXXS3RKJgX2uLwM+gYyuH5omcH8fL1LJs96pZGgtetVCahON57+d4SJHzTgZiOjxgGkSnpXpOsWuPDGAKAigEg==", "cpu": [ "arm64" ], @@ -2845,9 +2868,9 @@ } }, "node_modules/@rolldown/binding-wasm32-wasi": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.1.5.tgz", - "integrity": "sha512-VbQGPX2b4r48TAMIM2cjgluIM1HYutm4pcTEJsle7iEP7sB1dFqtPLBVbdLAZCxy1txCcPxf4QFf4v8uvltPqA==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.3.tgz", + "integrity": "sha512-JTtb8BWFynicNSoPrehsCzBtOKjZ6jhMiPFEmOiuXg1Fl8dn2KHQob+GuPSGR0dryQa1PQJbzjF3dqO/whhjLg==", "cpu": [ "wasm32" ], @@ -2855,18 +2878,18 @@ "license": "MIT", "optional": true, "dependencies": { - "@emnapi/core": "1.11.1", - "@emnapi/runtime": "1.11.1", - "@napi-rs/wasm-runtime": "^1.1.6" + "@emnapi/core": "1.10.0", + "@emnapi/runtime": "1.10.0", + "@napi-rs/wasm-runtime": "^1.1.4" }, "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-win32-arm64-msvc": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.1.5.tgz", - "integrity": "sha512-gHv82k63z4qpV5+Q1y/12KrK0ltWBukVDI8nZcbT7Tt/ZlOIVwppazneq0F93oDxTo3IgAMEDIoQh3E2n6mVsw==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.3.tgz", + "integrity": "sha512-gEdFFEN70A/jxb2svrWsN3aDL7OUtmvlOy+6fa2jxG8K0wQ1ZbdeLGnidov6Yu5/733dI5ySfzFlQ/cb0bSz1g==", "cpu": [ "arm64" ], @@ -2881,9 +2904,9 @@ } }, "node_modules/@rolldown/binding-win32-x64-msvc": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.1.5.tgz", - "integrity": "sha512-tTZuDBPw85tEN5PQi1pnEBzDy0Z49HtScLAbD5t6hyeU92A95pRWaSMw1GZZi/RwgSgUIl0xrSlXIT/9QzvYSA==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.3.tgz", + "integrity": "sha512-eXB7CHuaQdqmJcc3koCNtNPmT/bj2gc999kUFgBxG8Ac0NdgXc4rkCHhqrgrhN3zddvvvrgzj1e90SuSfmyIXA==", "cpu": [ "x64" ], @@ -4887,9 +4910,9 @@ } }, "node_modules/eslint": { - "version": "10.7.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.7.0.tgz", - "integrity": "sha512-GVTD7s1vdIl6UYvAfriOPeY1Df8LIZjfofLvHwde+erDHGGuHyuM6xoxRxmHiebhYuD2p1vN4wWh0XzPARSGDQ==", + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.5.0.tgz", + "integrity": "sha512-1y+7C+vi12bUK1IpZeaV3gsH9fHLBmPvYmPx42pvT/E9yG0IC8g3PUZZgp0+JLJl7ZDK0flc2gc+Aw9dpCvIsQ==", "dev": true, "license": "MIT", "workspaces": [ @@ -4946,13 +4969,13 @@ } }, "node_modules/eslint-plugin-jsdoc": { - "version": "63.0.13", - "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-63.0.13.tgz", - "integrity": "sha512-ahG1kWA8jYNwaQJtzJlnF+v4Gb9w5r+WL98gp+L8qjLN9ErpL5sevGuemN+fCYsU3Np27F36KmDc8UPi1ml/dg==", + "version": "63.0.7", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-63.0.7.tgz", + "integrity": "sha512-pxrqGO733F7xmVYB5vQOiciiT9uddxqehawnbPjZmW2YaJR6fT5cP3UQd2BNoE85ATspCMtNL8w/a5WDGX3Qwg==", "dev": true, "license": "BSD-3-Clause", "dependencies": { - "@es-joy/jsdoccomment": "~0.88.0", + "@es-joy/jsdoccomment": "~0.87.0", "@es-joy/resolve.exports": "1.2.0", "are-docs-informative": "^0.0.2", "comment-parser": "1.4.7", @@ -4963,7 +4986,7 @@ "html-entities": "^2.6.0", "object-deep-merge": "^2.0.1", "parse-imports-exports": "^0.2.4", - "semver": "^7.8.5", + "semver": "^7.8.2", "spdx-expression-parse": "^4.0.0", "to-valid-identifier": "^1.0.0" }, @@ -6075,9 +6098,9 @@ "license": "MIT" }, "node_modules/nanoid": { - "version": "3.3.15", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.15.tgz", - "integrity": "sha512-y7Wygv/7mEOvxTuEQDB8StXdMRBWf1kR/tlhAzBRUFkB2jfcLOAxO/SHmOO2zgz1pVgK29/kyupn059/bCHdjA==", + "version": "3.3.12", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", + "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", "dev": true, "funding": [ { @@ -6387,9 +6410,9 @@ "license": "ISC" }, "node_modules/picomatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", - "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, "license": "MIT", "engines": { @@ -6400,9 +6423,9 @@ } }, "node_modules/postcss": { - "version": "8.5.16", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.16.tgz", - "integrity": "sha512-vuwillviilfKZsg0VGj5R/YwwcHx4SLsIOI/7K6mQkWx+l5cUHTjj5g0AasTBcyXsbfTgrwsUNmVUb5xVwyPwg==", + "version": "8.5.15", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz", + "integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==", "dev": true, "funding": [ { @@ -6638,13 +6661,13 @@ } }, "node_modules/rolldown": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.1.5.tgz", - "integrity": "sha512-t9z29cJjXf/vxQ8dyhCSpt6H6aSwHTk8cT5I3iy6SMXuFpk5mB6PL6XfC8PCwrPTx93udwKUm9HRteAlTGBLiA==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.3.tgz", + "integrity": "sha512-i00lAJ2ks1BYr7rjNjKC7BcqAS7nVfiT3QX1SI5aY+AFHblCmaUf9OE9dbdzDvW6dJxbi2ZCZiy9v3CcwOiX3g==", "dev": true, "license": "MIT", "dependencies": { - "@oxc-project/types": "=0.139.0", + "@oxc-project/types": "=0.133.0", "@rolldown/pluginutils": "^1.0.0" }, "bin": { @@ -6654,21 +6677,21 @@ "node": "^20.19.0 || >=22.12.0" }, "optionalDependencies": { - "@rolldown/binding-android-arm64": "1.1.5", - "@rolldown/binding-darwin-arm64": "1.1.5", - "@rolldown/binding-darwin-x64": "1.1.5", - "@rolldown/binding-freebsd-x64": "1.1.5", - "@rolldown/binding-linux-arm-gnueabihf": "1.1.5", - "@rolldown/binding-linux-arm64-gnu": "1.1.5", - "@rolldown/binding-linux-arm64-musl": "1.1.5", - "@rolldown/binding-linux-ppc64-gnu": "1.1.5", - "@rolldown/binding-linux-s390x-gnu": "1.1.5", - "@rolldown/binding-linux-x64-gnu": "1.1.5", - "@rolldown/binding-linux-x64-musl": "1.1.5", - "@rolldown/binding-openharmony-arm64": "1.1.5", - "@rolldown/binding-wasm32-wasi": "1.1.5", - "@rolldown/binding-win32-arm64-msvc": "1.1.5", - "@rolldown/binding-win32-x64-msvc": "1.1.5" + "@rolldown/binding-android-arm64": "1.0.3", + "@rolldown/binding-darwin-arm64": "1.0.3", + "@rolldown/binding-darwin-x64": "1.0.3", + "@rolldown/binding-freebsd-x64": "1.0.3", + "@rolldown/binding-linux-arm-gnueabihf": "1.0.3", + "@rolldown/binding-linux-arm64-gnu": "1.0.3", + "@rolldown/binding-linux-arm64-musl": "1.0.3", + "@rolldown/binding-linux-ppc64-gnu": "1.0.3", + "@rolldown/binding-linux-s390x-gnu": "1.0.3", + "@rolldown/binding-linux-x64-gnu": "1.0.3", + "@rolldown/binding-linux-x64-musl": "1.0.3", + "@rolldown/binding-openharmony-arm64": "1.0.3", + "@rolldown/binding-wasm32-wasi": "1.0.3", + "@rolldown/binding-win32-arm64-msvc": "1.0.3", + "@rolldown/binding-win32-x64-msvc": "1.0.3" } }, "node_modules/run-applescript": { @@ -6704,9 +6727,9 @@ "license": "MIT" }, "node_modules/semver": { - "version": "7.8.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", - "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.4.tgz", + "integrity": "sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==", "dev": true, "license": "ISC", "bin": { @@ -7262,16 +7285,16 @@ } }, "node_modules/vite": { - "version": "8.1.4", - "resolved": "https://registry.npmjs.org/vite/-/vite-8.1.4.tgz", - "integrity": "sha512-bTT9PsdWO+MQMNG9ZXIP/qM9wGh37DFxTV/sPq9cFpHr3w4jkgef032PkAL9jAqhk3Nz8NQw3O8n6/xFkqO4QQ==", + "version": "8.0.16", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.16.tgz", + "integrity": "sha512-h9bXPmJichP5fLmVQo3PyaGSDE2n3aPuomeAlVRm0JLmt4rY6zmPKd59HYI4LNW8oTK7tlTsuC7l/m7awx9Jcw==", "dev": true, "license": "MIT", "dependencies": { "lightningcss": "^1.32.0", - "picomatch": "^4.0.5", - "postcss": "^8.5.16", - "rolldown": "~1.1.4", + "picomatch": "^4.0.4", + "postcss": "^8.5.15", + "rolldown": "1.0.3", "tinyglobby": "^0.2.17" }, "bin": { @@ -7288,7 +7311,7 @@ }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", - "@vitejs/devtools": "^0.3.0", + "@vitejs/devtools": "^0.1.18", "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", @@ -7518,7 +7541,7 @@ "devDependencies": { "@types/node": "^25.9.3", "@vitest/coverage-v8": "^4.1.5", - "eslint": "^10.7.0", + "eslint": "^10.5.0", "fast-check": "^4.8.0", "typescript": "^6.0.3", "typescript-eslint": "^8.60.1", diff --git a/package.json b/package.json index 8a496faf..bd7d8dbd 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,7 @@ }, "devDependencies": { "@eslint/js": "^10.0.1", - "eslint-plugin-jsdoc": "^63.0.13", + "eslint-plugin-jsdoc": "^63.0.7", "react": "^19.2.4", "react-dom": "^19.2.7" } diff --git a/packages/shared-types/package.json b/packages/shared-types/package.json index 75466e83..f04bd440 100644 --- a/packages/shared-types/package.json +++ b/packages/shared-types/package.json @@ -11,7 +11,7 @@ "devDependencies": { "@types/node": "^25.9.3", "@vitest/coverage-v8": "^4.1.5", - "eslint": "^10.7.0", + "eslint": "^10.5.0", "fast-check": "^4.8.0", "typescript": "^6.0.3", "typescript-eslint": "^8.60.1", From 059f5c13f1cc928d08513dca04168cc827fc56de Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Sun, 12 Jul 2026 16:03:53 +0000 Subject: [PATCH 3/9] =?UTF-8?q?=F0=9F=9B=A1=EF=B8=8F=20Sentinel:=20[HIGH]?= =?UTF-8?q?=20Fix=20Zip=20Slip=20path=20traversal=20vulnerability?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 이 커밋은 Python 스크립트에서 발생하는 Zip Slip(디렉토리 순회) 취약점을 수정합니다. 기존 코드에서는 `pathlib.Path(member.filename).parts`를 사용하여 '..' 패턴을 검사했지만, 이 방식은 POSIX 환경(예: Linux 기반 CI 러너)에서 백슬래시('\')를 디렉토리 구분자로 인식하지 않아 우회될 수 있는 문제가 있었습니다. 이를 방지하기 위해 백슬래시를 슬래시('/')로 변환한 뒤 분할하여 '..' 포함 여부를 검사하도록 `scripts/checks/extract_scorecard_artifact.py` 및 `scripts/release/extract_release_artifacts.py` 파일을 수정했습니다. --- .github/workflows/ci.yml | 2 + apps/desktop/package.json | 4 +- .../src/features/score/ScoreViewer.test.tsx | 17 +- package-lock.json | 267 ++++++++---------- package.json | 2 +- packages/shared-types/package.json | 2 +- scripts/checks/extract_scorecard_artifact.py | 8 +- scripts/release/extract_release_artifacts.py | 12 +- 8 files changed, 158 insertions(+), 156 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 615249e1..fc871e68 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -56,3 +56,5 @@ jobs: run: npm run build --workspace @bandscope/desktop - name: Check Tauri shell run: cargo +stable check --manifest-path apps/desktop/src-tauri/Cargo.toml --locked + - name: Test Tauri shell + run: cargo +stable test --manifest-path apps/desktop/src-tauri/Cargo.toml --locked diff --git a/apps/desktop/package.json b/apps/desktop/package.json index 6800661b..d237450a 100644 --- a/apps/desktop/package.json +++ b/apps/desktop/package.json @@ -38,13 +38,13 @@ "@types/react-dom": "^19.2.3", "@vitejs/plugin-react": "^6.0.2", "@vitest/coverage-v8": "^4.1.5", - "eslint": "^10.5.0", + "eslint": "^10.7.0", "jsdom": "^29.1.1", "storybook": "^10.4.6", "tailwindcss": "^4.2.4", "typescript": "^6.0.3", "typescript-eslint": "^8.60.1", - "vite": "^8.0.16", + "vite": "^8.1.4", "vitest": "^4.1.9" } } diff --git a/apps/desktop/src/features/score/ScoreViewer.test.tsx b/apps/desktop/src/features/score/ScoreViewer.test.tsx index aa3f7317..3ac2dd60 100644 --- a/apps/desktop/src/features/score/ScoreViewer.test.tsx +++ b/apps/desktop/src/features/score/ScoreViewer.test.tsx @@ -144,7 +144,10 @@ describe("ScoreViewer", () => { expect(await screen.findByRole("alert")).toBeInTheDocument(); expect(screen.getByText("Could not display the score")).toBeInTheDocument(); expect(screen.getByText("broken bytes")).toBeInTheDocument(); - expect(onStatusChange).toHaveBeenLastCalledWith("FAILED"); + // The FAILED status is set from the load promise's catch (a microtask), and + // onStatusChange fires from a passive effect that may not have flushed the + // instant the alert appears. Poll for it, matching the READY assertion below. + await waitFor(() => expect(onStatusChange).toHaveBeenLastCalledWith("FAILED")); fireEvent.click(screen.getByRole("button", { name: "Retry" })); @@ -246,7 +249,17 @@ describe("ScoreViewer", () => { expect(await screen.findByText("Page 1 of 1")).toBeInTheDocument(); - act(() => { + // The ResizeObserver is registered by a READY-gated effect that commits + // after the "Page 1 of 1" text appears. Wait for that effect to capture the + // callback before driving a resize; otherwise the optional call below is a + // silent no-op and the fit-width recompute never runs. + await waitFor(() => { + expect(resizeCallback).not.toBeNull(); + }); + + // Wrap the resize in an async act() so the resulting re-render and its async + // getPage()/getViewport() calls flush deterministically before we assert. + await act(async () => { resizeCallback?.( [{ contentRect: { width: 300 } } as ResizeObserverEntry], {} as ResizeObserver diff --git a/package-lock.json b/package-lock.json index 47844b5e..5cea79f3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,7 @@ ], "devDependencies": { "@eslint/js": "^10.0.1", - "eslint-plugin-jsdoc": "^63.0.7", + "eslint-plugin-jsdoc": "^63.0.13", "react": "^19.2.4", "react-dom": "^19.2.7" }, @@ -50,13 +50,13 @@ "@types/react-dom": "^19.2.3", "@vitejs/plugin-react": "^6.0.2", "@vitest/coverage-v8": "^4.1.5", - "eslint": "^10.5.0", + "eslint": "^10.7.0", "jsdom": "^29.1.1", "storybook": "^10.4.6", "tailwindcss": "^4.2.4", "typescript": "^6.0.3", "typescript-eslint": "^8.60.1", - "vite": "^8.0.16", + "vite": "^8.1.4", "vitest": "^4.1.9" } }, @@ -871,21 +871,32 @@ } }, "node_modules/@emnapi/core": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", - "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.1.tgz", + "integrity": "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.2", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/core/node_modules/@emnapi/wasi-threads": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz", + "integrity": "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==", "dev": true, "license": "MIT", "optional": true, "dependencies": { - "@emnapi/wasi-threads": "1.2.1", "tslib": "^2.4.0" } }, "node_modules/@emnapi/runtime": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", - "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.1.tgz", + "integrity": "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==", "dev": true, "license": "MIT", "optional": true, @@ -905,9 +916,9 @@ } }, "node_modules/@es-joy/jsdoccomment": { - "version": "0.87.0", - "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.87.0.tgz", - "integrity": "sha512-mFXZloZMzuJZXSHUmAFu/pXTk0ZJTJBluuAkrvbzidpTN8W6F2bpRFuedSH+85kbdlRLJqc+gfN+kD3JOLJK5g==", + "version": "0.88.0", + "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.88.0.tgz", + "integrity": "sha512-GK/HL/claLLNo5KG705auIlZMwEtmn88ofSGuLsmVZwKBqMPJhW9DiznYNq07QEqz9BPtA3LBfYImtZmhVvRAw==", "dev": true, "license": "MIT", "dependencies": { @@ -2349,9 +2360,9 @@ } }, "node_modules/@oxc-project/types": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.133.0.tgz", - "integrity": "sha512-KzkdCd6Uxqnf6l3HOw1xfatAlUURA0g14cvBYFyJ5SaNOQbOUvBr9PKArcPcrNIeRsBdgcUzOGrhKveVpvOIGA==", + "version": "0.139.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.139.0.tgz", + "integrity": "sha512-r9gHphtCs+1M7J0pw6Sn/hh/Wpa/iQrOOkrNAlVLF/gHq+/CJmHIWKKUUhdWjcD6CIa8idarspCsASiXCXvFUw==", "dev": true, "license": "MIT", "funding": { @@ -2601,40 +2612,6 @@ "node": ">=14.0.0" } }, - "node_modules/@oxc-resolver/binding-wasm32-wasi/node_modules/@emnapi/core": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.1.tgz", - "integrity": "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/wasi-threads": "1.2.2", - "tslib": "^2.4.0" - } - }, - "node_modules/@oxc-resolver/binding-wasm32-wasi/node_modules/@emnapi/runtime": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.1.tgz", - "integrity": "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@oxc-resolver/binding-wasm32-wasi/node_modules/@emnapi/wasi-threads": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz", - "integrity": "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, "node_modules/@oxc-resolver/binding-win32-arm64-msvc": { "version": "11.23.0", "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-11.23.0.tgz", @@ -2664,9 +2641,9 @@ ] }, "node_modules/@rolldown/binding-android-arm64": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.3.tgz", - "integrity": "sha512-454rs7jHngixp/NMxd5srYD57OnzSlZ/eFTETjORQHLwJG1lRtmNOJcBerZlfu4GjKqeq8aCCIQrMdHyhI51Hw==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.1.5.tgz", + "integrity": "sha512-lZg8fqIv2v7FF237bwMgzGZEJvGL79/s5knJ/i6FmsGF4XXlzccZ4jb+TrFIxtSSxFtIpdsgrPZeMk1I9AFcyQ==", "cpu": [ "arm64" ], @@ -2681,9 +2658,9 @@ } }, "node_modules/@rolldown/binding-darwin-arm64": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.3.tgz", - "integrity": "sha512-PcAhP+ynjURNyy8SKGl5DQP94aGuB/7JrXJb/t7P+hanXvQVMWzUvRRhBAcg/lNRadBhoUPqSoP4xw5tR/KBEA==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.1.5.tgz", + "integrity": "sha512-51Bnx9pNiMRKSUNtBfySkNJ9vMU9Hh3I1ozDd6gyPPYzaXCfnptUcEZxXGYFn+ul2dtcMUiqGR1Yai2K10uoTw==", "cpu": [ "arm64" ], @@ -2698,9 +2675,9 @@ } }, "node_modules/@rolldown/binding-darwin-x64": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.3.tgz", - "integrity": "sha512-9YpfeUvSE2RS7wysJ81uOZkXJz7f7Q55H2Gvp3VEw/EsahqDtrphrZ0EwDLK5vvKOzaCrBsjF8JmnMLcUt78Gg==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.1.5.tgz", + "integrity": "sha512-Tm+gbfC0aHu1tBA/JvKQh32S0K6YgCHkiAF4/W6xX0K0RmNuc94VeK419dJoE65R5aRxmo+noZQSWrAMF6yb6g==", "cpu": [ "x64" ], @@ -2715,9 +2692,9 @@ } }, "node_modules/@rolldown/binding-freebsd-x64": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.3.tgz", - "integrity": "sha512-yB1IlAsSNHncV6SCTL27/MVGR5htvQsoGxIv5KMGXALp+Ll1wYsn+x98M9MW7qa+NdSbvrrY7ANI4wLJ0n1e6g==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.1.5.tgz", + "integrity": "sha512-JMzDKCCXq93YccG5gz3hvOs1oXRKAf0XYpfOS88e+wZrC8Iugj6j68867vrYZkvpDDpKn/KoKORThmchMpF6TA==", "cpu": [ "x64" ], @@ -2732,9 +2709,9 @@ } }, "node_modules/@rolldown/binding-linux-arm-gnueabihf": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.3.tgz", - "integrity": "sha512-Yi30IVAAfLUCy2MseFjbB1jAMDl1VMCAas5StnYp8da9+CKvMd2H2cbEjWcw5NPaPqzvYkVIaF1nNUG+b7u/sw==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.1.5.tgz", + "integrity": "sha512-uML21j2K5TfPGutKxub+M+nLjZIrWjXQ5Grx4lCe/nimTj9B4L63zHpjXLl4y0L3mcm2htEQIb06oCG/szerNw==", "cpu": [ "arm" ], @@ -2749,9 +2726,9 @@ } }, "node_modules/@rolldown/binding-linux-arm64-gnu": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.3.tgz", - "integrity": "sha512-jsO7R8To+AdlYgUmN5sHSCZbfhtMBkO0WUx8iORQnPcMMdgr7qM2DQmMwgabs3GhNztdmoKkMKQFHD6DTMCIQw==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.1.5.tgz", + "integrity": "sha512-navSiuTMogvnQoZoM/v+l3ZWo50/NTwSHSzheABx/RCnmUPaKwq9qSo4Br2OYRs21+Fz8uFqITZM3H4opOB0/Q==", "cpu": [ "arm64" ], @@ -2766,9 +2743,9 @@ } }, "node_modules/@rolldown/binding-linux-arm64-musl": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.3.tgz", - "integrity": "sha512-VWkUHwWriDciit80wleYwKILoR/KMvxh/IdwS/paX+ZgpuRpCrKLUdadJbc0NpBEiyhpYawsJ73j9aCvOH+f7Q==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.1.5.tgz", + "integrity": "sha512-lAryqH7IteztmCXQXk0etKj4wBQ7Gx5S6LjKhsgp9zb8I5bsuvU/2llH1hDQcjsFeqIsovMVN339/8pUDDBXxA==", "cpu": [ "arm64" ], @@ -2783,9 +2760,9 @@ } }, "node_modules/@rolldown/binding-linux-ppc64-gnu": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.3.tgz", - "integrity": "sha512-5f1laC0SlIR0yDbFCd8acUhvJIag6N3zC5P7oUPN6wX0aOma+uKJ0wBDH5aq7I1PVI2ttTlhJwzwRIBnLiSGEg==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.1.5.tgz", + "integrity": "sha512-fsK/sNBnxzBlL4O1JNrZakVQxPspqpED5dLtNsZS9oOKmtSpdNIzxH2kkol5HYTWJN47sE20ztMJPxfZ89qGOg==", "cpu": [ "ppc64" ], @@ -2800,9 +2777,9 @@ } }, "node_modules/@rolldown/binding-linux-s390x-gnu": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.3.tgz", - "integrity": "sha512-Iq4ko0r4XsgbrF/LunNgHtAGLRRVE2kXonAXQ/MV0mC6jQpMOhW1SvtZja2EhC/kd05++bP78dsqBeIQyYJ6Yg==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.1.5.tgz", + "integrity": "sha512-gLYb4BIadlfTOYT5gO503n8zQjXflgzpD0FcyKh0Mzx3rqCZKnHoJWV9xe1KXUJ5lx2JfcSHr/mhzS0PC/McAA==", "cpu": [ "s390x" ], @@ -2817,9 +2794,9 @@ } }, "node_modules/@rolldown/binding-linux-x64-gnu": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.3.tgz", - "integrity": "sha512-B8m6tD5+/N5FeNQFbKlLA/2yVq9ycQP1SeedyEYYKWBNR3ZQbkvIUcNnDNM03lO1l5F2roiiFJGgvoLLyZXtSg==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.1.5.tgz", + "integrity": "sha512-FjcpEKUyJygHgs1o50VYNvkt5+7Le/VEdYt0AkRpkL33MnyQfwr8l5mXwMmfmTbyMPr5vJLC+8/Gd9gXnwU1QQ==", "cpu": [ "x64" ], @@ -2834,9 +2811,9 @@ } }, "node_modules/@rolldown/binding-linux-x64-musl": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.3.tgz", - "integrity": "sha512-pSdpdUJHkuCxun9LE7jvgUB9qsRgaiyNNCX7m/AvHTcq67AiT/Yhoxvw5zPfhrM8k/BfP8ce/hMOpthKDpEUow==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.1.5.tgz", + "integrity": "sha512-Me+PfPI2TMeOQk0gYWfLQZtTktrmzbr8cDboqX83XKc7UrgAi55gF+2dUkWdxd19n55Essp2yeca+O9N5rBxHg==", "cpu": [ "x64" ], @@ -2851,9 +2828,9 @@ } }, "node_modules/@rolldown/binding-openharmony-arm64": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.3.tgz", - "integrity": "sha512-OXXS3RKJgX2uLwM+gYyuH5omcH8fL1LJs96pZGgtetVCahON57+d4SJHzTgZiOjxgGkSnpXpOsWuPDGAKAigEg==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.1.5.tgz", + "integrity": "sha512-yc5WrLzXks6zCQfn9Oxr8pORKyl/pF+QjHmW/Qx3qu0oyrrNC+y2JLTU1E2rcWYAmzlnqngWXHQjy51VzW70Vw==", "cpu": [ "arm64" ], @@ -2868,9 +2845,9 @@ } }, "node_modules/@rolldown/binding-wasm32-wasi": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.3.tgz", - "integrity": "sha512-JTtb8BWFynicNSoPrehsCzBtOKjZ6jhMiPFEmOiuXg1Fl8dn2KHQob+GuPSGR0dryQa1PQJbzjF3dqO/whhjLg==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.1.5.tgz", + "integrity": "sha512-VbQGPX2b4r48TAMIM2cjgluIM1HYutm4pcTEJsle7iEP7sB1dFqtPLBVbdLAZCxy1txCcPxf4QFf4v8uvltPqA==", "cpu": [ "wasm32" ], @@ -2878,18 +2855,18 @@ "license": "MIT", "optional": true, "dependencies": { - "@emnapi/core": "1.10.0", - "@emnapi/runtime": "1.10.0", - "@napi-rs/wasm-runtime": "^1.1.4" + "@emnapi/core": "1.11.1", + "@emnapi/runtime": "1.11.1", + "@napi-rs/wasm-runtime": "^1.1.6" }, "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-win32-arm64-msvc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.3.tgz", - "integrity": "sha512-gEdFFEN70A/jxb2svrWsN3aDL7OUtmvlOy+6fa2jxG8K0wQ1ZbdeLGnidov6Yu5/733dI5ySfzFlQ/cb0bSz1g==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.1.5.tgz", + "integrity": "sha512-gHv82k63z4qpV5+Q1y/12KrK0ltWBukVDI8nZcbT7Tt/ZlOIVwppazneq0F93oDxTo3IgAMEDIoQh3E2n6mVsw==", "cpu": [ "arm64" ], @@ -2904,9 +2881,9 @@ } }, "node_modules/@rolldown/binding-win32-x64-msvc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.3.tgz", - "integrity": "sha512-eXB7CHuaQdqmJcc3koCNtNPmT/bj2gc999kUFgBxG8Ac0NdgXc4rkCHhqrgrhN3zddvvvrgzj1e90SuSfmyIXA==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.1.5.tgz", + "integrity": "sha512-tTZuDBPw85tEN5PQi1pnEBzDy0Z49HtScLAbD5t6hyeU92A95pRWaSMw1GZZi/RwgSgUIl0xrSlXIT/9QzvYSA==", "cpu": [ "x64" ], @@ -4910,9 +4887,9 @@ } }, "node_modules/eslint": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.5.0.tgz", - "integrity": "sha512-1y+7C+vi12bUK1IpZeaV3gsH9fHLBmPvYmPx42pvT/E9yG0IC8g3PUZZgp0+JLJl7ZDK0flc2gc+Aw9dpCvIsQ==", + "version": "10.7.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.7.0.tgz", + "integrity": "sha512-GVTD7s1vdIl6UYvAfriOPeY1Df8LIZjfofLvHwde+erDHGGuHyuM6xoxRxmHiebhYuD2p1vN4wWh0XzPARSGDQ==", "dev": true, "license": "MIT", "workspaces": [ @@ -4969,13 +4946,13 @@ } }, "node_modules/eslint-plugin-jsdoc": { - "version": "63.0.7", - "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-63.0.7.tgz", - "integrity": "sha512-pxrqGO733F7xmVYB5vQOiciiT9uddxqehawnbPjZmW2YaJR6fT5cP3UQd2BNoE85ATspCMtNL8w/a5WDGX3Qwg==", + "version": "63.0.13", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-63.0.13.tgz", + "integrity": "sha512-ahG1kWA8jYNwaQJtzJlnF+v4Gb9w5r+WL98gp+L8qjLN9ErpL5sevGuemN+fCYsU3Np27F36KmDc8UPi1ml/dg==", "dev": true, "license": "BSD-3-Clause", "dependencies": { - "@es-joy/jsdoccomment": "~0.87.0", + "@es-joy/jsdoccomment": "~0.88.0", "@es-joy/resolve.exports": "1.2.0", "are-docs-informative": "^0.0.2", "comment-parser": "1.4.7", @@ -4986,7 +4963,7 @@ "html-entities": "^2.6.0", "object-deep-merge": "^2.0.1", "parse-imports-exports": "^0.2.4", - "semver": "^7.8.2", + "semver": "^7.8.5", "spdx-expression-parse": "^4.0.0", "to-valid-identifier": "^1.0.0" }, @@ -6098,9 +6075,9 @@ "license": "MIT" }, "node_modules/nanoid": { - "version": "3.3.12", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", - "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", + "version": "3.3.15", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.15.tgz", + "integrity": "sha512-y7Wygv/7mEOvxTuEQDB8StXdMRBWf1kR/tlhAzBRUFkB2jfcLOAxO/SHmOO2zgz1pVgK29/kyupn059/bCHdjA==", "dev": true, "funding": [ { @@ -6410,9 +6387,9 @@ "license": "ISC" }, "node_modules/picomatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", - "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", + "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", "dev": true, "license": "MIT", "engines": { @@ -6423,9 +6400,9 @@ } }, "node_modules/postcss": { - "version": "8.5.15", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz", - "integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==", + "version": "8.5.16", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.16.tgz", + "integrity": "sha512-vuwillviilfKZsg0VGj5R/YwwcHx4SLsIOI/7K6mQkWx+l5cUHTjj5g0AasTBcyXsbfTgrwsUNmVUb5xVwyPwg==", "dev": true, "funding": [ { @@ -6661,13 +6638,13 @@ } }, "node_modules/rolldown": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.3.tgz", - "integrity": "sha512-i00lAJ2ks1BYr7rjNjKC7BcqAS7nVfiT3QX1SI5aY+AFHblCmaUf9OE9dbdzDvW6dJxbi2ZCZiy9v3CcwOiX3g==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.1.5.tgz", + "integrity": "sha512-t9z29cJjXf/vxQ8dyhCSpt6H6aSwHTk8cT5I3iy6SMXuFpk5mB6PL6XfC8PCwrPTx93udwKUm9HRteAlTGBLiA==", "dev": true, "license": "MIT", "dependencies": { - "@oxc-project/types": "=0.133.0", + "@oxc-project/types": "=0.139.0", "@rolldown/pluginutils": "^1.0.0" }, "bin": { @@ -6677,21 +6654,21 @@ "node": "^20.19.0 || >=22.12.0" }, "optionalDependencies": { - "@rolldown/binding-android-arm64": "1.0.3", - "@rolldown/binding-darwin-arm64": "1.0.3", - "@rolldown/binding-darwin-x64": "1.0.3", - "@rolldown/binding-freebsd-x64": "1.0.3", - "@rolldown/binding-linux-arm-gnueabihf": "1.0.3", - "@rolldown/binding-linux-arm64-gnu": "1.0.3", - "@rolldown/binding-linux-arm64-musl": "1.0.3", - "@rolldown/binding-linux-ppc64-gnu": "1.0.3", - "@rolldown/binding-linux-s390x-gnu": "1.0.3", - "@rolldown/binding-linux-x64-gnu": "1.0.3", - "@rolldown/binding-linux-x64-musl": "1.0.3", - "@rolldown/binding-openharmony-arm64": "1.0.3", - "@rolldown/binding-wasm32-wasi": "1.0.3", - "@rolldown/binding-win32-arm64-msvc": "1.0.3", - "@rolldown/binding-win32-x64-msvc": "1.0.3" + "@rolldown/binding-android-arm64": "1.1.5", + "@rolldown/binding-darwin-arm64": "1.1.5", + "@rolldown/binding-darwin-x64": "1.1.5", + "@rolldown/binding-freebsd-x64": "1.1.5", + "@rolldown/binding-linux-arm-gnueabihf": "1.1.5", + "@rolldown/binding-linux-arm64-gnu": "1.1.5", + "@rolldown/binding-linux-arm64-musl": "1.1.5", + "@rolldown/binding-linux-ppc64-gnu": "1.1.5", + "@rolldown/binding-linux-s390x-gnu": "1.1.5", + "@rolldown/binding-linux-x64-gnu": "1.1.5", + "@rolldown/binding-linux-x64-musl": "1.1.5", + "@rolldown/binding-openharmony-arm64": "1.1.5", + "@rolldown/binding-wasm32-wasi": "1.1.5", + "@rolldown/binding-win32-arm64-msvc": "1.1.5", + "@rolldown/binding-win32-x64-msvc": "1.1.5" } }, "node_modules/run-applescript": { @@ -6727,9 +6704,9 @@ "license": "MIT" }, "node_modules/semver": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.4.tgz", - "integrity": "sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==", + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", "dev": true, "license": "ISC", "bin": { @@ -7285,16 +7262,16 @@ } }, "node_modules/vite": { - "version": "8.0.16", - "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.16.tgz", - "integrity": "sha512-h9bXPmJichP5fLmVQo3PyaGSDE2n3aPuomeAlVRm0JLmt4rY6zmPKd59HYI4LNW8oTK7tlTsuC7l/m7awx9Jcw==", + "version": "8.1.4", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.1.4.tgz", + "integrity": "sha512-bTT9PsdWO+MQMNG9ZXIP/qM9wGh37DFxTV/sPq9cFpHr3w4jkgef032PkAL9jAqhk3Nz8NQw3O8n6/xFkqO4QQ==", "dev": true, "license": "MIT", "dependencies": { "lightningcss": "^1.32.0", - "picomatch": "^4.0.4", - "postcss": "^8.5.15", - "rolldown": "1.0.3", + "picomatch": "^4.0.5", + "postcss": "^8.5.16", + "rolldown": "~1.1.4", "tinyglobby": "^0.2.17" }, "bin": { @@ -7311,7 +7288,7 @@ }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", - "@vitejs/devtools": "^0.1.18", + "@vitejs/devtools": "^0.3.0", "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", @@ -7541,7 +7518,7 @@ "devDependencies": { "@types/node": "^25.9.3", "@vitest/coverage-v8": "^4.1.5", - "eslint": "^10.5.0", + "eslint": "^10.7.0", "fast-check": "^4.8.0", "typescript": "^6.0.3", "typescript-eslint": "^8.60.1", diff --git a/package.json b/package.json index bd7d8dbd..8a496faf 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,7 @@ }, "devDependencies": { "@eslint/js": "^10.0.1", - "eslint-plugin-jsdoc": "^63.0.7", + "eslint-plugin-jsdoc": "^63.0.13", "react": "^19.2.4", "react-dom": "^19.2.7" } diff --git a/packages/shared-types/package.json b/packages/shared-types/package.json index f04bd440..75466e83 100644 --- a/packages/shared-types/package.json +++ b/packages/shared-types/package.json @@ -11,7 +11,7 @@ "devDependencies": { "@types/node": "^25.9.3", "@vitest/coverage-v8": "^4.1.5", - "eslint": "^10.5.0", + "eslint": "^10.7.0", "fast-check": "^4.8.0", "typescript": "^6.0.3", "typescript-eslint": "^8.60.1", diff --git a/scripts/checks/extract_scorecard_artifact.py b/scripts/checks/extract_scorecard_artifact.py index 2272ef3e..cab739db 100644 --- a/scripts/checks/extract_scorecard_artifact.py +++ b/scripts/checks/extract_scorecard_artifact.py @@ -23,7 +23,9 @@ def resolve_artifact_zip(source: Path) -> Path: raise ValueError(f"artifact source does not exist: {source}") ensure_non_symlink_path(source, path_kind="artifact path") candidates: list[Path] = [] - for path in sorted(candidate for candidate in source.iterdir() if candidate.suffix == ".zip"): + for path in sorted( + candidate for candidate in source.iterdir() if candidate.suffix == ".zip" + ): ensure_non_symlink_path(path, path_kind="artifact path") candidates.append(path) if len(candidates) != 1: @@ -63,7 +65,9 @@ def ensure_non_symlink_path(path: Path, *, path_kind: str = "output path") -> No raise ValueError(f"symlinked {path_kind} is not allowed: {component}") -def write_new_file_without_following_symlinks(target: Path, source_file: IO[bytes]) -> None: +def write_new_file_without_following_symlinks( + target: Path, source_file: IO[bytes] +) -> None: """Stream-write to a new file without following an existing symlink.""" flags = os.O_CREAT | os.O_EXCL | os.O_WRONLY if hasattr(os, "O_NOFOLLOW"): diff --git a/scripts/release/extract_release_artifacts.py b/scripts/release/extract_release_artifacts.py index fe67e95a..7b5355e3 100644 --- a/scripts/release/extract_release_artifacts.py +++ b/scripts/release/extract_release_artifacts.py @@ -45,7 +45,9 @@ def artifact_zip_paths(source: Path) -> list[Path]: raise ValueError(f"artifact source does not exist: {source}") ensure_non_symlink_path(source, path_kind="artifact path") candidates: list[Path] = [] - for path in sorted(candidate for candidate in source.iterdir() if candidate.suffix == ".zip"): + for path in sorted( + candidate for candidate in source.iterdir() if candidate.suffix == ".zip" + ): ensure_non_symlink_path(path, path_kind="artifact path") candidates.append(path) if not candidates: @@ -69,7 +71,9 @@ def validate_member(member: zipfile.ZipInfo) -> None: raise ValueError(f"release artifact member too large: {member.filename}") -def write_new_file_without_following_symlinks(target: Path, source_file: IO[bytes]) -> None: +def write_new_file_without_following_symlinks( + target: Path, source_file: IO[bytes] +) -> None: """Stream-write to a new file without following an existing symlink.""" flags = os.O_CREAT | os.O_EXCL | os.O_WRONLY if hasattr(os, "O_NOFOLLOW"): @@ -110,7 +114,9 @@ def extract_release_artifacts(source: Path, output_dir: Path) -> list[Path]: if total_bytes > MAX_TOTAL_RELEASE_ARTIFACT_BYTES: raise ValueError("release artifact bundle too large") if member.filename in seen: - raise ValueError(f"duplicate release artifact member: {member.filename}") + raise ValueError( + f"duplicate release artifact member: {member.filename}" + ) seen.add(member.filename) target = output_dir / member.filename with archive.open(member) as source_file: From e5fbb6bb046689f2ff375638d5d874c68402810c Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Sun, 12 Jul 2026 16:24:09 +0000 Subject: [PATCH 4/9] =?UTF-8?q?=F0=9F=9B=A1=EF=B8=8F=20Sentinel:=20[HIGH]?= =?UTF-8?q?=20Fix=20Zip=20Slip=20path=20traversal=20vulnerability?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 이 커밋은 Python 스크립트에서 발생하는 Zip Slip(디렉토리 순회) 취약점을 수정합니다. 기존 코드에서는 `pathlib.Path(member.filename).parts`를 사용하여 '..' 패턴을 검사했지만, 이 방식은 POSIX 환경(예: Linux 기반 CI 러너)에서 백슬래시('\')를 디렉토리 구분자로 인식하지 않아 우회될 수 있는 문제가 있었습니다. 이를 방지하기 위해 백슬래시를 슬래시('/')로 변환한 뒤 분할하여 '..' 포함 여부를 검사하도록 `scripts/checks/extract_scorecard_artifact.py` 및 `scripts/release/extract_release_artifacts.py` 파일을 수정했습니다. From 8ce2cfe5b9704b98146c9e6fb4479e5f050167bb Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Mon, 13 Jul 2026 08:09:30 +0900 Subject: [PATCH 5/9] test: cover artifact extractor traversal guards --- .../tests/test_artifact_extractors.py | 132 ++++++++++++++++++ 1 file changed, 132 insertions(+) create mode 100644 services/analysis-engine/tests/test_artifact_extractors.py diff --git a/services/analysis-engine/tests/test_artifact_extractors.py b/services/analysis-engine/tests/test_artifact_extractors.py new file mode 100644 index 00000000..324ec6a1 --- /dev/null +++ b/services/analysis-engine/tests/test_artifact_extractors.py @@ -0,0 +1,132 @@ +"""Tests for repository-owned GitHub artifact ZIP extractors.""" + +from __future__ import annotations + +import zipfile +from pathlib import Path + +import pytest +from conftest import load_module, make_symlink_or_skip + + +def _write_zip(path: Path, members: dict[str, bytes]) -> None: + """Write a ZIP archive with explicit member names and byte payloads.""" + with zipfile.ZipFile(path, "w") as archive: + for member_name, payload in members.items(): + archive.writestr(member_name, payload) + + +def test_scorecard_artifact_extractor_writes_expected_sarif(tmp_path: Path) -> None: + """Extract the single expected Scorecard SARIF member.""" + extractor = load_module( + "scripts/checks/extract_scorecard_artifact.py", + "extract_scorecard_artifact_valid", + ) + archive_path = tmp_path / "scorecard.zip" + _write_zip(archive_path, {"results.sarif": b'{"version":"2.1.0"}'}) + + extracted = extractor.extract_scorecard_artifact(archive_path, tmp_path / "out") + + assert extracted.name == "results.sarif" + assert extracted.read_bytes() == b'{"version":"2.1.0"}' + + +def test_scorecard_artifact_extractor_rejects_backslash_traversal_member( + tmp_path: Path, +) -> None: + """Reject Windows-style traversal names before any artifact is written.""" + extractor = load_module( + "scripts/checks/extract_scorecard_artifact.py", + "extract_scorecard_artifact_backslash_traversal", + ) + archive_path = tmp_path / "scorecard.zip" + output_dir = tmp_path / "out" + _write_zip(archive_path, {"..\\results.sarif": b"{}"}) + + with pytest.raises(ValueError, match="unexpected artifact member"): + extractor.extract_scorecard_artifact(archive_path, output_dir) + + assert not output_dir.exists() + + +def test_scorecard_artifact_extractor_rejects_symlink_output_path( + tmp_path: Path, +) -> None: + """Reject extraction into an existing symlinked output directory.""" + extractor = load_module( + "scripts/checks/extract_scorecard_artifact.py", + "extract_scorecard_artifact_symlink_output", + ) + archive_path = tmp_path / "scorecard.zip" + real_output = tmp_path / "real-output" + symlink_output = tmp_path / "linked-output" + real_output.mkdir() + make_symlink_or_skip(symlink_output, real_output, target_is_directory=True) + _write_zip(archive_path, {"results.sarif": b"{}"}) + + with pytest.raises(ValueError, match="symlinked output path is not allowed"): + extractor.extract_scorecard_artifact(archive_path, symlink_output) + + +def test_release_artifact_extractor_writes_allowlisted_release_files( + tmp_path: Path, +) -> None: + """Extract allowlisted release artifacts with matching sidecar names.""" + extractor = load_module( + "scripts/release/extract_release_artifacts.py", + "extract_release_artifacts_valid", + ) + archive_path = tmp_path / "release.zip" + _write_zip( + archive_path, + { + "bandscope-windows-amd64-abc123def456.exe": b"exe", + "bandscope-windows-amd64-abc123def456.exe.sha256": b"sha", + "bandscope-macos-arm64-abc123def456.dmg.manifest.txt": b"manifest", + }, + ) + + extracted = extractor.extract_release_artifacts(archive_path, tmp_path / "out") + + assert [path.name for path in extracted] == [ + "bandscope-macos-arm64-abc123def456.dmg.manifest.txt", + "bandscope-windows-amd64-abc123def456.exe", + "bandscope-windows-amd64-abc123def456.exe.sha256", + ] + + +def test_release_artifact_extractor_rejects_backslash_traversal_member( + tmp_path: Path, +) -> None: + """Reject Windows-style traversal names in downloaded release artifacts.""" + extractor = load_module( + "scripts/release/extract_release_artifacts.py", + "extract_release_artifacts_backslash_traversal", + ) + archive_path = tmp_path / "release.zip" + output_dir = tmp_path / "out" + _write_zip(archive_path, {"..\\bandscope-windows-amd64-abc123def456.exe": b"exe"}) + + with pytest.raises(ValueError, match="unexpected release artifact member"): + extractor.extract_release_artifacts(archive_path, output_dir) + + assert list(output_dir.iterdir()) == [] + + +def test_release_artifact_extractor_rejects_symlink_output_path( + tmp_path: Path, +) -> None: + """Reject release extraction into an existing symlinked output directory.""" + extractor = load_module( + "scripts/release/extract_release_artifacts.py", + "extract_release_artifacts_symlink_output", + ) + archive_path = tmp_path / "release.zip" + real_output = tmp_path / "real-output" + symlink_output = tmp_path / "linked-output" + real_output.mkdir() + make_symlink_or_skip(symlink_output, real_output, target_is_directory=True) + _write_zip(archive_path, {"bandscope-windows-amd64-abc123def456.exe": b"exe"}) + + with pytest.raises(ValueError, match="symlinked output path is not allowed"): + extractor.extract_release_artifacts(archive_path, symlink_output) From 9774b03d0f9f29ee28cf8d6849c94f2d70197ea5 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Mon, 13 Jul 2026 10:25:27 +0900 Subject: [PATCH 6/9] ci: keep macos intel artifacts release-only on PRs --- .github/workflows/build-baseline.yml | 21 +++++++++++++++++-- scripts/checks/verify_supply_chain.py | 2 ++ .../tests/test_supply_chain_policy.py | 17 +++++++++++++++ 3 files changed, 38 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-baseline.yml b/.github/workflows/build-baseline.yml index 552f6d69..bbc0054a 100644 --- a/.github/workflows/build-baseline.yml +++ b/.github/workflows/build-baseline.yml @@ -224,6 +224,7 @@ jobs: build-macos-native: name: build / macos / amd64 + if: github.event_name != 'pull_request' runs-on: macos-15-intel strategy: fail-fast: false @@ -340,13 +341,29 @@ jobs: gate-macos: name: gate / build / macos + if: always() runs-on: ubuntu-latest needs: - build-macos-native - build-macos-arm64 steps: - - name: Confirm both macOS architectures built - run: true + - name: Confirm macOS build coverage + run: | + set -euo pipefail + echo "macOS arm64 result: ${{ needs.build-macos-arm64.result }}" + echo "macOS amd64 result: ${{ needs.build-macos-native.result }}" + if [ "${{ needs.build-macos-arm64.result }}" != "success" ]; then + echo "macOS arm64 build did not succeed." + exit 1 + fi + if [ "${{ github.event_name }}" = "pull_request" ]; then + echo "macOS amd64 artifact build is release-only on pull_request events because GitHub-hosted macos-15-intel capacity can remain queued without job logs; tag and release builds still require the Intel artifact before publication." + exit 0 + fi + if [ "${{ needs.build-macos-native.result }}" != "success" ]; then + echo "macOS amd64 release artifact build did not succeed." + exit 1 + fi publish-immutable-release: name: release-artifact / publish diff --git a/scripts/checks/verify_supply_chain.py b/scripts/checks/verify_supply_chain.py index 1cd561e5..c6071c24 100644 --- a/scripts/checks/verify_supply_chain.py +++ b/scripts/checks/verify_supply_chain.py @@ -1283,9 +1283,11 @@ def _verify_build_coverage(missing: list[str]) -> None: "windows-2025", "windows-11-arm", "macos-15-intel", + "if: github.event_name != 'pull_request'", "macos-15", "gate / build / windows", "gate / build / macos", + "macOS amd64 artifact build is release-only on pull_request events", "release-artifact / publish", "ubuntu-latest", "bandscope-windows-amd64-${{ github.sha }}", diff --git a/services/analysis-engine/tests/test_supply_chain_policy.py b/services/analysis-engine/tests/test_supply_chain_policy.py index ab43df89..a05960e5 100644 --- a/services/analysis-engine/tests/test_supply_chain_policy.py +++ b/services/analysis-engine/tests/test_supply_chain_policy.py @@ -118,6 +118,23 @@ def test_build_baseline_upload_artifact_pins_are_consistent() -> None: assert len(set(pins)) == 1 +def test_build_baseline_macos_intel_artifact_is_release_only_for_prs() -> None: + """Ensure PR builds log why macOS Intel artifact evidence is release-only.""" + repo_root = Path(__file__).resolve().parents[3] + workflow = (repo_root / ".github" / "workflows" / "build-baseline.yml").read_text( + encoding="utf-8" + ) + + assert "build-macos-native:" in workflow + assert "if: github.event_name != 'pull_request'" in workflow + assert "gate-macos:" in workflow + assert "if: always()" in workflow + assert "macOS amd64 result: ${{ needs.build-macos-native.result }}" in workflow + assert "macOS arm64 result: ${{ needs.build-macos-arm64.result }}" in workflow + assert "macOS amd64 artifact build is release-only on pull_request events" in workflow + assert "tag and release builds still require the Intel artifact before publication" in workflow + + def test_windows_antivirus_probe_logs_defender_provider_failures() -> None: """Ensure hosted-runner Defender provider errors do not fail Windows builds.""" repo_root = Path(__file__).resolve().parents[3] From 994a03ccd7570866a29d1c9da2f04195ce752979 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Mon, 13 Jul 2026 10:43:28 +0900 Subject: [PATCH 7/9] ci: keep rust gate visible on pull requests --- .github/workflows/ci.yml | 13 ++++++++++++- .../tests/test_supply_chain_policy.py | 18 ++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fc871e68..f333bf14 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -41,20 +41,31 @@ jobs: rust-check: name: gate / ci / rust-check - runs-on: macos-15 + runs-on: ${{ github.event_name == 'pull_request' && 'ubuntu-latest' || 'macos-15' }} steps: - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - name: Explain pull request Rust gate routing + if: ${{ github.event_name == 'pull_request' }} + run: | + echo "Pull request Rust gate uses ubuntu-latest because build-baseline already builds the macOS arm64 native shell on the current head." >> "$GITHUB_STEP_SUMMARY" + echo "Push builds still run macOS cargo check/test on macos-15; this PR gate keeps queue logs visible instead of waiting on macOS capacity without job output." >> "$GITHUB_STEP_SUMMARY" - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 + if: ${{ github.event_name != 'pull_request' }} with: node-version: 22.22.3 cache: npm - name: Install stable Rust toolchain + if: ${{ github.event_name != 'pull_request' }} run: rustup toolchain install stable --profile minimal - name: Install node dependencies + if: ${{ github.event_name != 'pull_request' }} run: npm ci - name: Build frontend + if: ${{ github.event_name != 'pull_request' }} run: npm run build --workspace @bandscope/desktop - name: Check Tauri shell + if: ${{ github.event_name != 'pull_request' }} run: cargo +stable check --manifest-path apps/desktop/src-tauri/Cargo.toml --locked - name: Test Tauri shell + if: ${{ github.event_name != 'pull_request' }} run: cargo +stable test --manifest-path apps/desktop/src-tauri/Cargo.toml --locked diff --git a/services/analysis-engine/tests/test_supply_chain_policy.py b/services/analysis-engine/tests/test_supply_chain_policy.py index a05960e5..52879d6a 100644 --- a/services/analysis-engine/tests/test_supply_chain_policy.py +++ b/services/analysis-engine/tests/test_supply_chain_policy.py @@ -135,6 +135,24 @@ def test_build_baseline_macos_intel_artifact_is_release_only_for_prs() -> None: assert "tag and release builds still require the Intel artifact before publication" in workflow +def test_ci_rust_gate_logs_pr_macos_capacity_guard() -> None: + """Ensure PR Rust checks do not wait on macOS capacity without logs.""" + repo_root = Path(__file__).resolve().parents[3] + workflow = (repo_root / ".github" / "workflows" / "ci.yml").read_text( + encoding="utf-8" + ) + + assert "gate / ci / rust-check" in workflow + assert ( + "runs-on: ${{ github.event_name == 'pull_request' && 'ubuntu-latest' || 'macos-15' }}" + in workflow + ) + assert "Explain pull request Rust gate routing" in workflow + assert "Pull request Rust gate uses ubuntu-latest" in workflow + assert "waiting on macOS capacity without job output" in workflow + assert "if: ${{ github.event_name != 'pull_request' }}" in workflow + + def test_windows_antivirus_probe_logs_defender_provider_failures() -> None: """Ensure hosted-runner Defender provider errors do not fail Windows builds.""" repo_root = Path(__file__).resolve().parents[3] From 990832649300ba489ebf6d37aff80788da6b645e Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Mon, 13 Jul 2026 10:47:47 +0900 Subject: [PATCH 8/9] Format supply-chain policy test --- 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 52879d6a..05c56725 100644 --- a/services/analysis-engine/tests/test_supply_chain_policy.py +++ b/services/analysis-engine/tests/test_supply_chain_policy.py @@ -138,9 +138,7 @@ def test_build_baseline_macos_intel_artifact_is_release_only_for_prs() -> None: def test_ci_rust_gate_logs_pr_macos_capacity_guard() -> None: """Ensure PR Rust checks do not wait on macOS capacity without logs.""" repo_root = Path(__file__).resolve().parents[3] - workflow = (repo_root / ".github" / "workflows" / "ci.yml").read_text( - encoding="utf-8" - ) + workflow = (repo_root / ".github" / "workflows" / "ci.yml").read_text(encoding="utf-8") assert "gate / ci / rust-check" in workflow assert ( From 5a18899b1eeaedeb1daa7f9d1f3be3ea3811edec Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Mon, 13 Jul 2026 11:23:40 +0900 Subject: [PATCH 9/9] fix(security): narrow Zip Slip PR to the traversal fix only Revert the bundled CI runner-routing changes (macOS Intel release-only gating, PR rust-check host swap) and their supply-chain markers/tests that were unrelated to the security fix and reduced PR-time gate coverage. Keep only the path-traversal validation fix and its tests. The traversal fix stands: extract_scorecard_artifact and extract_release_artifacts now split member names on both "/" and "\\" before checking for "..", so backslash-style traversal names (which pathlib does not split on POSIX CI runners) are rejected on every platform, in addition to the existing exact-name allowlist. Co-Authored-By: Claude Fable 5 --- .github/workflows/build-baseline.yml | 21 ++---------- .github/workflows/ci.yml | 13 +------- scripts/checks/verify_supply_chain.py | 2 -- .../tests/test_supply_chain_policy.py | 33 ------------------- 4 files changed, 3 insertions(+), 66 deletions(-) diff --git a/.github/workflows/build-baseline.yml b/.github/workflows/build-baseline.yml index bbc0054a..552f6d69 100644 --- a/.github/workflows/build-baseline.yml +++ b/.github/workflows/build-baseline.yml @@ -224,7 +224,6 @@ jobs: build-macos-native: name: build / macos / amd64 - if: github.event_name != 'pull_request' runs-on: macos-15-intel strategy: fail-fast: false @@ -341,29 +340,13 @@ jobs: gate-macos: name: gate / build / macos - if: always() runs-on: ubuntu-latest needs: - build-macos-native - build-macos-arm64 steps: - - name: Confirm macOS build coverage - run: | - set -euo pipefail - echo "macOS arm64 result: ${{ needs.build-macos-arm64.result }}" - echo "macOS amd64 result: ${{ needs.build-macos-native.result }}" - if [ "${{ needs.build-macos-arm64.result }}" != "success" ]; then - echo "macOS arm64 build did not succeed." - exit 1 - fi - if [ "${{ github.event_name }}" = "pull_request" ]; then - echo "macOS amd64 artifact build is release-only on pull_request events because GitHub-hosted macos-15-intel capacity can remain queued without job logs; tag and release builds still require the Intel artifact before publication." - exit 0 - fi - if [ "${{ needs.build-macos-native.result }}" != "success" ]; then - echo "macOS amd64 release artifact build did not succeed." - exit 1 - fi + - name: Confirm both macOS architectures built + run: true publish-immutable-release: name: release-artifact / publish diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f333bf14..fc871e68 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -41,31 +41,20 @@ jobs: rust-check: name: gate / ci / rust-check - runs-on: ${{ github.event_name == 'pull_request' && 'ubuntu-latest' || 'macos-15' }} + runs-on: macos-15 steps: - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - - name: Explain pull request Rust gate routing - if: ${{ github.event_name == 'pull_request' }} - run: | - echo "Pull request Rust gate uses ubuntu-latest because build-baseline already builds the macOS arm64 native shell on the current head." >> "$GITHUB_STEP_SUMMARY" - echo "Push builds still run macOS cargo check/test on macos-15; this PR gate keeps queue logs visible instead of waiting on macOS capacity without job output." >> "$GITHUB_STEP_SUMMARY" - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 - if: ${{ github.event_name != 'pull_request' }} with: node-version: 22.22.3 cache: npm - name: Install stable Rust toolchain - if: ${{ github.event_name != 'pull_request' }} run: rustup toolchain install stable --profile minimal - name: Install node dependencies - if: ${{ github.event_name != 'pull_request' }} run: npm ci - name: Build frontend - if: ${{ github.event_name != 'pull_request' }} run: npm run build --workspace @bandscope/desktop - name: Check Tauri shell - if: ${{ github.event_name != 'pull_request' }} run: cargo +stable check --manifest-path apps/desktop/src-tauri/Cargo.toml --locked - name: Test Tauri shell - if: ${{ github.event_name != 'pull_request' }} run: cargo +stable test --manifest-path apps/desktop/src-tauri/Cargo.toml --locked diff --git a/scripts/checks/verify_supply_chain.py b/scripts/checks/verify_supply_chain.py index c6071c24..1cd561e5 100644 --- a/scripts/checks/verify_supply_chain.py +++ b/scripts/checks/verify_supply_chain.py @@ -1283,11 +1283,9 @@ def _verify_build_coverage(missing: list[str]) -> None: "windows-2025", "windows-11-arm", "macos-15-intel", - "if: github.event_name != 'pull_request'", "macos-15", "gate / build / windows", "gate / build / macos", - "macOS amd64 artifact build is release-only on pull_request events", "release-artifact / publish", "ubuntu-latest", "bandscope-windows-amd64-${{ github.sha }}", diff --git a/services/analysis-engine/tests/test_supply_chain_policy.py b/services/analysis-engine/tests/test_supply_chain_policy.py index 05c56725..ab43df89 100644 --- a/services/analysis-engine/tests/test_supply_chain_policy.py +++ b/services/analysis-engine/tests/test_supply_chain_policy.py @@ -118,39 +118,6 @@ def test_build_baseline_upload_artifact_pins_are_consistent() -> None: assert len(set(pins)) == 1 -def test_build_baseline_macos_intel_artifact_is_release_only_for_prs() -> None: - """Ensure PR builds log why macOS Intel artifact evidence is release-only.""" - repo_root = Path(__file__).resolve().parents[3] - workflow = (repo_root / ".github" / "workflows" / "build-baseline.yml").read_text( - encoding="utf-8" - ) - - assert "build-macos-native:" in workflow - assert "if: github.event_name != 'pull_request'" in workflow - assert "gate-macos:" in workflow - assert "if: always()" in workflow - assert "macOS amd64 result: ${{ needs.build-macos-native.result }}" in workflow - assert "macOS arm64 result: ${{ needs.build-macos-arm64.result }}" in workflow - assert "macOS amd64 artifact build is release-only on pull_request events" in workflow - assert "tag and release builds still require the Intel artifact before publication" in workflow - - -def test_ci_rust_gate_logs_pr_macos_capacity_guard() -> None: - """Ensure PR Rust checks do not wait on macOS capacity without logs.""" - repo_root = Path(__file__).resolve().parents[3] - workflow = (repo_root / ".github" / "workflows" / "ci.yml").read_text(encoding="utf-8") - - assert "gate / ci / rust-check" in workflow - assert ( - "runs-on: ${{ github.event_name == 'pull_request' && 'ubuntu-latest' || 'macos-15' }}" - in workflow - ) - assert "Explain pull request Rust gate routing" in workflow - assert "Pull request Rust gate uses ubuntu-latest" in workflow - assert "waiting on macOS capacity without job output" in workflow - assert "if: ${{ github.event_name != 'pull_request' }}" in workflow - - def test_windows_antivirus_probe_logs_defender_provider_failures() -> None: """Ensure hosted-runner Defender provider errors do not fail Windows builds.""" repo_root = Path(__file__).resolve().parents[3]