retire 5.4.3 → 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,16 @@
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
+
3
14
  ## [5.4.3]
4
15
 
5
16
  * Remove dependencies that are not needed anymore
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,
@@ -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
  });
@@ -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,
@@ -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)
@@ -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));
@@ -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)
@@ -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.3';
7
+ exports.version = '5.5.0';
8
8
 
9
9
  function isDefined(o) {
10
10
  return typeof o !== 'undefined';
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.3",
5
+ "version": "5.5.0",
6
6
  "license": "Apache-2.0",
7
7
  "repository": {
8
8
  "type": "git",
@@ -14,30 +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
20
  "walkdir": "0.4.1",
21
21
  "zod": "^3.22.4"
22
22
  },
23
23
  "devDependencies": {
24
- "@types/chai": "^4.3.17",
25
- "@types/jest": "^29.5.14",
26
24
  "@types/node": "^18.13.0",
27
25
  "@types/uuid": "^9.0.0",
28
26
  "@typescript-eslint/eslint-plugin": "^7.1.1",
29
27
  "@typescript-eslint/parser": "^7.1.1",
30
- "chai": "^4.x.x",
31
28
  "eslint": "^8.34.0",
32
29
  "eslint-config-prettier": "^9.0.0",
33
30
  "jsonschema": "^1.4.1",
34
31
  "prettier": "^3.1.0",
35
- "ts-jest": "^29.3.4",
36
32
  "ts-node": "^10.9.2",
37
33
  "typescript": "^5.0.4"
38
34
  },
39
35
  "scripts": {
40
- "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\"",
41
37
  "build": "tsc && chmod ugo+x lib/cli.js && cp src/retire.d.ts lib/retire.d.ts && cp ../LICENSE.md .",
42
38
  "watch": "tsc --watch ",
43
39
  "typecheck": "tsc --noEmit",