diff --git a/.github/workflows/nodejs.yml b/.github/workflows/nodejs.yml index f1400f860..56d3a6f61 100644 --- a/.github/workflows/nodejs.yml +++ b/.github/workflows/nodejs.yml @@ -391,7 +391,7 @@ jobs: js-type: [ 'cjs', 'mjs' ] include: - # lowest reasonable number that works - typescript-version: '^3.8' + typescript-version: '^4.0' nodeTypes-version: '^14' js-type: 'cjs' env: diff --git a/HISTORY.md b/HISTORY.md index 68500ed63..3fc1f3fbe 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -18,7 +18,9 @@ All notable changes to this project will be documented in this file. * Serializers and `Bom`-Normalizers will take changed `Models.Bom.tools` into account ([#1152] via [#1163]) * Dependencies * Support `libxmljs2@^0.35` (via [#1173]) + * Use `packageurl-js@^2.0.1`, was `@>=0.0.6 <0.0.8 || ^1` (via [#1142]) +[#1142]: https://github.com/CycloneDX/cyclonedx-javascript-library/pull/1142 [#1152]: https://github.com/CycloneDX/cyclonedx-javascript-library/issues/1152 [#1163]: https://github.com/CycloneDX/cyclonedx-javascript-library/pull/1163 [#1173]: https://github.com/CycloneDX/cyclonedx-javascript-library/pull/1173 diff --git a/package.json b/package.json index a4679c5a2..7dd9d3ff8 100644 --- a/package.json +++ b/package.json @@ -80,7 +80,7 @@ "node": ">=14.0.0" }, "dependencies": { - "packageurl-js": ">=0.0.6 <0.0.8 || ^1", + "packageurl-js": "^2.0.1", "spdx-expression-parse": "^3.0.1 || ^4" }, "optionalDependencies": { diff --git a/src/_helpers/packageUrl.ts b/src/_helpers/packageUrl.ts deleted file mode 100644 index 5d55e52c4..000000000 --- a/src/_helpers/packageUrl.ts +++ /dev/null @@ -1,32 +0,0 @@ -/*! -This file is part of CycloneDX JavaScript Library. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. - -SPDX-License-Identifier: Apache-2.0 -Copyright (c) OWASP Foundation. All Rights Reserved. -*/ - -/** - * Known PURL qualifier names. - * To be used until {@link https://github.com/package-url/packageurl-js/pull/34 | PackageURL PR#34} gets merged and released, - * and {@link https://github.com/package-url/packageurl-js/issues/35 | PackageURL Issue#35} gets sorted out. - * - * For the list/spec of the well-known keys, - * see {@link https://github.com/package-url/purl-spec/blob/master/PURL-SPECIFICATION.rst#known-qualifiers-keyvalue-pairs | known qualifiers key/value-pairs} - */ -export const enum PackageUrlQualifierNames { - DownloadURL = 'download_url', - VcsUrl = 'vcs_url', - Checksum = 'checksum', -} diff --git a/src/factories/fromNodePackageJson.node.ts b/src/factories/fromNodePackageJson.node.ts index 62c60a958..1d32899e8 100644 --- a/src/factories/fromNodePackageJson.node.ts +++ b/src/factories/fromNodePackageJson.node.ts @@ -27,11 +27,11 @@ Copyright (c) OWASP Foundation. All Rights Reserved. */ import type { PackageURL } from 'packageurl-js' +import { PurlQualifierNames } from 'packageurl-js' import {tryCanonicalizeGitUrl} from "../_helpers/gitUrl" import { isNotUndefined } from '../_helpers/notUndefined' import type { PackageJson } from '../_helpers/packageJson' -import { PackageUrlQualifierNames } from '../_helpers/packageUrl' import { ExternalReferenceType } from '../enums/externalReferenceType' import type { Component } from '../models/component' import { ExternalReference } from '../models/externalReference' @@ -137,20 +137,24 @@ export class PackageUrlFactory extends PlainPackageUrlFactory<'npm'> { #finalizeQualifiers (purl: PackageURL): PackageURL { const qualifiers = new Map(Object.entries(purl.qualifiers ?? {})) - const downloadUrl = qualifiers.get(PackageUrlQualifierNames.DownloadURL) + const downloadUrl = qualifiers.get(PurlQualifierNames.DownloadUrl) if (downloadUrl !== undefined) { - qualifiers.delete(PackageUrlQualifierNames.VcsUrl) + qualifiers.delete(PurlQualifierNames.VcsUrl) if (npmDefaultRepositoryMatcher.test(downloadUrl)) { - qualifiers.delete(PackageUrlQualifierNames.DownloadURL) + qualifiers.delete(PurlQualifierNames.DownloadUrl) } } - if (!qualifiers.has(PackageUrlQualifierNames.DownloadURL) && !qualifiers.has(PackageUrlQualifierNames.VcsUrl)) { + if (!qualifiers.has(PurlQualifierNames.DownloadUrl) && !qualifiers.has(PurlQualifierNames.VcsUrl)) { // nothing to base a checksum on - qualifiers.delete(PackageUrlQualifierNames.Checksum) + qualifiers.delete(PurlQualifierNames.Checksum) + } + if (qualifiers.size > 0) { + purl.qualifiers = Object.fromEntries(qualifiers.entries()) + /* @ts-expect-error TS2322 */ + purl.qualifiers.__proto__ = null /* eslint-disable-line no-proto -- intended */ + } else { + purl.qualifiers = undefined } - purl.qualifiers = qualifiers.size > 0 - ? Object.fromEntries(qualifiers.entries()) - : undefined return purl } diff --git a/src/factories/packageUrl.ts b/src/factories/packageUrl.ts index 8789de26b..069e01f72 100644 --- a/src/factories/packageUrl.ts +++ b/src/factories/packageUrl.ts @@ -17,9 +17,8 @@ SPDX-License-Identifier: Apache-2.0 Copyright (c) OWASP Foundation. All Rights Reserved. */ -import { PackageURL } from 'packageurl-js' +import { PackageURL, PurlQualifierNames } from 'packageurl-js' -import { PackageUrlQualifierNames } from '../_helpers/packageUrl' import { ExternalReferenceType } from '../enums/externalReferenceType' import type { Component } from '../models/component' @@ -37,6 +36,8 @@ export class PackageUrlFactory 0) { - qualifiers[PackageUrlQualifierNames.Checksum] = Array.from( + qualifiers[PurlQualifierNames.Checksum] = Array.from( sort ? hashes.sorted() : hashes, diff --git a/tests/integration/Factories.FromNodePackageJson.PackageUrlFactory.test.js b/tests/integration/Factories.FromNodePackageJson.PackageUrlFactory.test.js index ee26404f5..dddc549b7 100644 --- a/tests/integration/Factories.FromNodePackageJson.PackageUrlFactory.test.js +++ b/tests/integration/Factories.FromNodePackageJson.PackageUrlFactory.test.js @@ -251,7 +251,7 @@ suite('integration: Factories.FromNodePackageJson.PackageUrlFactory', () => { vcs_url: 'git+https://foo.bar/repo.git' }, undefined) // expect objet's keys in alphabetical oder, expect sorted hash list - const expectedString = 'pkg:testing/name?checksum=blake3%3Aaa51dcd43d5c6c5203ee16906fd6b35db298b9b2e1de3fce81811d4806b76b7d%2Csha-256%3Ac3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f2&vcs_url=git%2Bhttps%3A//foo.bar/repo.git' + const expectedString = 'pkg:testing/name?checksum=blake3%3Aaa51dcd43d5c6c5203ee16906fd6b35db298b9b2e1de3fce81811d4806b76b7d%2Csha-256%3Ac3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f2&vcs_url=git%2Bhttps%3A%2F%2Ffoo.bar%2Frepo.git' const actual = sut.makeFromComponent(component, true) @@ -287,7 +287,7 @@ suite('integration: Factories.FromNodePackageJson.PackageUrlFactory', () => { download_url: 'https://foo.bar/download-2' }, undefined) // expect objet's keys in alphabetical oder, expect sorted hash list - const expectedString = 'pkg:testing/name?download_url=https%3A//foo.bar/download-2' + const expectedString = 'pkg:testing/name?download_url=https%3A%2F%2Ffoo.bar%2Fdownload-2' const actual1 = sut.makeFromComponent(component1, true) const actual2 = sut.makeFromComponent(component2, true) diff --git a/tests/integration/Factories.PackageUrlFactory.test.js b/tests/integration/Factories.PackageUrlFactory.test.js index fd069d33d..60c4d8409 100644 --- a/tests/integration/Factories.PackageUrlFactory.test.js +++ b/tests/integration/Factories.PackageUrlFactory.test.js @@ -174,7 +174,7 @@ suite('integration: Factories.PackageUrlFactory', () => { vcs_url: 'git+https://foo.bar/repo.git' }, undefined) // expect objet's keys in alphabetical oder, expect sorted hash list - const expectedString = 'pkg:testing/name?checksum=blake3%3Aaa51dcd43d5c6c5203ee16906fd6b35db298b9b2e1de3fce81811d4806b76b7d%2Csha-256%3Ac3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f2&download_url=https%3A//foo.bar/download&vcs_url=git%2Bhttps%3A//foo.bar/repo.git' + const expectedString = 'pkg:testing/name?checksum=blake3%3Aaa51dcd43d5c6c5203ee16906fd6b35db298b9b2e1de3fce81811d4806b76b7d%2Csha-256%3Ac3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f2&download_url=https%3A%2F%2Ffoo.bar%2Fdownload&vcs_url=git%2Bhttps%3A%2F%2Ffoo.bar%2Frepo.git' const actual = sut.makeFromComponent(component, true) @@ -213,7 +213,7 @@ suite('integration: Factories.PackageUrlFactory', () => { vcs_url: 'git+https://foo.bar/repo.git' }, undefined) // expect objet's keys in alphabetical oder, expect sorted hash list - const expectedString = 'pkg:testing/name?download_url=https%3A//foo.bar/download-2&vcs_url=git%2Bhttps%3A//foo.bar/repo.git' + const expectedString = 'pkg:testing/name?download_url=https%3A%2F%2Ffoo.bar%2Fdownload-2&vcs_url=git%2Bhttps%3A%2F%2Ffoo.bar%2Frepo.git' const actual1 = sut.makeFromComponent(component1, true) const actual2 = sut.makeFromComponent(component2, true)