@node-core/utils 5.8.0 → 5.10.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.
@@ -363,8 +363,14 @@ export default class ReleasePreparation extends Session {
363
363
  try {
364
364
  runSync('git', ['rev-parse', tagName]);
365
365
  } catch {
366
- this.cli.startSpinner(`Error parsing git ref ${tagName}, attempting fetching it as a tag`);
367
- runSync('git', ['fetch', this.upstream, 'tag', '-n', tagName]);
366
+ this.cli.error(`Error parsing git ref ${tagName}`);
367
+ this.cli.startSpinner('Attempting fetching it as a tag\n');
368
+ try {
369
+ runSync('git', ['fetch', this.upstream, 'tag', '-n', tagName]);
370
+ } catch (e) {
371
+ this.cli.error(`${e.message}\nRun: git remote update`);
372
+ process.exit(1);
373
+ }
368
374
  this.cli.stopSpinner(`Tag fetched: ${tagName}`);
369
375
  }
370
376
  try {
@@ -765,7 +771,7 @@ export default class ReleasePreparation extends Session {
765
771
  }
766
772
 
767
773
  async prepareLocalBranch() {
768
- const { cli } = this;
774
+ const { cli, isSecurityRelease } = this;
769
775
  if (this.newVersion) {
770
776
  // If the CLI asked for a specific version:
771
777
  const newVersion = semver.parse(this.newVersion);
@@ -789,11 +795,19 @@ export default class ReleasePreparation extends Session {
789
795
  // Otherwise, we need to figure out what's the next version number for the
790
796
  // release line of the branch that's currently checked out.
791
797
  const currentBranch = this.getCurrentBranch();
792
- const match = /^v(\d+)\.x-staging$/.exec(currentBranch);
793
798
 
799
+ // In security releases vN.x branch is the base
800
+ let regex = /^v(\d+)\.x-staging$/;
801
+ let targetBranch = 'vN.x-staging';
802
+ if (isSecurityRelease) {
803
+ regex = /^v(\d+)\.x$/;
804
+ targetBranch = 'vN.x';
805
+ }
806
+
807
+ const match = regex.exec(currentBranch);
794
808
  if (!match) {
795
809
  cli.warn(`Cannot prepare a release from ${currentBranch
796
- }. Switch to a staging branch before proceeding.`);
810
+ }. Switch to a ${targetBranch} branch before proceeding.`);
797
811
  return;
798
812
  }
799
813
  this.stagingBranch = currentBranch;
@@ -131,6 +131,7 @@ export default class ReleasePromotion extends Session {
131
131
  throw new Error('Aborted');
132
132
  }
133
133
  await this.secureTagRelease();
134
+ await this.verifyTagSignature();
134
135
 
135
136
  // Set up for next release.
136
137
  cli.startSpinner('Setting up for next release');
@@ -223,6 +224,28 @@ export default class ReleasePromotion extends Session {
223
224
  this.isLTS ? '=false' : ''} --title=${JSON.stringify(this.releaseTitle)} --notes-file -`);
224
225
  }
225
226
 
227
+ async verifyTagSignature() {
228
+ const { cli, version } = this;
229
+ const [needle, haystack] = await Promise.all([forceRunAsync(
230
+ 'git', ['--no-pager',
231
+ 'log', '-1',
232
+ `refs/tags/v${version}`,
233
+ '--format=* **%an** <<%ae>>\n `%GF`'
234
+ ], { captureStdout: true }), fs.readFile('README.md')]);
235
+ if (haystack.includes(needle)) {
236
+ return;
237
+ }
238
+ cli.warn('Tag was signed with an undocumented identity/key pair!');
239
+ cli.info('Expected to find the following entry in the README:');
240
+ cli.info(needle);
241
+ cli.info('If you are using a subkey, it might be OK.');
242
+ cli.info(`Otherwise consider removing the tag (git tag -d v${version
243
+ }), check your local config, and start the process over.`);
244
+ if (!await cli.prompt('Do you want to proceed anyway?', { defaultAnswer: false })) {
245
+ throw new Error('Aborted');
246
+ }
247
+ }
248
+
226
249
  async verifyPRAttributes() {
227
250
  const { cli, prid, owner, repo, req } = this;
228
251
 
@@ -47,7 +47,7 @@ export default class SecurityBlog extends SecurityRelease {
47
47
  const fileNameExt = fileName + '.md';
48
48
  const preRelease = this.buildPreRelease(template, data);
49
49
 
50
- const pathToBlogPosts = 'apps/site/pages/en/blog/release';
50
+ const pathToBlogPosts = 'apps/site/pages/en/blog/vulnerability';
51
51
  const pathToBannerJson = 'apps/site/site.json';
52
52
 
53
53
  const file = path.resolve(process.cwd(), nodejsOrgFolder, pathToBlogPosts, fileNameExt);
@@ -101,7 +101,7 @@ export default class SecurityBlog extends SecurityRelease {
101
101
  dependencyUpdates: content.dependencies
102
102
  };
103
103
 
104
- const pathToBlogPosts = path.resolve(nodejsOrgFolder, 'apps/site/pages/en/blog/release');
104
+ const pathToBlogPosts = path.resolve(nodejsOrgFolder, 'apps/site/pages/en/blog/vulnerability');
105
105
  const pathToBannerJson = path.resolve(nodejsOrgFolder, 'apps/site/site.json');
106
106
 
107
107
  const preReleasePath = path.resolve(pathToBlogPosts, data.slug + '.md');
@@ -161,7 +161,7 @@ export default class SecurityBlog extends SecurityRelease {
161
161
  link: content.link ?? currentValue.link,
162
162
  type: content.type ?? currentValue.type
163
163
  };
164
- fs.writeFileSync(siteJsonPath, JSON.stringify(siteJson, null, 2));
164
+ fs.writeFileSync(siteJsonPath, JSON.stringify(siteJson, null, 2) + '\n');
165
165
  }
166
166
 
167
167
  formatReleaseDate(releaseDate) {
@@ -250,12 +250,8 @@ export default class SecurityBlog extends SecurityRelease {
250
250
  if (Object.keys(dependencyUpdates).length === 0) return '';
251
251
  let template = '\nThis security release includes the following dependency' +
252
252
  ' updates to address public vulnerabilities:\n';
253
- for (const dependencyUpdate of Object.values(dependencyUpdates)) {
254
- for (const dependency of dependencyUpdate) {
255
- const title = dependency.title.substring(dependency.title.indexOf(':') + ':'.length).trim();
256
- template += `- ${title}\
257
- on ${dependency.affectedVersions.join(', ')}\n`;
258
- }
253
+ for (const [dependency, { versions, affectedVersions }] of Object.entries(dependencyUpdates)) {
254
+ template += `- ${dependency} (${versions.join(', ')}) on ${affectedVersions.join(', ')}\n`;
259
255
  }
260
256
  return template;
261
257
  }
@@ -299,34 +295,37 @@ export default class SecurityBlog extends SecurityRelease {
299
295
  }
300
296
 
301
297
  getImpact(content) {
302
- const impact = content.reports.reduce((acc, report) => {
303
- for (const affectedVersion of report.affectedVersions) {
304
- if (acc[affectedVersion]) {
305
- acc[affectedVersion].push(report);
306
- } else {
307
- acc[affectedVersion] = [report];
308
- }
298
+ const impact = new Map();
299
+ for (const report of content.reports) {
300
+ for (const version of report.affectedVersions) {
301
+ if (!impact.has(version)) impact.set(version, []);
302
+ impact.get(version).push(report);
309
303
  }
310
- return acc;
311
- }, {});
312
-
313
- const impactText = [];
314
- for (const [key, value] of Object.entries(impact)) {
315
- const groupedByRating = Object.values(_.groupBy(value, 'severity.rating'))
316
- .map(severity => {
317
- if (!severity[0]?.severity?.rating) {
318
- this.cli.error(`severity.rating not found for the report ${severity[0].id}. \
319
- Please add it manually before continuing.`);
304
+ }
305
+
306
+ const result = Array.from(impact.entries())
307
+ .sort(([a], [b]) => b.localeCompare(a)) // DESC
308
+ .map(([version, reports]) => {
309
+ const severityCount = new Map();
310
+
311
+ for (const report of reports) {
312
+ const rating = report.severity.rating?.toLowerCase();
313
+ if (!rating) {
314
+ this.cli.error(`severity.rating not found for report ${report.id}.`);
320
315
  process.exit(1);
321
316
  }
322
- const firstSeverityRating = severity[0].severity.rating.toLocaleLowerCase();
323
- return `${severity.length} ${firstSeverityRating} severity issues`;
324
- }).join(', ');
317
+ severityCount.set(rating, (severityCount.get(rating) || 0) + 1);
318
+ }
325
319
 
326
- impactText.push(`The ${key} release line of Node.js is vulnerable to ${groupedByRating}.`);
327
- }
320
+ const groupedByRating = Array.from(severityCount.entries())
321
+ .map(([rating, count]) => `${count} ${rating} severity issues`)
322
+ .join(', ');
323
+
324
+ return `The ${version} release line of Node.js is vulnerable to ${groupedByRating}.`;
325
+ })
326
+ .join('\n');
328
327
 
329
- return impactText.join('\n');
328
+ return result;
330
329
  }
331
330
 
332
331
  getVulnerabilities(content) {
@@ -41,6 +41,9 @@ const fp16Ignore = `!/third_party/fp16
41
41
  const fastFloatReplace = `/third_party/fast_float/src/*
42
42
  !/third_party/fast_float/src/include`;
43
43
 
44
+ const highwayIgnore = `/third_party/highway/src/*
45
+ !/third_party/highway/src/hwy`;
46
+
44
47
  export const v8Deps = [
45
48
  {
46
49
  name: 'trace_event',
@@ -115,5 +118,14 @@ export const v8Deps = [
115
118
  replace: fastFloatReplace
116
119
  },
117
120
  since: 130
121
+ },
122
+ {
123
+ name: 'highway',
124
+ repo: 'third_party/highway/src',
125
+ gitignore: {
126
+ match: '/third_party/highway/src',
127
+ replace: highwayIgnore
128
+ },
129
+ since: 134
118
130
  }
119
131
  ];
@@ -226,8 +226,11 @@ Summary: ${summary}\n`,
226
226
  vectorString: cvss_vector_string
227
227
  }
228
228
  ],
229
+ auto_submit_on_publicly_disclosing_report: true,
230
+ references: ['https://nodejs.org/en/blog/vulnerability'],
231
+ report_id: report.id,
229
232
  weakness_id: Number(weakness_id),
230
- description: title,
233
+ description: report.summary,
231
234
  vulnerability_discovered_at: new Date().toISOString()
232
235
  }
233
236
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@node-core/utils",
3
- "version": "5.8.0",
3
+ "version": "5.10.0",
4
4
  "description": "Utilities for Node.js core collaborators",
5
5
  "type": "module",
6
6
  "engines": {
@@ -34,35 +34,35 @@
34
34
  ],
35
35
  "license": "MIT",
36
36
  "dependencies": {
37
- "@inquirer/prompts": "^6.0.1",
38
- "@listr2/prompt-adapter-enquirer": "^2.0.11",
37
+ "@inquirer/prompts": "^7.2.3",
38
+ "@listr2/prompt-adapter-enquirer": "^2.0.12",
39
39
  "@node-core/caritat": "^1.6.0",
40
40
  "@pkgjs/nv": "^0.2.2",
41
41
  "branch-diff": "^3.1.1",
42
- "chalk": "^5.3.0",
43
- "changelog-maker": "^4.1.1",
42
+ "chalk": "^5.4.1",
43
+ "changelog-maker": "^4.3.1",
44
44
  "cheerio": "^1.0.0",
45
45
  "clipboardy": "^4.0.0",
46
46
  "core-validate-commit": "^4.1.0",
47
47
  "figures": "^6.1.0",
48
- "ghauth": "^6.0.7",
48
+ "ghauth": "^6.0.10",
49
49
  "git-secure-tag": "^2.3.1",
50
50
  "js-yaml": "^4.1.0",
51
- "listr2": "^8.2.4",
51
+ "listr2": "^8.2.5",
52
52
  "lodash": "^4.17.21",
53
53
  "log-symbols": "^7.0.0",
54
- "ora": "^8.1.0",
55
- "replace-in-file": "^8.2.0",
56
- "undici": "^6.19.8",
57
- "which": "^4.0.0",
54
+ "ora": "^8.1.1",
55
+ "replace-in-file": "^8.3.0",
56
+ "undici": "^7.2.2",
57
+ "which": "^5.0.0",
58
58
  "yargs": "^17.7.2"
59
59
  },
60
60
  "devDependencies": {
61
- "@reporters/github": "^1.7.1",
62
- "c8": "^10.1.2",
61
+ "@reporters/github": "^1.7.2",
62
+ "c8": "^10.1.3",
63
63
  "eslint": "^8.57.1",
64
64
  "eslint-config-standard": "^17.1.0",
65
- "eslint-plugin-import": "^2.30.0",
65
+ "eslint-plugin-import": "^2.31.0",
66
66
  "eslint-plugin-n": "^16.6.2",
67
67
  "eslint-plugin-promise": "^6.6.0",
68
68
  "sinon": "^19.0.2"