Skip to content

Commit ce35e84

Browse files
authored
fix: hardenFactories.FromNodePackageJson.PackageUrlFactory's default package repository detection (#1074)
fixes #1073 closes https://github.com/CycloneDX/cyclonedx-javascript-library/security/code-scanning/1 --------- Signed-off-by: Jan Kowalleck <[email protected]>
1 parent 74404ed commit ce35e84

File tree

9 files changed

+139
-9
lines changed

9 files changed

+139
-9
lines changed

.eslintrc.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,18 @@ module.exports = {
108108
project: './tsconfig.json'
109109
}
110110
},
111+
{
112+
files: ['examples/node/typescript/example.cjs/src/*.ts'],
113+
parserOptions: {
114+
project: './examples/node/typescript/example.cjs/tsconfig.json'
115+
}
116+
},
117+
{
118+
files: ['examples/node/typescript/example.mjs/src/*.ts'],
119+
parserOptions: {
120+
project: './examples/node/typescript/example.mjs/tsconfig.json'
121+
}
122+
},
111123
{
112124
files: ['*.js', '*.mjs', '*.cjs'],
113125
plugins: [

.github/workflows/nodejs.yml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,26 @@ jobs:
5959
path: dist.${{ matrix.target }}
6060
if-no-files-found: error
6161

62+
test-lint:
63+
name: test lint
64+
runs-on: ubuntu-latest
65+
timeout-minutes: 10
66+
steps:
67+
- name: Checkout
68+
# see https://github.com/actions/checkout
69+
uses: actions/checkout@v4
70+
- name: Setup Node.js ${{ env.NODE_ACTIVE_LTS }}
71+
# see https://github.com/actions/setup-node
72+
uses: actions/setup-node@v4
73+
with:
74+
node-version: ${{ env.NODE_ACTIVE_LTS }}
75+
# cache: "npm"
76+
# cache-dependency-path: "**/package-lock.json"
77+
- name: setup project
78+
run: npm i --ignore-scripts --include=optional --loglevel=silly
79+
- name: test
80+
run: npm run test:lint
81+
6282
test-standard:
6383
name: test standard
6484
runs-on: ubuntu-latest

HISTORY.md

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

77
<!-- add unreleased items here -->
88

9+
* Fixed
10+
* Hardened `Factories.FromNodePackageJson.PackageUrlFactory`'s default package repository detection ([#1073] via [#1074])
11+
12+
[#1073]: https://github.com/CycloneDX/cyclonedx-javascript-library/issues/1073
13+
[#1074]: https://github.com/CycloneDX/cyclonedx-javascript-library/pull/1074
14+
915
## 6.8.0 -- 2024-05-14
1016

1117
* Added

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,7 @@
194194
"test": "run-p --aggregate-output -lc test:*",
195195
"test:node": "c8 mocha -p",
196196
"test:web": "node -e 'console.log(\"TODO: write web test\")'",
197+
"test:lint": "tsc --noEmit",
197198
"test:standard": "eslint .",
198199
"api-doc": "run-p --aggregate-output -lc api-doc:*",
199200
"api-doc:node": "typedoc --options typedoc.node.json",

src/factories/fromNodePackageJson.node.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -100,12 +100,17 @@ export class ExternalReferenceFactory {
100100
}
101101
}
102102

103-
const npmDefaultRegistryMatcher = /^https?:\/\/registry\.npmjs\.org/
103+
/**
104+
* The default repository is `https://registry.npmjs.org`.
105+
* @see {@link https://github.com/package-url/purl-spec/blob/master/PURL-TYPES.rst#npm}
106+
*/
107+
const npmDefaultRepositoryMatcher = /^https?:\/\/registry\.npmjs\.org(:?\/|$)/
104108

105109
/**
106110
* Node-specific PackageUrlFactory.
111+
* @see {@link https://github.com/package-url/purl-spec/blob/master/PURL-TYPES.rst#npm}
107112
*/
108-
export class PackageUrlFactory extends PlainPackageUrlFactory {
113+
export class PackageUrlFactory extends PlainPackageUrlFactory<'npm'> {
109114
override makeFromComponent (component: Component, sort: boolean = false): PackageURL | undefined {
110115
const purl = super.makeFromComponent(component, sort)
111116
return purl === undefined
@@ -132,7 +137,7 @@ export class PackageUrlFactory extends PlainPackageUrlFactory {
132137
const downloadUrl = qualifiers.get(PackageUrlQualifierNames.DownloadURL)
133138
if (downloadUrl !== undefined) {
134139
qualifiers.delete(PackageUrlQualifierNames.VcsUrl)
135-
if (npmDefaultRegistryMatcher.test(downloadUrl)) {
140+
if (npmDefaultRepositoryMatcher.test(downloadUrl)) {
136141
qualifiers.delete(PackageUrlQualifierNames.DownloadURL)
137142
}
138143
}

src/factories/packageUrl.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,14 @@ import { PackageUrlQualifierNames } from '../_helpers/packageUrl'
2323
import { ExternalReferenceType } from '../enums/externalReferenceType'
2424
import type { Component } from '../models/component'
2525

26-
export class PackageUrlFactory {
27-
readonly #type: PackageURL['type']
26+
export class PackageUrlFactory<PurlType extends PackageURL['type']> {
27+
readonly #type: PurlType
2828

29-
constructor (type: PackageUrlFactory['type']) {
29+
constructor (type: PurlType) {
3030
this.#type = type
3131
}
3232

33-
get type (): PackageURL['type'] {
33+
get type (): PurlType {
3434
return this.#type
3535
}
3636

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
/*!
2+
This file is part of CycloneDX JavaScript Library.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
16+
SPDX-License-Identifier: Apache-2.0
17+
Copyright (c) OWASP Foundation. All Rights Reserved.
18+
*/
19+
20+
const assert = require('assert')
21+
const { suite, test } = require('mocha')
22+
23+
const {
24+
Factories: { FromNodePackageJson: { PackageUrlFactory } },
25+
Enums: { ComponentType, ExternalReferenceType },
26+
Models: { Component, ExternalReference, ExternalReferenceRepository }
27+
} = require('../../')
28+
29+
suite('Factories.FromNodePackageJson.PackageUrlFactory', () => {
30+
suite('makeFromComponent()', () => {
31+
test('plain', () => {
32+
const component = new Component(ComponentType.Library, 'testing')
33+
const purlFac = new PackageUrlFactory('npm')
34+
const actual = purlFac.makeFromComponent(component)
35+
assert.deepEqual(actual, 'TODO')
36+
})
37+
38+
test('strips default repo', () => {
39+
// see https://github.com/package-url/purl-spec/blob/master/PURL-TYPES.rst#npm
40+
const component = new Component(ComponentType.Library, 'testing', {
41+
externalReferences: new ExternalReferenceRepository([
42+
new ExternalReference(
43+
'https://registry.npmjs.org/@cyclonedx/cyclonedx-library/-/cyclonedx-library-1.0.0-beta.2.tgz',
44+
ExternalReferenceType.Distribution
45+
)
46+
])
47+
})
48+
const purlFac = new PackageUrlFactory('npm')
49+
const actual = purlFac.makeFromComponent(component)
50+
assert.deepEqual(actual, {
51+
type: 'npm',
52+
name: 'testing',
53+
namespace: undefined,
54+
version: undefined,
55+
qualifiers: undefined,
56+
subpath: undefined
57+
})
58+
})
59+
60+
test('dont strip BA repo', () => {
61+
// regression test for https://github.com/CycloneDX/cyclonedx-javascript-library/issues/1073
62+
const component = new Component(ComponentType.Library, 'testing', {
63+
externalReferences: new ExternalReferenceRepository([
64+
new ExternalReference(
65+
'https://registry.npmjs.org.badactor.net/@cyclonedx/cyclonedx-library/-/cyclonedx-library-1.0.0-beta.2.tgz',
66+
ExternalReferenceType.Distribution
67+
)
68+
])
69+
})
70+
const purlFac = new PackageUrlFactory('npm')
71+
const actual = purlFac.makeFromComponent(component)
72+
assert.deepEqual(actual,
73+
{
74+
type: 'npm',
75+
name: 'testing',
76+
namespace: undefined,
77+
version: undefined,
78+
qualifiers: {
79+
download_url: 'https://registry.npmjs.org.badactor.net/@cyclonedx/cyclonedx-library/-/cyclonedx-library-1.0.0-beta.2.tgz'
80+
},
81+
subpath: undefined
82+
})
83+
})
84+
})
85+
})

tests/unit/Factories.PackageUrlFactory.spec.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ const {
2626

2727
const { randomString } = require('../_helpers/stringFunctions')
2828

29-
suite('Builders.FromNodePackageJson.ToolBuilder', () => {
29+
suite('Factories.PackageUrlFactory', () => {
3030
test('construct', () => {
3131
const type = randomString(5)
3232

tsconfig.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@
104104
},
105105
"exclude": [
106106
"node_modules",
107-
"**/*.spec.ts", "**/*.test.ts"
107+
"tests", "**/*.spec.ts", "**/*.test.ts",
108+
"examples"
108109
]
109110
}

0 commit comments

Comments
 (0)