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 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.join(', '),
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: 'Retire.js',
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
- // "vers:npm/1.2.3|>=2.0.0|<5.0.0"
125
- range: "vers:npm/" + (vuln.atOrAbove ? ">=" + vuln.atOrAbove + "|" : "") + "<" + vuln.below,
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:${(0, uuid_1.v4)()}`,
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:${(0, uuid_1.v4)()}`,
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:${(0, uuid_1.v4)()}" version="1">
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, '&amp;')
129
+ .replace(/</g, '&lt;')
130
+ .replace(/>/g, '&gt;')
131
+ .replace(/"/g, '&quot;');
132
+ }
120
133
  function mapLicenses(licenses) {
121
134
  if (!licenses)
122
135
  return '';
@@ -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
@@ -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[];
@@ -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
+ }
@@ -4,7 +4,7 @@ export type LoggerOptions = {
4
4
  verbose: boolean;
5
5
  colors: boolean;
6
6
  outputformat: string;
7
- jsRepo: string;
7
+ jsRepo: string[];
8
8
  path: string;
9
9
  colorwarn: (s: string) => string;
10
10
  };
package/lib/retire.js CHANGED
@@ -4,7 +4,7 @@
4
4
  */
5
5
 
6
6
  var exports = exports || {};
7
- exports.version = '5.4.2';
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
@@ -28,6 +28,7 @@ export type Vulnerability = {
28
28
  summary?: string;
29
29
  githubID?: string;
30
30
  };
31
+ details?: string;
31
32
  info: string[];
32
33
  };
33
34
  export type Component = {
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.4.2",
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.0.0",
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": "rm -rf spec/out && mkdir -p spec/out && jest --ci --runInBand",
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",