@node-core/utils 5.1.0 → 5.2.1

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.
@@ -12,6 +12,10 @@ const securityOptions = {
12
12
  describe: 'Start security release process',
13
13
  type: 'boolean'
14
14
  },
15
+ sync: {
16
+ describe: 'Synchronize an ongoing security release with HackerOne',
17
+ type: 'boolean'
18
+ },
15
19
  'update-date': {
16
20
  describe: 'Updates the target date of the security release',
17
21
  type: 'string'
@@ -46,6 +50,10 @@ export function builder(yargs) {
46
50
  .example(
47
51
  'git node security --start',
48
52
  'Prepare a security release of Node.js')
53
+ .example(
54
+ 'git node security --sync',
55
+ 'Synchronize an ongoing security release with HackerOne'
56
+ )
49
57
  .example(
50
58
  'git node security --update-date=YYYY/MM/DD',
51
59
  'Updates the target date of the security release'
@@ -76,6 +84,9 @@ export function handler(argv) {
76
84
  if (argv.start) {
77
85
  return startSecurityRelease(argv);
78
86
  }
87
+ if (argv.sync) {
88
+ return syncSecurityRelease(argv);
89
+ }
79
90
  if (argv['update-date']) {
80
91
  return updateReleaseDate(argv);
81
92
  }
@@ -142,6 +153,13 @@ async function startSecurityRelease(argv) {
142
153
  return release.start();
143
154
  }
144
155
 
156
+ async function syncSecurityRelease(argv) {
157
+ const logStream = process.stdout.isTTY ? process.stdout : process.stderr;
158
+ const cli = new CLI(logStream);
159
+ const release = new UpdateSecurityRelease(cli);
160
+ return release.sync();
161
+ }
162
+
145
163
  async function notifyPreRelease() {
146
164
  const logStream = process.stdout.isTTY ? process.stdout : process.stderr;
147
165
  const cli = new CLI(logStream);
@@ -18,6 +18,10 @@ releases lines on or shortly after, %RELEASE_DATE% in order to address:
18
18
 
19
19
  %IMPACT%
20
20
 
21
+ It's important to note that End-of-Life versions are always affected when a security release occurs.
22
+ To ensure your system's security, please use an up-to-date version as outlined in our
23
+ [Release Schedule](https://github.com/nodejs/release#release-schedule).
24
+
21
25
  ## Release timing
22
26
 
23
27
  Releases will be available on, or shortly after, %RELEASE_DATE%.
@@ -248,8 +248,7 @@ export default class PrepareSecurityRelease {
248
248
  });
249
249
 
250
250
  try {
251
- const prUrl = dep.replace('https://github.com/', 'https://api.github.com/repos/').replace('pull', 'pulls');
252
- const res = await this.req.getPullRequest(prUrl);
251
+ const res = await this.req.getPullRequest(dep);
253
252
  const { html_url, title } = res;
254
253
  deps.push({
255
254
  name,
package/lib/request.js CHANGED
@@ -77,7 +77,8 @@ export default class Request {
77
77
  return this.json(url, options);
78
78
  }
79
79
 
80
- async getPullRequest(url) {
80
+ async getPullRequest(fullUrl) {
81
+ const prUrl = fullUrl.replace('https://github.com/', 'https://api.github.com/repos/').replace('pull', 'pulls');
81
82
  const options = {
82
83
  method: 'GET',
83
84
  headers: {
@@ -86,7 +87,7 @@ export default class Request {
86
87
  Accept: 'application/vnd.github+json'
87
88
  }
88
89
  };
89
- return this.json(url, options);
90
+ return this.json(prUrl, options);
90
91
  }
91
92
 
92
93
  async createPullRequest(title, body, { owner, repo, head, base }) {
@@ -87,9 +87,8 @@ export async function getSupportedVersions() {
87
87
  return supportedVersions;
88
88
  }
89
89
 
90
- export async function getSummary(reportId, req) {
91
- const { data } = await req.getReport(reportId);
92
- const summaryList = data?.relationships?.summaries?.data;
90
+ export function getSummary(report) {
91
+ const summaryList = report?.relationships?.summaries?.data;
93
92
  if (!summaryList?.length) return;
94
93
  const summaries = summaryList.filter((summary) => summary?.attributes?.category === 'team');
95
94
  if (!summaries?.length) return;
@@ -139,17 +138,25 @@ export async function createIssue(title, content, repository, { cli, req }) {
139
138
  }
140
139
  }
141
140
 
142
- export async function pickReport(report, { cli, req }) {
141
+ export function getReportSeverity(report) {
143
142
  const {
144
- id, attributes: { title, cve_ids },
145
- relationships: { severity, weakness, reporter }
143
+ relationships: { severity, weakness }
146
144
  } = report;
147
- const link = `https://hackerone.com/reports/${id}`;
148
145
  const reportSeverity = {
149
146
  rating: severity?.data?.attributes?.rating || '',
150
147
  cvss_vector_string: severity?.data?.attributes?.cvss_vector_string || '',
151
148
  weakness_id: weakness?.data?.id || ''
152
149
  };
150
+ return reportSeverity;
151
+ }
152
+
153
+ export async function pickReport(report, { cli, req }) {
154
+ const {
155
+ id, attributes: { title, cve_ids },
156
+ relationships: { reporter, custom_field_values }
157
+ } = report;
158
+ const link = `https://hackerone.com/reports/${id}`;
159
+ const reportSeverity = getReportSeverity(report);
153
160
 
154
161
  cli.separator();
155
162
  cli.info(`Report: ${link} - ${title} (${reportSeverity?.rating})`);
@@ -165,19 +172,27 @@ export async function pickReport(report, { cli, req }) {
165
172
  defaultAnswer: await getSupportedVersions()
166
173
  });
167
174
 
168
- let patchAuthors = await cli.prompt(
169
- 'Add github username of the authors of the patch (split by comma if multiple)', {
170
- questionType: 'input',
171
- defaultAnswer: ''
172
- });
173
-
174
- if (!patchAuthors) {
175
- patchAuthors = [];
175
+ let prURL = '';
176
+ let patchAuthors = [];
177
+ if (custom_field_values.data.length) {
178
+ prURL = custom_field_values.data[0].attributes.value;
179
+ const { user } = await req.getPullRequest(prURL);
180
+ patchAuthors = [user.login];
176
181
  } else {
177
- patchAuthors = patchAuthors.split(',').map((p) => p.trim());
182
+ patchAuthors = await cli.prompt(
183
+ 'Add github username of the authors of the patch (split by comma if multiple)', {
184
+ questionType: 'input',
185
+ defaultAnswer: ''
186
+ });
187
+
188
+ if (!patchAuthors) {
189
+ patchAuthors = [];
190
+ } else {
191
+ patchAuthors = patchAuthors.split(',').map((p) => p.trim());
192
+ }
178
193
  }
179
194
 
180
- const summaryContent = await getSummary(id, req);
195
+ const summaryContent = getSummary(report);
181
196
 
182
197
  return {
183
198
  id,
@@ -186,6 +201,7 @@ export async function pickReport(report, { cli, req }) {
186
201
  severity: reportSeverity,
187
202
  summary: summaryContent ?? '',
188
203
  patchAuthors,
204
+ prURL,
189
205
  affectedVersions: versions.split(',').map((v) => v.replace('v', '').trim()),
190
206
  link,
191
207
  reporter: reporter.data.attributes.username
@@ -2,9 +2,12 @@ import {
2
2
  NEXT_SECURITY_RELEASE_FOLDER,
3
3
  NEXT_SECURITY_RELEASE_REPOSITORY,
4
4
  checkoutOnSecurityReleaseBranch,
5
+ checkRemote,
5
6
  commitAndPushVulnerabilitiesJSON,
6
7
  validateDate,
7
- pickReport
8
+ pickReport,
9
+ getReportSeverity,
10
+ getSummary
8
11
  } from './security-release/security-release.js';
9
12
  import fs from 'node:fs';
10
13
  import path from 'node:path';
@@ -18,6 +21,41 @@ export default class UpdateSecurityRelease {
18
21
  this.cli = cli;
19
22
  }
20
23
 
24
+ async sync() {
25
+ checkRemote(this.cli, this.repository);
26
+
27
+ const vulnerabilitiesJSONPath = this.getVulnerabilitiesJSONPath();
28
+ const content = this.readVulnerabilitiesJSON(vulnerabilitiesJSONPath);
29
+ const credentials = await auth({
30
+ github: true,
31
+ h1: true
32
+ });
33
+ const req = new Request(credentials);
34
+ for (let i = 0; i < content.reports.length; ++i) {
35
+ const report = content.reports[i];
36
+ const { data } = await req.getReport(report.id);
37
+ const reportSeverity = getReportSeverity(data);
38
+ const summaryContent = getSummary(data);
39
+ const link = `https://hackerone.com/reports/${report.id}`;
40
+ let prURL = report.prURL;
41
+ if (data.relationships.custom_field_values.data.length) {
42
+ prURL = data.relationships.custom_field_values.data[0].attributes.value;
43
+ }
44
+
45
+ content.reports[i] = {
46
+ ...report,
47
+ title: data.attributes.title,
48
+ cveIds: data.attributes.cve_ids,
49
+ severity: reportSeverity,
50
+ summary: summaryContent ?? report.summary,
51
+ link,
52
+ prURL
53
+ };
54
+ }
55
+ fs.writeFileSync(vulnerabilitiesJSONPath, JSON.stringify(content, null, 2));
56
+ this.cli.ok('Synced vulnerabilities.json with HackerOne');
57
+ }
58
+
21
59
  async updateReleaseDate(releaseDate) {
22
60
  const { cli } = this;
23
61
 
@@ -114,7 +114,7 @@ export default class VotingSession extends Session {
114
114
  const body = 'I would like to close this vote, and for this effect, I\'m revealing my ' +
115
115
  `key part:\n\n${'```'}\n${keyPart}\n${'```'}\n`;
116
116
  if (this.postComment) {
117
- const { html_url } = await this.req.json(`https://api.github.com/repos/${this.owner}/${this.repo}/issues/${this.prid}/comments`, {
117
+ const { message, html_url } = await this.req.json(`https://api.github.com/repos/${this.owner}/${this.repo}/issues/${this.prid}/comments`, {
118
118
  agent: this.req.proxyAgent,
119
119
  method: 'POST',
120
120
  headers: {
@@ -124,13 +124,23 @@ export default class VotingSession extends Session {
124
124
  },
125
125
  body: JSON.stringify({ body })
126
126
  });
127
- this.cli.log('Comment posted at:', html_url);
128
- } else if (isGhAvailable()) {
127
+ if (html_url) {
128
+ this.cli.log(`Comment posted at: ${html_url}`);
129
+ return;
130
+ } else {
131
+ this.cli.warn(message);
132
+ this.cli.error('Failed to post comment');
133
+ }
134
+ }
135
+ if (isGhAvailable()) {
129
136
  this.cli.log('\nRun the following command to post the comment:\n');
130
137
  this.cli.log(
131
138
  `gh pr comment ${this.prid} --repo ${this.owner}/${this.repo} ` +
132
139
  `--body-file - <<'EOF'\n${body}\nEOF`
133
140
  );
141
+ } else {
142
+ this.cli.log('\nPost the following comment on the PR thread:\n');
143
+ this.cli.log(body);
134
144
  }
135
145
  }
136
146
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@node-core/utils",
3
- "version": "5.1.0",
3
+ "version": "5.2.1",
4
4
  "description": "Utilities for Node.js core collaborators",
5
5
  "type": "module",
6
6
  "engines": {