diff --git a/CHANGELOG.md b/CHANGELOG.md index 5634e79..41a6187 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,9 @@ ### Security +- Upgrade the pinned CycloneDX SBOM generator to 6.0.0 to fix Windows + workspace-argument shell injection (`GHSA-q69g-4hcv-6jg4`). Guard the + manifest and every lockfile copy against reintroducing a vulnerable release. - Port the dependency security fixes from the Full edition without changing the macOS Author version or capability boundary: require xmldom 0.8.15+, brace-expansion 1.1.18 / 2.1.4 / 5.0.9, and nanoid 3.3.18+. diff --git a/docs/src/changelog.md b/docs/src/changelog.md index b913d55..b410b3e 100644 --- a/docs/src/changelog.md +++ b/docs/src/changelog.md @@ -5,6 +5,8 @@ shipped semver tag; older entries summarize the foundational work by theme. ## Unreleased +- **SBOM tooling security:** Pin CycloneDX npm 6.0.0 to address + `GHSA-q69g-4hcv-6jg4`, with regression coverage for resolved versions. - **Dependency security:** Port patched xmldom, brace-expansion, and nanoid dependencies, and update Electron to 42.11.3 to remove the vulnerable legacy ZIP extractor. The macOS Author version and author-only capability diff --git a/package-lock.json b/package-lock.json index f813446..23a9339 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,7 +14,7 @@ "packages/*" ], "devDependencies": { - "@cyclonedx/cyclonedx-npm": "5.0.0", + "@cyclonedx/cyclonedx-npm": "6.0.0", "@playwright/test": "^1.59.1", "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.2", @@ -1524,9 +1524,9 @@ } }, "node_modules/@cyclonedx/cyclonedx-npm": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@cyclonedx/cyclonedx-npm/-/cyclonedx-npm-5.0.0.tgz", - "integrity": "sha512-pEz/pqYJHQ0ZvY5xFJa+GQ5AjNL7JOEJCXCHymvKo2N1VnzKE1ROpkumduJt8a0DukQjQfBGRXouGQ9xqJtJaQ==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@cyclonedx/cyclonedx-npm/-/cyclonedx-npm-6.0.0.tgz", + "integrity": "sha512-kpWjjV0j5y0mMHUB5dSx1hxweH8K2blSqkgdQ6eHgU7aClB4CcXGhbHtGY6WVHSo3A01Rt7WOLas/wQ1E+tBDg==", "dev": true, "funding": [ { diff --git a/package.json b/package.json index c9f66f6..f427203 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,7 @@ "locales:review": "node scripts/review-locales.mjs" }, "devDependencies": { - "@cyclonedx/cyclonedx-npm": "5.0.0", + "@cyclonedx/cyclonedx-npm": "6.0.0", "@playwright/test": "^1.59.1", "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.2", diff --git a/scripts/release-metadata.test.mjs b/scripts/release-metadata.test.mjs index 61fcc48..a8369f0 100644 --- a/scripts/release-metadata.test.mjs +++ b/scripts/release-metadata.test.mjs @@ -77,6 +77,27 @@ describe('public release metadata', () => { expect(nonPublic).toEqual([]); }); + it('pins a patched CycloneDX SBOM generator without vulnerable resolved copies', async () => { + const [packageJson, lockfile] = await Promise.all([ + read('package.json').then(JSON.parse), + read('package-lock.json').then(JSON.parse), + ]); + const dependency = '@cyclonedx/cyclonedx-npm'; + const pinned = packageJson.devDependencies[dependency]; + const version = /^(\d+)\.\d+\.\d+$/.exec(pinned); + const copies = Object.entries(lockfile.packages) + .filter(([packagePath]) => packagePath.endsWith(`node_modules/${dependency}`)); + + expect(version, pinned).not.toBeNull(); + expect(Number(version?.[1])).toBeGreaterThanOrEqual(6); + expect(lockfile.packages[''].devDependencies[dependency]).toBe(pinned); + expect(copies.length).toBeGreaterThan(0); + for (const [packagePath, metadata] of copies) { + expect(metadata.version, packagePath).toBe(pinned); + expect(metadata.dev, packagePath).toBe(true); + } + }); + it('pins patched brace-expansion versions in every supported major', async () => { const [packageJson, lockfile] = await Promise.all([ read('package.json').then(JSON.parse),