Skip to content

Commit 12d196f

Browse files
authored
fix: PackageUrlFactory omit empty ext-ref urls (#180)
Signed-off-by: Jan Kowalleck <[email protected]>
1 parent f14df2f commit 12d196f

File tree

3 files changed

+81
-5
lines changed

3 files changed

+81
-5
lines changed

HISTORY.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@ All notable changes to this project will be documented in this file.
44

55
## unreleased
66

7+
* Fixed
8+
* `Factories.PackageUrlFactory` omits empty-string URLs for PackageUrl's qualifiers `download_url` & `vcs_url`. (via [#180])
9+
10+
[#180]: https://github.com/CycloneDX/cyclonedx-javascript-library/pull/180
11+
712
## 1.3.3 - 2022-08-16
813

914
* Fixed

src/factories/packageUrl.ts

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,14 +40,25 @@ export class PackageUrlFactory {
4040
const qualifiers: PackageURL['qualifiers'] = {}
4141
let subpath: PackageURL['subpath']
4242

43-
const extRefs = component.externalReferences
44-
for (const extRef of (sort ? extRefs.sorted() : extRefs)) {
43+
// sorting to allow reproducibility: use the last instance for a `extRef.type`, if multiples exist
44+
const extRefs = sort
45+
? component.externalReferences.sorted()
46+
: component.externalReferences
47+
for (const extRef of extRefs) {
48+
const url = extRef.url.toString()
49+
if (url.length <= 0) {
50+
continue
51+
}
52+
// No further need for validation.
53+
// According to https://github.com/package-url/purl-spec/blob/master/PURL-TYPES.rst
54+
// there is no formal requirement to a `..._url`.
55+
// Everything is possible: URL-encoded, not encoded, with schema, without schema
4556
switch (extRef.type) {
4657
case ExternalReferenceType.VCS:
47-
[qualifiers.vcs_url, subpath] = extRef.url.toString().split('#', 2)
58+
[qualifiers.vcs_url, subpath] = url.split('#', 2)
4859
break
4960
case ExternalReferenceType.Distribution:
50-
qualifiers.download_url = extRef.url.toString()
61+
qualifiers.download_url = url
5162
break
5263
}
5364
}

tests/integration/Factories.PackageUrlFactory.test.js

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,24 @@ suite('Factories.PackageUrlFactory', () => {
117117
assert.deepStrictEqual(actual, expected)
118118
})
119119

120+
test('extRef empty url -> omit', () => {
121+
const component = new Models.Component(
122+
Enums.ComponentType.Library,
123+
`name-${salt}`,
124+
{
125+
externalReferences: new Models.ExternalReferenceRepository([
126+
new Models.ExternalReference('', Enums.ExternalReferenceType.VCS),
127+
new Models.ExternalReference('', Enums.ExternalReferenceType.Distribution)
128+
])
129+
}
130+
)
131+
const expected = new PackageURL('testing', undefined, `name-${salt}`, undefined, {}, undefined)
132+
133+
const actual = sut.makeFromComponent(component)
134+
135+
assert.deepStrictEqual(actual, expected)
136+
})
137+
120138
test('hashes -> qualifiers.checksum', () => {
121139
const component = new Models.Component(
122140
Enums.ComponentType.Library,
@@ -134,7 +152,7 @@ suite('Factories.PackageUrlFactory', () => {
134152
assert.deepStrictEqual(actual, expected)
135153
})
136154

137-
test('sorted', () => {
155+
test('sorted hashes', () => {
138156
const component = new Models.Component(
139157
Enums.ComponentType.Library,
140158
'name',
@@ -164,5 +182,47 @@ suite('Factories.PackageUrlFactory', () => {
164182
assert.deepStrictEqual(actual, expectedObject)
165183
assert.deepStrictEqual(actual.toString(), expectedString)
166184
})
185+
186+
test('sorted references', () => {
187+
const component1 = new Models.Component(
188+
Enums.ComponentType.Library,
189+
'name',
190+
{
191+
externalReferences: new Models.ExternalReferenceRepository([
192+
new Models.ExternalReference('https://foo.bar/download-1', Enums.ExternalReferenceType.Distribution),
193+
new Models.ExternalReference('git+https://foo.bar/repo.git', Enums.ExternalReferenceType.VCS),
194+
new Models.ExternalReference('https://foo.bar/download-2', Enums.ExternalReferenceType.Distribution)
195+
])
196+
}
197+
)
198+
const component2 = new Models.Component(
199+
Enums.ComponentType.Library,
200+
'name',
201+
{
202+
externalReferences: new Models.ExternalReferenceRepository([
203+
// different order of extRefs
204+
new Models.ExternalReference('https://foo.bar/download-2', Enums.ExternalReferenceType.Distribution),
205+
new Models.ExternalReference('git+https://foo.bar/repo.git', Enums.ExternalReferenceType.VCS),
206+
new Models.ExternalReference('https://foo.bar/download-1', Enums.ExternalReferenceType.Distribution)
207+
])
208+
}
209+
)
210+
const expectedObject = new PackageURL('testing', undefined, 'name', undefined,
211+
{
212+
// expect sorted hash list
213+
download_url: 'https://foo.bar/download-2',
214+
vcs_url: 'git+https://foo.bar/repo.git'
215+
}, undefined)
216+
// expect objet's keys in alphabetical oder, expect sorted hash list
217+
const expectedString = 'pkg:testing/name?download_url=https://foo.bar/download-2&vcs_url=git+https://foo.bar/repo.git'
218+
219+
const actual1 = sut.makeFromComponent(component1, true)
220+
const actual2 = sut.makeFromComponent(component2, true)
221+
222+
assert.deepStrictEqual(actual1, expectedObject)
223+
assert.deepStrictEqual(actual1.toString(), expectedString)
224+
assert.deepStrictEqual(actual2, expectedObject)
225+
assert.deepStrictEqual(actual2.toString(), expectedString)
226+
})
167227
})
168228
})

0 commit comments

Comments
 (0)