retire 5.4.2 → 5.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +15 -0
- package/lib/cli.js +1 -1
- package/lib/repo.js +1 -0
- package/lib/reporters/cyclonedx-1_6-json.js +23 -5
- package/lib/reporters/cyclonedx-json.js +9 -2
- package/lib/reporters/cyclonedx.js +16 -3
- package/lib/reporters/json.js +2 -0
- package/lib/reporters/utils.d.ts +4 -0
- package/lib/reporters/utils.js +9 -0
- package/lib/reporting.d.ts +1 -1
- package/lib/retire.js +4 -1
- package/lib/types.d.ts +1 -0
- package/package.json +3 -8
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [5.5.0]
|
|
4
|
+
|
|
5
|
+
### Improvements
|
|
6
|
+
|
|
7
|
+
- Include the vulnerability repositories used for the scan in the output: as `vulnerabilityRepositories` in the JSON report, and as `retirejs:vulnerability-repository` entries under `metadata.properties` in all CycloneDX reports.
|
|
8
|
+
- CycloneDX 1.6 VEX output now identifies Retire.js as the source of each vulnerability and rating, including the repository URL when a single repository is used.
|
|
9
|
+
|
|
10
|
+
### Bugfixes
|
|
11
|
+
|
|
12
|
+
- CycloneDX 1.6 VEX output was not schema valid: `ratings[].source` was a string rather than an object, and version ranges were reported as `affects[].range` rather than nested under `affects[].versions[]`.
|
|
13
|
+
|
|
14
|
+
## [5.4.3]
|
|
15
|
+
|
|
16
|
+
* Remove dependencies that are not needed anymore
|
|
17
|
+
|
|
3
18
|
## [5.4.2]
|
|
4
19
|
|
|
5
20
|
* Fix build scripts
|
package/lib/cli.js
CHANGED
|
@@ -95,7 +95,7 @@ const scanpath = prg.path ?? prg.jspath ?? '.';
|
|
|
95
95
|
const log = reporting.open({
|
|
96
96
|
colors: !!prg.colors,
|
|
97
97
|
colorwarn,
|
|
98
|
-
jsRepo: jsrepolocation
|
|
98
|
+
jsRepo: jsrepolocation,
|
|
99
99
|
outputformat: prg.outputformat,
|
|
100
100
|
outputpath: prg.outputpath,
|
|
101
101
|
path: scanpath,
|
package/lib/repo.js
CHANGED
|
@@ -98,6 +98,7 @@ function validateRepository(repo, replacer) {
|
|
|
98
98
|
if (ids == 0)
|
|
99
99
|
ctx.addIssue({ code: z.ZodIssueCode.custom, message: 'Must have at least one identifier' });
|
|
100
100
|
}),
|
|
101
|
+
details: z.string().optional(),
|
|
101
102
|
info: z.array(z.string().regex(/^https?:\/\/.+/)),
|
|
102
103
|
})
|
|
103
104
|
.strict();
|
|
@@ -35,9 +35,9 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
const retire = __importStar(require("../retire"));
|
|
37
37
|
const fs = __importStar(require("fs"));
|
|
38
|
-
const uuid_1 = require("uuid");
|
|
39
38
|
const utils_1 = require("./utils");
|
|
40
39
|
const path = __importStar(require("path"));
|
|
40
|
+
const crypto = __importStar(require("crypto"));
|
|
41
41
|
function configureCycloneDXJSONLogger(logger, writer, config, hash) {
|
|
42
42
|
let vulnsFound = false;
|
|
43
43
|
const finalResults = {
|
|
@@ -68,6 +68,12 @@ function configureCycloneDXJSONLogger(logger, writer, config, hash) {
|
|
|
68
68
|
const seen = new Map();
|
|
69
69
|
const vulnerabilitiesCyclone = new Map();
|
|
70
70
|
const includeVEX = config.outputformat === 'cyclonedxJSON1_6_VEX';
|
|
71
|
+
const repositories = (0, utils_1.vulnerabilityRepositories)(config.jsRepo);
|
|
72
|
+
// Only attributable to a single URL when exactly one repository was used
|
|
73
|
+
const retireSource = {
|
|
74
|
+
name: 'Retire.js',
|
|
75
|
+
url: repositories.length === 1 ? repositories[0] : undefined,
|
|
76
|
+
};
|
|
71
77
|
const components = finalResults.data
|
|
72
78
|
.filter((d) => d.results)
|
|
73
79
|
.map((r) => r.results
|
|
@@ -106,13 +112,14 @@ function configureCycloneDXJSONLogger(logger, writer, config, hash) {
|
|
|
106
112
|
const { references, advisories } = mapUrls(vuln);
|
|
107
113
|
vulnerabilitiesCyclone.set(id, {
|
|
108
114
|
id,
|
|
115
|
+
source: retireSource,
|
|
109
116
|
cwes: vuln.cwe.map((c) => parseInt(c.split("-")[1])),
|
|
110
117
|
description: vuln.identifiers.summary,
|
|
111
118
|
advisories: advisories,
|
|
112
119
|
references: references,
|
|
113
120
|
ratings: [
|
|
114
121
|
{
|
|
115
|
-
source:
|
|
122
|
+
source: retireSource,
|
|
116
123
|
severity: vuln.severity,
|
|
117
124
|
},
|
|
118
125
|
],
|
|
@@ -121,8 +128,13 @@ function configureCycloneDXJSONLogger(logger, writer, config, hash) {
|
|
|
121
128
|
}
|
|
122
129
|
vulnerabilitiesCyclone.get(id).affects.push({
|
|
123
130
|
ref: bomRef,
|
|
124
|
-
|
|
125
|
-
|
|
131
|
+
versions: [
|
|
132
|
+
{
|
|
133
|
+
// "vers:npm/1.2.3|>=2.0.0|<5.0.0"
|
|
134
|
+
range: 'vers:npm/' + (vuln.atOrAbove ? '>=' + vuln.atOrAbove + '|' : '') + '<' + vuln.below,
|
|
135
|
+
status: 'affected',
|
|
136
|
+
},
|
|
137
|
+
],
|
|
126
138
|
});
|
|
127
139
|
});
|
|
128
140
|
});
|
|
@@ -145,7 +157,7 @@ function configureCycloneDXJSONLogger(logger, writer, config, hash) {
|
|
|
145
157
|
write(JSON.stringify({
|
|
146
158
|
bomFormat: 'CycloneDX',
|
|
147
159
|
specVersion: '1.6',
|
|
148
|
-
serialNumber: `urn:uuid:${
|
|
160
|
+
serialNumber: `urn:uuid:${crypto.randomUUID()}`,
|
|
149
161
|
version: 1,
|
|
150
162
|
metadata: {
|
|
151
163
|
timestamp: finalResults.start,
|
|
@@ -156,6 +168,12 @@ function configureCycloneDXJSONLogger(logger, writer, config, hash) {
|
|
|
156
168
|
version: retire.version,
|
|
157
169
|
},
|
|
158
170
|
],
|
|
171
|
+
properties: repositories.length
|
|
172
|
+
? repositories.map((repo) => ({
|
|
173
|
+
name: 'retirejs:vulnerability-repository',
|
|
174
|
+
value: repo,
|
|
175
|
+
}))
|
|
176
|
+
: undefined,
|
|
159
177
|
},
|
|
160
178
|
components: components,
|
|
161
179
|
vulnerabilities: includeVEX ? Array.from(vulnerabilitiesCyclone.values()) : undefined,
|
|
@@ -35,9 +35,9 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
const retire = __importStar(require("../retire"));
|
|
37
37
|
const fs = __importStar(require("fs"));
|
|
38
|
-
const uuid_1 = require("uuid");
|
|
39
38
|
const utils_1 = require("./utils");
|
|
40
39
|
const path = __importStar(require("path"));
|
|
40
|
+
const crypto = __importStar(require("crypto"));
|
|
41
41
|
function configureCycloneDXJSONLogger(logger, writer, config, hash) {
|
|
42
42
|
let vulnsFound = false;
|
|
43
43
|
const finalResults = {
|
|
@@ -65,6 +65,7 @@ function configureCycloneDXJSONLogger(logger, writer, config, hash) {
|
|
|
65
65
|
};
|
|
66
66
|
logger.close = function (callback) {
|
|
67
67
|
const write = vulnsFound ? writer.err : writer.out;
|
|
68
|
+
const repositories = (0, utils_1.vulnerabilityRepositories)(config.jsRepo);
|
|
68
69
|
const seen = new Map();
|
|
69
70
|
const components = finalResults.data
|
|
70
71
|
.filter((d) => d.results)
|
|
@@ -111,7 +112,7 @@ function configureCycloneDXJSONLogger(logger, writer, config, hash) {
|
|
|
111
112
|
write(JSON.stringify({
|
|
112
113
|
bomFormat: 'CycloneDX',
|
|
113
114
|
specVersion: '1.4',
|
|
114
|
-
serialNumber: `urn:uuid:${
|
|
115
|
+
serialNumber: `urn:uuid:${crypto.randomUUID()}`,
|
|
115
116
|
version: 1,
|
|
116
117
|
metadata: {
|
|
117
118
|
timestamp: finalResults.start,
|
|
@@ -122,6 +123,12 @@ function configureCycloneDXJSONLogger(logger, writer, config, hash) {
|
|
|
122
123
|
version: retire.version,
|
|
123
124
|
},
|
|
124
125
|
],
|
|
126
|
+
properties: repositories.length
|
|
127
|
+
? repositories.map((repo) => ({
|
|
128
|
+
name: 'retirejs:vulnerability-repository',
|
|
129
|
+
value: repo,
|
|
130
|
+
}))
|
|
131
|
+
: undefined,
|
|
125
132
|
},
|
|
126
133
|
components: components,
|
|
127
134
|
}, undefined, 2));
|
|
@@ -36,8 +36,8 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
36
36
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
37
37
|
const retire = __importStar(require("../retire"));
|
|
38
38
|
const fs = __importStar(require("fs"));
|
|
39
|
-
const uuid_1 = require("uuid");
|
|
40
39
|
const utils_1 = require("./utils");
|
|
40
|
+
const crypto = __importStar(require("crypto"));
|
|
41
41
|
function configureCycloneDXLogger(logger, writer, config, hash) {
|
|
42
42
|
let vulnsFound = false;
|
|
43
43
|
const finalResults = {
|
|
@@ -65,6 +65,10 @@ function configureCycloneDXLogger(logger, writer, config, hash) {
|
|
|
65
65
|
};
|
|
66
66
|
logger.close = function (callback) {
|
|
67
67
|
const write = vulnsFound ? writer.err : writer.out;
|
|
68
|
+
const repositories = (0, utils_1.vulnerabilityRepositories)(config.jsRepo)
|
|
69
|
+
.map((repo) => `
|
|
70
|
+
<property name="retirejs:vulnerability-repository">${escapeXml(repo)}</property>`)
|
|
71
|
+
.join('');
|
|
68
72
|
const seen = new Set();
|
|
69
73
|
const components = finalResults.data
|
|
70
74
|
.filter((d) => d.results.length > 0)
|
|
@@ -100,7 +104,7 @@ function configureCycloneDXLogger(logger, writer, config, hash) {
|
|
|
100
104
|
.join(''))
|
|
101
105
|
.join('');
|
|
102
106
|
write(`<?xml version="1.0"?>
|
|
103
|
-
<bom xmlns="http://cyclonedx.org/schema/bom/1.4" serialNumber="urn:uuid:${
|
|
107
|
+
<bom xmlns="http://cyclonedx.org/schema/bom/1.4" serialNumber="urn:uuid:${crypto.randomUUID()}" version="1">
|
|
104
108
|
<metadata>
|
|
105
109
|
<timestamp>${finalResults.start.toISOString()}</timestamp>
|
|
106
110
|
<tools>
|
|
@@ -109,7 +113,9 @@ function configureCycloneDXLogger(logger, writer, config, hash) {
|
|
|
109
113
|
<name>retire.js</name>
|
|
110
114
|
<version>${retire.version}</version>
|
|
111
115
|
</tool>
|
|
112
|
-
</tools
|
|
116
|
+
</tools>${repositories ? `
|
|
117
|
+
<properties>${repositories}
|
|
118
|
+
</properties>` : ''}
|
|
113
119
|
</metadata>
|
|
114
120
|
<components>${components}
|
|
115
121
|
</components>
|
|
@@ -117,6 +123,13 @@ function configureCycloneDXLogger(logger, writer, config, hash) {
|
|
|
117
123
|
writer.close(callback);
|
|
118
124
|
};
|
|
119
125
|
}
|
|
126
|
+
function escapeXml(value) {
|
|
127
|
+
return value
|
|
128
|
+
.replace(/&/g, '&')
|
|
129
|
+
.replace(/</g, '<')
|
|
130
|
+
.replace(/>/g, '>')
|
|
131
|
+
.replace(/"/g, '"');
|
|
132
|
+
}
|
|
120
133
|
function mapLicenses(licenses) {
|
|
121
134
|
if (!licenses)
|
|
122
135
|
return '';
|
package/lib/reporters/json.js
CHANGED
|
@@ -35,6 +35,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
35
35
|
})();
|
|
36
36
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
37
37
|
const retire = __importStar(require("../retire"));
|
|
38
|
+
const utils_1 = require("./utils");
|
|
38
39
|
exports.default = {
|
|
39
40
|
configure: (logger, writer, config) => {
|
|
40
41
|
const scanStart = Date.now();
|
|
@@ -45,6 +46,7 @@ exports.default = {
|
|
|
45
46
|
messages: [],
|
|
46
47
|
errors: [],
|
|
47
48
|
time: undefined,
|
|
49
|
+
vulnerabilityRepositories: (0, utils_1.vulnerabilityRepositories)(config.jsRepo),
|
|
48
50
|
};
|
|
49
51
|
logger.info = finalResults.messages.push;
|
|
50
52
|
logger.debug = config.verbose
|
package/lib/reporters/utils.d.ts
CHANGED
|
@@ -1,2 +1,6 @@
|
|
|
1
1
|
import { Component } from '../types';
|
|
2
2
|
export declare function generatePURL(component: Component): string;
|
|
3
|
+
/**
|
|
4
|
+
* The vulnerability repositories used for the scan, as configured via --jsrepo.
|
|
5
|
+
*/
|
|
6
|
+
export declare function vulnerabilityRepositories(jsRepo: string[] | undefined): string[];
|
package/lib/reporters/utils.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.generatePURL = generatePURL;
|
|
4
|
+
exports.vulnerabilityRepositories = vulnerabilityRepositories;
|
|
4
5
|
function encodePURLchars(str) {
|
|
5
6
|
return str.replace(/[^A-Za-z0-9.+/=%-]/g, (match) => '%' + ('0' + match.charCodeAt(0).toString(16).toUpperCase()).slice(-2));
|
|
6
7
|
}
|
|
@@ -13,3 +14,11 @@ function generatePURL(component) {
|
|
|
13
14
|
const compName = component.npmname || component.component;
|
|
14
15
|
return `pkg:npm/${encodePURLchars(compName)}@${encodePURLchars(component.version)}`;
|
|
15
16
|
}
|
|
17
|
+
/**
|
|
18
|
+
* The vulnerability repositories used for the scan, as configured via --jsrepo.
|
|
19
|
+
*/
|
|
20
|
+
function vulnerabilityRepositories(jsRepo) {
|
|
21
|
+
if (!jsRepo)
|
|
22
|
+
return [];
|
|
23
|
+
return jsRepo.map((repo) => repo.trim()).filter((repo) => repo.length > 0);
|
|
24
|
+
}
|
package/lib/reporting.d.ts
CHANGED
package/lib/retire.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
var exports = exports || {};
|
|
7
|
-
exports.version = '5.
|
|
7
|
+
exports.version = '5.5.0';
|
|
8
8
|
|
|
9
9
|
function isDefined(o) {
|
|
10
10
|
return typeof o !== 'undefined';
|
|
@@ -116,6 +116,9 @@ function check(results, repo) {
|
|
|
116
116
|
if (vulns[i].cwe) {
|
|
117
117
|
vulnerability.cwe = vulns[i].cwe;
|
|
118
118
|
}
|
|
119
|
+
if (vulns[i].details) {
|
|
120
|
+
vulnerability.details = vulns[i].details;
|
|
121
|
+
}
|
|
119
122
|
result.vulnerabilities = result.vulnerabilities || [];
|
|
120
123
|
result.vulnerabilities.push(vulnerability);
|
|
121
124
|
}
|
package/lib/types.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"author": "Erlend Oftedal <erlend@oftedal.no>",
|
|
3
3
|
"name": "retire",
|
|
4
4
|
"description": "Retire is a tool for detecting use of vulnerable libraries",
|
|
5
|
-
"version": "5.
|
|
5
|
+
"version": "5.5.0",
|
|
6
6
|
"license": "Apache-2.0",
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|
|
@@ -14,31 +14,26 @@
|
|
|
14
14
|
"main": "./lib/retire.js",
|
|
15
15
|
"dependencies": {
|
|
16
16
|
"ansi-colors": "^4.1.1",
|
|
17
|
-
"astronomical": "^3.
|
|
17
|
+
"astronomical": "^3.2.0",
|
|
18
18
|
"commander": "^10.0.1",
|
|
19
19
|
"proxy-agent": "^6.4.0",
|
|
20
|
-
"uuid": "^9.0.1",
|
|
21
20
|
"walkdir": "0.4.1",
|
|
22
21
|
"zod": "^3.22.4"
|
|
23
22
|
},
|
|
24
23
|
"devDependencies": {
|
|
25
|
-
"@types/chai": "^4.3.17",
|
|
26
|
-
"@types/jest": "^29.5.14",
|
|
27
24
|
"@types/node": "^18.13.0",
|
|
28
25
|
"@types/uuid": "^9.0.0",
|
|
29
26
|
"@typescript-eslint/eslint-plugin": "^7.1.1",
|
|
30
27
|
"@typescript-eslint/parser": "^7.1.1",
|
|
31
|
-
"chai": "^4.x.x",
|
|
32
28
|
"eslint": "^8.34.0",
|
|
33
29
|
"eslint-config-prettier": "^9.0.0",
|
|
34
30
|
"jsonschema": "^1.4.1",
|
|
35
31
|
"prettier": "^3.1.0",
|
|
36
|
-
"ts-jest": "^29.3.4",
|
|
37
32
|
"ts-node": "^10.9.2",
|
|
38
33
|
"typescript": "^5.0.4"
|
|
39
34
|
},
|
|
40
35
|
"scripts": {
|
|
41
|
-
"test": "
|
|
36
|
+
"test": "TS_NODE_PROJECT=tsconfig.spec.json node --require ts-node/register --test \"spec/tests/**/*.spec.ts\"",
|
|
42
37
|
"build": "tsc && chmod ugo+x lib/cli.js && cp src/retire.d.ts lib/retire.d.ts && cp ../LICENSE.md .",
|
|
43
38
|
"watch": "tsc --watch ",
|
|
44
39
|
"typecheck": "tsc --noEmit",
|