@node-core/utils 4.2.3 → 4.4.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/README.md CHANGED
@@ -1,5 +1,5 @@
1
1
  # Node.js Core Utilities
2
- [![npm](https://img.shields.io/npm/v/node-core-utils.svg?style=flat-square)](https://npmjs.org/package/node-core-utils)
2
+ [![npm](https://img.shields.io/npm/v/@node-core/utils.svg?style=flat-square)](https://npmjs.org/package/@node-core/utils)
3
3
  [![Build Status](https://img.shields.io/github/actions/workflow/status/nodejs/node-core-utils/nodejs.yml?branch=main&style=flat-square)](https://github.com/nodejs/node-core-utils/workflows/Node.js%20CI/badge.svg?branch=main)
4
4
  [![codecov](https://img.shields.io/codecov/c/github/nodejs/node-core-utils.svg?style=flat-square)](https://codecov.io/gh/nodejs/node-core-utils)
5
5
  [![Known Vulnerabilities](https://snyk.io/test/github/nodejs/node-core-utils/badge.svg?style=flat-square)](https://snyk.io/test/github/nodejs/node-core-utils)
@@ -1,5 +1,8 @@
1
1
  import CLI from '../../lib/cli.js';
2
2
  import SecurityReleaseSteward from '../../lib/prepare_security.js';
3
+ import UpdateSecurityRelease from '../../lib/update_security_release.js';
4
+ import SecurityBlog from '../../lib/security_blog.js';
5
+ import SecurityAnnouncement from '../../lib/security-announcement.js';
3
6
 
4
7
  export const command = 'security [options]';
5
8
  export const describe = 'Manage an in-progress security release or start a new one.';
@@ -8,6 +11,26 @@ const securityOptions = {
8
11
  start: {
9
12
  describe: 'Start security release process',
10
13
  type: 'boolean'
14
+ },
15
+ 'update-date': {
16
+ describe: 'Updates the target date of the security release',
17
+ type: 'string'
18
+ },
19
+ 'add-report': {
20
+ describe: 'Extracts data from HackerOne report and adds it into vulnerabilities.json',
21
+ type: 'string'
22
+ },
23
+ 'remove-report': {
24
+ describe: 'Removes a report from vulnerabilities.json',
25
+ type: 'string'
26
+ },
27
+ 'pre-release': {
28
+ describe: 'Create the pre-release announcement',
29
+ type: 'boolean'
30
+ },
31
+ 'notify-pre-release': {
32
+ describe: 'Notify the community about the security release',
33
+ type: 'boolean'
11
34
  }
12
35
  };
13
36
 
@@ -15,21 +38,94 @@ let yargsInstance;
15
38
 
16
39
  export function builder(yargs) {
17
40
  yargsInstance = yargs;
18
- return yargs.options(securityOptions).example(
19
- 'git node security --start',
20
- 'Prepare a security release of Node.js');
41
+ return yargs.options(securityOptions)
42
+ .example(
43
+ 'git node security --start',
44
+ 'Prepare a security release of Node.js')
45
+ .example(
46
+ 'git node security --update-date=YYYY/MM/DD',
47
+ 'Updates the target date of the security release'
48
+ )
49
+ .example(
50
+ 'git node security --add-report=H1-ID',
51
+ 'Fetches HackerOne report based on ID provided and adds it into vulnerabilities.json'
52
+ )
53
+ .example(
54
+ 'git node security --remove-report=H1-ID',
55
+ 'Removes the Hackerone report based on ID provided from vulnerabilities.json'
56
+ )
57
+ .example(
58
+ 'git node security --pre-release' +
59
+ 'Create the pre-release announcement on the Nodejs.org repo'
60
+ ).example(
61
+ 'git node security --notify-pre-release' +
62
+ 'Notifies the community about the security release'
63
+ );
21
64
  }
22
65
 
23
66
  export function handler(argv) {
24
67
  if (argv.start) {
25
68
  return startSecurityRelease(argv);
26
69
  }
70
+ if (argv['update-date']) {
71
+ return updateReleaseDate(argv);
72
+ }
73
+ if (argv['pre-release']) {
74
+ return createPreRelease(argv);
75
+ }
76
+ if (argv['add-report']) {
77
+ return addReport(argv);
78
+ }
79
+ if (argv['remove-report']) {
80
+ return removeReport(argv);
81
+ }
82
+ if (argv['notify-pre-release']) {
83
+ return notifyPreRelease(argv);
84
+ }
27
85
  yargsInstance.showHelp();
28
86
  }
29
87
 
30
- async function startSecurityRelease(argv) {
88
+ async function removeReport(argv) {
89
+ const reportId = argv['remove-report'];
90
+ const logStream = process.stdout.isTTY ? process.stdout : process.stderr;
91
+ const cli = new CLI(logStream);
92
+ const update = new UpdateSecurityRelease(cli);
93
+ return update.removeReport(reportId);
94
+ }
95
+
96
+ async function addReport(argv) {
97
+ const reportId = argv['add-report'];
98
+ const logStream = process.stdout.isTTY ? process.stdout : process.stderr;
99
+ const cli = new CLI(logStream);
100
+ const update = new UpdateSecurityRelease(cli);
101
+ return update.addReport(reportId);
102
+ }
103
+
104
+ async function updateReleaseDate(argv) {
105
+ const releaseDate = argv['update-date'];
106
+ const logStream = process.stdout.isTTY ? process.stdout : process.stderr;
107
+ const cli = new CLI(logStream);
108
+ const update = new UpdateSecurityRelease(cli);
109
+ return update.updateReleaseDate(releaseDate);
110
+ }
111
+
112
+ async function createPreRelease() {
113
+ const logStream = process.stdout.isTTY ? process.stdout : process.stderr;
114
+ const cli = new CLI(logStream);
115
+ const preRelease = new SecurityBlog(cli);
116
+ return preRelease.createPreRelease();
117
+ }
118
+
119
+ async function startSecurityRelease() {
31
120
  const logStream = process.stdout.isTTY ? process.stdout : process.stderr;
32
121
  const cli = new CLI(logStream);
33
122
  const release = new SecurityReleaseSteward(cli);
34
123
  return release.start();
35
124
  }
125
+
126
+ async function notifyPreRelease() {
127
+ const logStream = process.stdout.isTTY ? process.stdout : process.stderr;
128
+ const cli = new CLI(logStream);
129
+ const preRelease = new SecurityAnnouncement(cli);
130
+ return preRelease.notifyPreRelease();
131
+ }
@@ -0,0 +1,30 @@
1
+ ---
2
+ date: %ANNOUNCEMENT_DATE%
3
+ category: vulnerability
4
+ title: %RELEASE_DATE% Security Releases
5
+ slug: %SLUG%
6
+ layout: blog-post
7
+ author: The Node.js Project
8
+ ---
9
+
10
+ # Summary
11
+
12
+ The Node.js project will release new versions of the %AFFECTED_VERSIONS%
13
+ releases lines on or shortly after, %RELEASE_DATE% in order to address:
14
+
15
+ %VULNERABILITIES%
16
+ %OPENSSL_UPDATES%
17
+ ## Impact
18
+
19
+ %IMPACT%
20
+
21
+ ## Release timing
22
+
23
+ Releases will be available on, or shortly after, %RELEASE_DATE%.
24
+
25
+ ## Contact and future updates
26
+
27
+ The current Node.js security policy can be found at https://nodejs.org/en/security/.
28
+ Please follow the process outlined in https://github.com/nodejs/node/blob/master/SECURITY.md if you wish to report a vulnerability in Node.js.
29
+
30
+ Subscribe to the low-volume announcement-only nodejs-sec mailing list at https://groups.google.com/forum/#!forum/nodejs-sec to stay up to date on security vulnerabilities and security-related releases of Node.js and the projects maintained in the nodejs GitHub organization.
@@ -1,9 +1,21 @@
1
1
  import nv from '@pkgjs/nv';
2
+ import fs from 'node:fs';
3
+ import path from 'node:path';
2
4
  import auth from './auth.js';
3
5
  import Request from './request.js';
4
- import fs from 'node:fs';
6
+ import {
7
+ NEXT_SECURITY_RELEASE_BRANCH,
8
+ NEXT_SECURITY_RELEASE_FOLDER,
9
+ NEXT_SECURITY_RELEASE_REPOSITORY,
10
+ PLACEHOLDERS,
11
+ checkoutOnSecurityReleaseBranch,
12
+ commitAndPushVulnerabilitiesJSON,
13
+ getSummary,
14
+ validateDate
15
+ } from './security-release/security-release.js';
5
16
 
6
17
  export default class SecurityReleaseSteward {
18
+ repository = NEXT_SECURITY_RELEASE_REPOSITORY;
7
19
  constructor(cli) {
8
20
  this.cli = cli;
9
21
  }
@@ -16,71 +28,171 @@ export default class SecurityReleaseSteward {
16
28
  });
17
29
 
18
30
  const req = new Request(credentials);
19
- const create = await cli.prompt(
20
- 'Create the Next Security Release issue?',
21
- { defaultAnswer: true });
22
- if (create) {
23
- const issue = new SecurityReleaseIssue(req);
24
- const content = await issue.buildIssue(cli);
25
- const data = await req.createIssue('Next Security Release', content, {
26
- owner: 'nodejs-private',
27
- repo: 'node-private'
28
- });
29
- if (data.html_url) {
30
- cli.ok('Created: ' + data.html_url);
31
- } else {
32
- cli.error(data);
33
- }
31
+ const release = new PrepareSecurityRelease(req);
32
+ const releaseDate = await release.promptReleaseDate(cli);
33
+ validateDate(releaseDate);
34
+ const createVulnerabilitiesJSON = await release.promptVulnerabilitiesJSON(cli);
35
+
36
+ let securityReleasePRUrl;
37
+ if (createVulnerabilitiesJSON) {
38
+ securityReleasePRUrl = await this.createVulnerabilitiesJSON(req, release, { cli });
34
39
  }
40
+
41
+ const createIssue = await release.promptCreateRelaseIssue(cli);
42
+
43
+ if (createIssue) {
44
+ const content = await release.buildIssue(releaseDate, securityReleasePRUrl);
45
+ await release.createIssue(content, { cli });
46
+ };
47
+
48
+ cli.ok('Done!');
49
+ }
50
+
51
+ async createVulnerabilitiesJSON(req, release, { cli }) {
52
+ // checkout on the next-security-release branch
53
+ checkoutOnSecurityReleaseBranch(cli, this.repository);
54
+
55
+ // choose the reports to include in the security release
56
+ const reports = await release.chooseReports(cli);
57
+
58
+ // create the vulnerabilities.json file in the security-release repo
59
+ const filePath = await release.createVulnerabilitiesJSON(reports, { cli });
60
+
61
+ // review the vulnerabilities.json file
62
+ const review = await release.promptReviewVulnerabilitiesJSON(cli);
63
+
64
+ if (!review) {
65
+ cli.info(`To push the vulnerabilities.json file run:
66
+ - git add ${filePath}
67
+ - git commit -m "chore: create vulnerabilities.json for next security release"
68
+ - git push -u origin ${NEXT_SECURITY_RELEASE_BRANCH}
69
+ - open a PR on ${release.repository.owner}/${release.repository.repo}`);
70
+ return;
71
+ };
72
+
73
+ // commit and push the vulnerabilities.json file
74
+ const commitMessage = 'chore: create vulnerabilities.json for next security release';
75
+ commitAndPushVulnerabilitiesJSON(filePath, commitMessage, { cli, repository: this.repository });
76
+
77
+ const createPr = await release.promptCreatePR(cli);
78
+
79
+ if (!createPr) return;
80
+
81
+ // create pr on the security-release repo
82
+ return release.createPullRequest(req, { cli });
35
83
  }
36
84
  }
37
85
 
38
- class SecurityReleaseIssue {
39
- constructor(req) {
86
+ class PrepareSecurityRelease {
87
+ repository = NEXT_SECURITY_RELEASE_REPOSITORY;
88
+ title = 'Next Security Release';
89
+
90
+ constructor(req, repository) {
40
91
  this.req = req;
41
- this.content = '';
42
- this.title = 'Next Security Release';
43
- this.affectedLines = {};
92
+ if (repository) {
93
+ this.repository = repository;
94
+ }
44
95
  }
45
96
 
46
- getSecurityIssueTemplate() {
47
- return fs.readFileSync(
48
- new URL(
49
- './github/templates/next-security-release.md',
50
- import.meta.url
51
- ),
52
- 'utf-8'
53
- );
97
+ promptCreatePR(cli) {
98
+ return cli.prompt(
99
+ 'Create the Next Security Release PR?',
100
+ { defaultAnswer: true });
54
101
  }
55
102
 
56
- async buildIssue(cli) {
57
- this.content = this.getSecurityIssueTemplate();
58
- cli.info('Getting triaged H1 reports...');
59
- const reports = await this.req.getTriagedReports();
60
- await this.fillReports(cli, reports);
61
-
62
- this.fillAffectedLines(Object.keys(this.affectedLines));
103
+ async getSecurityIssueTemplate() {
104
+ const url = 'https://raw.githubusercontent.com/nodejs/node/main/doc/contributing/security-release-process.md';
105
+ try {
106
+ // fetch document from nodejs/node main so we dont need to keep a copy
107
+ const response = await fetch(url);
108
+ const body = await response.text();
109
+ // remove everything before the Planning section
110
+ const index = body.indexOf('## Planning');
111
+ if (index !== -1) {
112
+ return body.substring(index);
113
+ }
114
+ return body;
115
+ } catch (error) {
116
+ this.cli.error(`Could not retrieve the security issue template from ${url}`);
117
+ }
118
+ }
63
119
 
64
- const target = await cli.prompt('Enter target date in YYYY-MM-DD format:', {
120
+ async promptReleaseDate(cli) {
121
+ const nextWeekDate = new Date();
122
+ nextWeekDate.setDate(nextWeekDate.getDate() + 7);
123
+ // Format the date as YYYY/MM/DD
124
+ const formattedDate = nextWeekDate.toISOString().slice(0, 10).replace(/-/g, '/');
125
+ return cli.prompt('Enter target release date in YYYY/MM/DD format:', {
65
126
  questionType: 'input',
66
- defaultAnswer: 'TBD'
127
+ defaultAnswer: formattedDate
67
128
  });
68
- this.fillTargetDate(target);
129
+ }
130
+
131
+ async promptVulnerabilitiesJSON(cli) {
132
+ return cli.prompt(
133
+ 'Create the vulnerabilities.json?',
134
+ { defaultAnswer: true });
135
+ }
69
136
 
70
- return this.content;
137
+ async promptCreateRelaseIssue(cli) {
138
+ return cli.prompt(
139
+ 'Create the Next Security Release issue?',
140
+ { defaultAnswer: true });
71
141
  }
72
142
 
73
- async fillReports(cli, reports) {
143
+ async promptReviewVulnerabilitiesJSON(cli) {
144
+ return cli.prompt(
145
+ 'Please review vulnerabilities.json and press enter to proceed.',
146
+ { defaultAnswer: true });
147
+ }
148
+
149
+ async buildIssue(releaseDate, securityReleasePRUrl = PLACEHOLDERS.vulnerabilitiesPRURL) {
150
+ const template = await this.getSecurityIssueTemplate();
151
+ const content = template.replace(PLACEHOLDERS.releaseDate, releaseDate)
152
+ .replace(PLACEHOLDERS.vulnerabilitiesPRURL, securityReleasePRUrl);
153
+ return content;
154
+ }
155
+
156
+ async createIssue(content, { cli }) {
157
+ const data = await this.req.createIssue(this.title, content, this.repository);
158
+ if (data.html_url) {
159
+ cli.ok(`Created: ${data.html_url}`);
160
+ } else {
161
+ cli.error(data);
162
+ process.exit(1);
163
+ }
164
+ }
165
+
166
+ async chooseReports(cli) {
167
+ cli.info('Getting triaged H1 reports...');
168
+ const reports = await this.req.getTriagedReports();
74
169
  const supportedVersions = (await nv('supported'))
75
- .map((v) => v.versionName + '.x')
170
+ .map((v) => `${v.versionName}.x`)
76
171
  .join(',');
172
+ const selectedReports = [];
77
173
 
78
- let reportsContent = '';
79
174
  for (const report of reports.data) {
80
- const { id, attributes: { title }, relationships: { severity } } = report;
81
- const reportLevel = severity ? severity.data.attributes.rating : 'TBD';
175
+ const {
176
+ id, attributes: { title, cve_ids, created_at },
177
+ relationships: { severity, weakness, reporter }
178
+ } = report;
179
+ const link = `https://hackerone.com/reports/${id}`;
180
+ let reportSeverity = {
181
+ rating: '',
182
+ cvss_vector_string: '',
183
+ weakness_id: ''
184
+ };
185
+ if (severity?.data?.attributes?.cvss_vector_string) {
186
+ const { cvss_vector_string, rating } = severity.data.attributes;
187
+ reportSeverity = {
188
+ cvss_vector_string,
189
+ rating,
190
+ weakness_id: weakness?.data?.id
191
+ };
192
+ }
193
+
82
194
  cli.separator();
83
- cli.info(`Report: ${id} - ${title} (${reportLevel})`);
195
+ cli.info(`Report: ${link} - ${title} (${reportSeverity?.rating})`);
84
196
  const include = await cli.prompt(
85
197
  'Would you like to include this report to the next security release?',
86
198
  { defaultAnswer: true });
@@ -88,30 +200,72 @@ class SecurityReleaseIssue {
88
200
  continue;
89
201
  }
90
202
 
91
- reportsContent +=
92
- ` * **[${id}](https://hackerone.com/bugs?subject=nodejs&report_id=${id}) - ${title} (TBD) - (${reportLevel})**\n`;
93
203
  const versions = await cli.prompt('Which active release lines this report affects?', {
94
204
  questionType: 'input',
95
205
  defaultAnswer: supportedVersions
96
206
  });
97
- for (const v of versions.split(',')) {
98
- if (!this.affectedLines[v]) this.affectedLines[v] = true;
99
- reportsContent += ` * ${v} - TBD\n`;
100
- }
207
+ const summaryContent = await getSummary(id, this.req);
208
+
209
+ selectedReports.push({
210
+ id,
211
+ title,
212
+ cve_ids,
213
+ severity: reportSeverity,
214
+ summary: summaryContent ?? '',
215
+ affectedVersions: versions.split(',').map((v) => v.replace('v', '').trim()),
216
+ link,
217
+ reporter: reporter.data.attributes.username,
218
+ created_at // when we request CVE we need to input vulnerability_discovered_at
219
+ });
101
220
  }
102
- this.content = this.content.replace('%REPORTS%', reportsContent);
221
+ return selectedReports;
103
222
  }
104
223
 
105
- fillAffectedLines(affectedLines) {
106
- let affected = '';
107
- for (const line of affectedLines) {
108
- affected += ` * ${line} - TBD\n`;
224
+ async createVulnerabilitiesJSON(reports, { cli }) {
225
+ cli.separator('Creating vulnerabilities.json...');
226
+ const file = JSON.stringify({
227
+ reports
228
+ }, null, 2);
229
+
230
+ const folderPath = path.join(process.cwd(), NEXT_SECURITY_RELEASE_FOLDER);
231
+ try {
232
+ await fs.accessSync(folderPath);
233
+ } catch (error) {
234
+ await fs.mkdirSync(folderPath, { recursive: true });
109
235
  }
110
- this.content =
111
- this.content.replace('%AFFECTED_LINES%', affected);
236
+
237
+ const fullPath = path.join(folderPath, 'vulnerabilities.json');
238
+ fs.writeFileSync(fullPath, file);
239
+ cli.ok(`Created ${fullPath} `);
240
+
241
+ return fullPath;
112
242
  }
113
243
 
114
- fillTargetDate(date) {
115
- this.content = this.content.replace('%RELEASE_DATE%', date);
244
+ async createPullRequest(req, { cli }) {
245
+ const { owner, repo } = this.repository;
246
+ const response = await req.createPullRequest(
247
+ this.title,
248
+ 'List of vulnerabilities to be included in the next security release',
249
+ {
250
+ owner,
251
+ repo,
252
+ base: 'main',
253
+ head: 'next-security-release'
254
+ }
255
+
256
+ );
257
+ const url = response?.html_url;
258
+ if (url) {
259
+ cli.ok(`Created: ${url}`);
260
+ return url;
261
+ }
262
+ if (response?.errors) {
263
+ for (const error of response.errors) {
264
+ cli.error(error.message);
265
+ }
266
+ } else {
267
+ cli.error(response);
268
+ }
269
+ process.exit(1);
116
270
  }
117
271
  }
package/lib/request.js CHANGED
@@ -77,6 +77,25 @@ export default class Request {
77
77
  return this.json(url, options);
78
78
  }
79
79
 
80
+ async createPullRequest(title, body, { owner, repo, head, base }) {
81
+ const url = `https://api.github.com/repos/${owner}/${repo}/pulls`;
82
+ const options = {
83
+ method: 'POST',
84
+ headers: {
85
+ Authorization: `Basic ${this.credentials.github}`,
86
+ 'User-Agent': 'node-core-utils',
87
+ Accept: 'application/vnd.github+json'
88
+ },
89
+ body: JSON.stringify({
90
+ title,
91
+ body,
92
+ head,
93
+ base
94
+ })
95
+ };
96
+ return this.json(url, options);
97
+ }
98
+
80
99
  async gql(name, variables, path) {
81
100
  const query = this.loadQuery(name);
82
101
  if (path) {
@@ -113,6 +132,19 @@ export default class Request {
113
132
  return this.json(url, options);
114
133
  }
115
134
 
135
+ async getReport(reportId) {
136
+ const url = `https://api.hackerone.com/v1/reports/${reportId}`;
137
+ const options = {
138
+ method: 'GET',
139
+ headers: {
140
+ Authorization: `Basic ${this.credentials.h1}`,
141
+ 'User-Agent': 'node-core-utils',
142
+ Accept: 'application/json'
143
+ }
144
+ };
145
+ return this.json(url, options);
146
+ }
147
+
116
148
  // This is for github v4 API queries, for other types of queries
117
149
  // use .text or .json
118
150
  async query(query, variables) {
@@ -0,0 +1,83 @@
1
+ import {
2
+ NEXT_SECURITY_RELEASE_REPOSITORY,
3
+ checkoutOnSecurityReleaseBranch,
4
+ getVulnerabilitiesJSON,
5
+ validateDate,
6
+ formatDateToYYYYMMDD
7
+ } from './security-release/security-release.js';
8
+ import auth from './auth.js';
9
+ import Request from './request.js';
10
+
11
+ export default class SecurityAnnouncement {
12
+ repository = NEXT_SECURITY_RELEASE_REPOSITORY;
13
+ req;
14
+ constructor(cli) {
15
+ this.cli = cli;
16
+ }
17
+
18
+ async notifyPreRelease() {
19
+ const { cli } = this;
20
+
21
+ const credentials = await auth({
22
+ github: true,
23
+ h1: true
24
+ });
25
+
26
+ this.req = new Request(credentials);
27
+
28
+ // checkout on security release branch
29
+ checkoutOnSecurityReleaseBranch(cli, this.repository);
30
+ // read vulnerabilities JSON file
31
+ const content = getVulnerabilitiesJSON(cli);
32
+ // validate the release date read from vulnerabilities JSON
33
+ if (!content.releaseDate) {
34
+ cli.error('Release date is not set in vulnerabilities.json,' +
35
+ ' run `git node security --update-date=YYYY/MM/DD` to set the release date.');
36
+ process.exit(1);
37
+ }
38
+
39
+ validateDate(content.releaseDate);
40
+ const releaseDate = new Date(content.releaseDate);
41
+
42
+ await Promise.all([this.createDockerNodeIssue(releaseDate),
43
+ this.createBuildWGIssue(releaseDate)]);
44
+ }
45
+
46
+ async createBuildWGIssue(releaseDate) {
47
+ const repository = {
48
+ owner: 'nodejs',
49
+ repo: 'build'
50
+ };
51
+
52
+ const { title, content } = this.createPreleaseAnnouncementIssue(releaseDate, 'build');
53
+ await this.createIssue(title, content, repository);
54
+ }
55
+
56
+ createPreleaseAnnouncementIssue(releaseDate, team) {
57
+ const title = `[NEXT-SECURITY-RELEASE] Heads up on upcoming Node.js\
58
+ security release ${formatDateToYYYYMMDD(releaseDate)}`;
59
+ const content = 'As per security release workflow,' +
60
+ ` creating issue to give the ${team} team a heads up.`;
61
+ return { title, content };
62
+ }
63
+
64
+ async createDockerNodeIssue(releaseDate) {
65
+ const repository = {
66
+ owner: 'nodejs',
67
+ repo: 'docker-node'
68
+ };
69
+
70
+ const { title, content } = this.createPreleaseAnnouncementIssue(releaseDate, 'docker');
71
+ await this.createIssue(title, content, repository);
72
+ }
73
+
74
+ async createIssue(title, content, repository) {
75
+ const data = await this.req.createIssue(title, content, repository);
76
+ if (data.html_url) {
77
+ this.cli.ok(`Created: ${data.html_url}`);
78
+ } else {
79
+ this.cli.error(data);
80
+ process.exit(1);
81
+ }
82
+ }
83
+ }
@@ -0,0 +1,109 @@
1
+ import { runSync } from '../run.js';
2
+ import nv from '@pkgjs/nv';
3
+ import fs from 'node:fs';
4
+ import path from 'node:path';
5
+
6
+ export const NEXT_SECURITY_RELEASE_BRANCH = 'next-security-release';
7
+ export const NEXT_SECURITY_RELEASE_FOLDER = 'security-release/next-security-release';
8
+
9
+ export const NEXT_SECURITY_RELEASE_REPOSITORY = {
10
+ owner: 'nodejs-private',
11
+ repo: 'security-release'
12
+ };
13
+
14
+ export const PLACEHOLDERS = {
15
+ releaseDate: '%RELEASE_DATE%',
16
+ vulnerabilitiesPRURL: '%VULNERABILITIES_PR_URL%',
17
+ preReleasePrivate: '%PRE_RELEASE_PRIV%',
18
+ postReleasePrivate: '%POS_RELEASE_PRIV%',
19
+ affectedLines: '%AFFECTED_LINES%',
20
+ annoucementDate: '%ANNOUNCEMENT_DATE%',
21
+ slug: '%SLUG%',
22
+ affectedVersions: '%AFFECTED_VERSIONS%',
23
+ openSSLUpdate: '%OPENSSL_UPDATES%',
24
+ impact: '%IMPACT%',
25
+ vulnerabilities: '%VULNERABILITIES%'
26
+ };
27
+
28
+ export function checkRemote(cli, repository) {
29
+ const remote = runSync('git', ['ls-remote', '--get-url', 'origin']).trim();
30
+ const { owner, repo } = repository;
31
+ const securityReleaseOrigin = [
32
+ `https://github.com/${owner}/${repo}.git`,
33
+ `git@github.com:${owner}/${repo}.git`
34
+ ];
35
+
36
+ if (!securityReleaseOrigin.includes(remote)) {
37
+ cli.error(`Wrong repository! It should be ${securityReleaseOrigin}`);
38
+ process.exit(1);
39
+ }
40
+ }
41
+
42
+ export function checkoutOnSecurityReleaseBranch(cli, repository) {
43
+ checkRemote(cli, repository);
44
+ const currentBranch = runSync('git', ['branch', '--show-current']).trim();
45
+ cli.info(`Current branch: ${currentBranch} `);
46
+
47
+ if (currentBranch !== NEXT_SECURITY_RELEASE_BRANCH) {
48
+ runSync('git', ['checkout', '-B', NEXT_SECURITY_RELEASE_BRANCH]);
49
+ cli.ok(`Checkout on branch: ${NEXT_SECURITY_RELEASE_BRANCH} `);
50
+ };
51
+ }
52
+
53
+ export function commitAndPushVulnerabilitiesJSON(filePath, commitMessage, { cli, repository }) {
54
+ checkRemote(cli, repository);
55
+
56
+ if (Array.isArray(filePath)) {
57
+ for (const path of filePath) {
58
+ runSync('git', ['add', path]);
59
+ }
60
+ } else {
61
+ runSync('git', ['add', filePath]);
62
+ }
63
+
64
+ runSync('git', ['commit', '-m', commitMessage]);
65
+ runSync('git', ['push', '-u', 'origin', NEXT_SECURITY_RELEASE_BRANCH]);
66
+ cli.ok(`Pushed commit: ${commitMessage} to ${NEXT_SECURITY_RELEASE_BRANCH}`);
67
+ }
68
+
69
+ export async function getSupportedVersions() {
70
+ const supportedVersions = (await nv('supported'))
71
+ .map((v) => `${v.versionName}.x`)
72
+ .join(',');
73
+ return supportedVersions;
74
+ }
75
+
76
+ export async function getSummary(reportId, req) {
77
+ const { data } = await req.getReport(reportId);
78
+ const summaryList = data?.relationships?.summaries?.data;
79
+ if (!summaryList?.length) return;
80
+ const summaries = summaryList.filter((summary) => summary?.attributes?.category === 'team');
81
+ if (!summaries?.length) return;
82
+ return summaries?.[0].attributes?.content;
83
+ }
84
+
85
+ export function getVulnerabilitiesJSON(cli) {
86
+ const vulnerabilitiesJSONPath = path.join(process.cwd(),
87
+ NEXT_SECURITY_RELEASE_FOLDER, 'vulnerabilities.json');
88
+ cli.startSpinner(`Reading vulnerabilities.json from ${vulnerabilitiesJSONPath}..`);
89
+ const file = JSON.parse(fs.readFileSync(vulnerabilitiesJSONPath, 'utf-8'));
90
+ cli.stopSpinner(`Done reading vulnerabilities.json from ${vulnerabilitiesJSONPath}`);
91
+ return file;
92
+ }
93
+
94
+ export function validateDate(releaseDate) {
95
+ const value = new Date(releaseDate).valueOf();
96
+ if (Number.isNaN(value) || value < 0) {
97
+ throw new Error('Invalid date format');
98
+ }
99
+ }
100
+
101
+ export function formatDateToYYYYMMDD(date) {
102
+ // Get year, month, and day
103
+ const year = date.getFullYear();
104
+ const month = String(date.getMonth() + 1).padStart(2, '0'); // Months are zero-based
105
+ const day = String(date.getDate()).padStart(2, '0');
106
+
107
+ // Concatenate year, month, and day with slashes
108
+ return `${year}/${month}/${day}`;
109
+ }
@@ -0,0 +1,182 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import _ from 'lodash';
4
+ import {
5
+ PLACEHOLDERS,
6
+ getVulnerabilitiesJSON,
7
+ checkoutOnSecurityReleaseBranch,
8
+ NEXT_SECURITY_RELEASE_REPOSITORY,
9
+ validateDate
10
+ } from './security-release/security-release.js';
11
+
12
+ export default class SecurityBlog {
13
+ repository = NEXT_SECURITY_RELEASE_REPOSITORY;
14
+ constructor(cli) {
15
+ this.cli = cli;
16
+ }
17
+
18
+ async createPreRelease() {
19
+ const { cli } = this;
20
+
21
+ // checkout on security release branch
22
+ checkoutOnSecurityReleaseBranch(cli, this.repository);
23
+
24
+ // read vulnerabilities JSON file
25
+ const content = getVulnerabilitiesJSON(cli);
26
+ // validate the release date read from vulnerabilities JSON
27
+ if (!content.releaseDate) {
28
+ cli.error('Release date is not set in vulnerabilities.json,' +
29
+ ' run `git node security --update-date=YYYY/MM/DD` to set the release date.');
30
+ process.exit(1);
31
+ }
32
+
33
+ validateDate(content.releaseDate);
34
+ const releaseDate = new Date(content.releaseDate);
35
+
36
+ const template = this.getSecurityPreReleaseTemplate();
37
+ const data = {
38
+ annoucementDate: await this.getAnnouncementDate(cli),
39
+ releaseDate: this.formatReleaseDate(releaseDate),
40
+ affectedVersions: this.getAffectedVersions(content),
41
+ vulnerabilities: this.getVulnerabilities(content),
42
+ slug: this.getSlug(releaseDate),
43
+ impact: this.getImpact(content),
44
+ openSSLUpdate: await this.promptOpenSSLUpdate(cli)
45
+ };
46
+ const month = releaseDate.toLocaleString('en-US', { month: 'long' }).toLowerCase();
47
+ const year = releaseDate.getFullYear();
48
+ const fileName = `${month}-${year}-security-releases.md`;
49
+ const preRelease = this.buildPreRelease(template, data);
50
+ const file = path.join(process.cwd(), fileName);
51
+ fs.writeFileSync(file, preRelease);
52
+ cli.ok(`Pre-release announcement file created at ${file}`);
53
+ }
54
+
55
+ promptOpenSSLUpdate(cli) {
56
+ return cli.prompt('Does this security release containt OpenSSL updates?', {
57
+ defaultAnswer: true
58
+ });
59
+ }
60
+
61
+ formatReleaseDate(releaseDate) {
62
+ const options = {
63
+ weekday: 'long',
64
+ month: 'long',
65
+ day: 'numeric',
66
+ year: 'numeric'
67
+ };
68
+ return releaseDate.toLocaleDateString('en-US', options);
69
+ }
70
+
71
+ buildPreRelease(template, data) {
72
+ const {
73
+ annoucementDate,
74
+ releaseDate,
75
+ affectedVersions,
76
+ vulnerabilities,
77
+ slug,
78
+ impact,
79
+ openSSLUpdate
80
+ } = data;
81
+ return template.replaceAll(PLACEHOLDERS.annoucementDate, annoucementDate)
82
+ .replaceAll(PLACEHOLDERS.slug, slug)
83
+ .replaceAll(PLACEHOLDERS.affectedVersions, affectedVersions)
84
+ .replaceAll(PLACEHOLDERS.vulnerabilities, vulnerabilities)
85
+ .replaceAll(PLACEHOLDERS.releaseDate, releaseDate)
86
+ .replaceAll(PLACEHOLDERS.impact, impact)
87
+ .replaceAll(PLACEHOLDERS.openSSLUpdate, this.getOpenSSLUpdateTemplate(openSSLUpdate));
88
+ }
89
+
90
+ getOpenSSLUpdateTemplate(openSSLUpdate) {
91
+ if (openSSLUpdate) {
92
+ return '\n## OpenSSL Security updates\n\n' +
93
+ 'This security release includes OpenSSL security updates\n';
94
+ }
95
+ return '';
96
+ }
97
+
98
+ getSlug(releaseDate) {
99
+ const month = releaseDate.toLocaleString('en-US', { month: 'long' });
100
+ const year = releaseDate.getFullYear();
101
+ return `${month.toLocaleLowerCase()}-${year}-security-releases`;
102
+ }
103
+
104
+ async getAnnouncementDate(cli) {
105
+ try {
106
+ const date = await this.promptAnnouncementDate(cli);
107
+ validateDate(date);
108
+ return new Date(date).toISOString();
109
+ } catch (error) {
110
+ return PLACEHOLDERS.annoucementDate;
111
+ }
112
+ }
113
+
114
+ promptAnnouncementDate(cli) {
115
+ const today = new Date().toISOString().substring(0, 10).replace(/-/g, '/');
116
+ return cli.prompt('When is the security release going to be announced? ' +
117
+ 'Enter in YYYY/MM/DD format:', {
118
+ questionType: 'input',
119
+ defaultAnswer: today
120
+ });
121
+ }
122
+
123
+ getImpact(content) {
124
+ const impact = content.reports.reduce((acc, report) => {
125
+ for (const affectedVersion of report.affectedVersions) {
126
+ if (acc[affectedVersion]) {
127
+ acc[affectedVersion].push(report);
128
+ } else {
129
+ acc[affectedVersion] = [report];
130
+ }
131
+ }
132
+ return acc;
133
+ }, {});
134
+
135
+ const impactText = [];
136
+ for (const [key, value] of Object.entries(impact)) {
137
+ const groupedByRating = Object.values(_.groupBy(value, 'severity.rating'))
138
+ .map(severity => {
139
+ if (!severity[0]?.severity?.rating) {
140
+ this.cli.error(`severity.rating not found for the report ${severity[0].id}. \
141
+ Please add it manually before continuing.`);
142
+ process.exit(1);
143
+ }
144
+ const firstSeverityRating = severity[0].severity.rating.toLocaleLowerCase();
145
+ return `${severity.length} ${firstSeverityRating} severity issues`;
146
+ }).join(', ');
147
+
148
+ impactText.push(`The ${key} release line of Node.js is vulnerable to ${groupedByRating}.`);
149
+ }
150
+
151
+ return impactText.join('\n');
152
+ }
153
+
154
+ getVulnerabilities(content) {
155
+ const grouped = _.groupBy(content.reports, 'severity.rating');
156
+ const text = [];
157
+ for (const [key, value] of Object.entries(grouped)) {
158
+ text.push(`- ${value.length} ${key.toLocaleLowerCase()} severity issues.`);
159
+ }
160
+ return text.join('\n');
161
+ }
162
+
163
+ getAffectedVersions(content) {
164
+ const affectedVersions = new Set();
165
+ for (const report of Object.values(content.reports)) {
166
+ for (const affectedVersion of report.affectedVersions) {
167
+ affectedVersions.add(affectedVersion);
168
+ }
169
+ }
170
+ return Array.from(affectedVersions).join(', ');
171
+ }
172
+
173
+ getSecurityPreReleaseTemplate() {
174
+ return fs.readFileSync(
175
+ new URL(
176
+ './github/templates/security-pre-release.md',
177
+ import.meta.url
178
+ ),
179
+ 'utf-8'
180
+ );
181
+ }
182
+ }
@@ -34,6 +34,10 @@ const abseilIgnore = `!/third_party/abseil-cpp
34
34
  /third_party/abseil-cpp/.github
35
35
  /third_party/abseil-cpp/ci`;
36
36
 
37
+ const fp16Ignore = `!/third_party/fp16
38
+ /third_party/fp16/src/*
39
+ !/third_party/fp16/src/include`;
40
+
37
41
  export const v8Deps = [
38
42
  {
39
43
  name: 'trace_event',
@@ -92,5 +96,11 @@ export const v8Deps = [
92
96
  repo: 'third_party/abseil-cpp',
93
97
  gitignore: abseilIgnore,
94
98
  since: 121
99
+ },
100
+ {
101
+ name: 'fp16',
102
+ repo: 'third_party/fp16/src',
103
+ gitignore: fp16Ignore,
104
+ since: 124
95
105
  }
96
106
  ];
@@ -0,0 +1,138 @@
1
+ import {
2
+ NEXT_SECURITY_RELEASE_FOLDER,
3
+ NEXT_SECURITY_RELEASE_REPOSITORY,
4
+ checkoutOnSecurityReleaseBranch,
5
+ commitAndPushVulnerabilitiesJSON,
6
+ getSupportedVersions,
7
+ getSummary,
8
+ validateDate
9
+ } from './security-release/security-release.js';
10
+ import fs from 'node:fs';
11
+ import path from 'node:path';
12
+ import auth from './auth.js';
13
+ import Request from './request.js';
14
+
15
+ export default class UpdateSecurityRelease {
16
+ repository = NEXT_SECURITY_RELEASE_REPOSITORY;
17
+ constructor(cli) {
18
+ this.cli = cli;
19
+ }
20
+
21
+ async updateReleaseDate(releaseDate) {
22
+ const { cli } = this;
23
+
24
+ try {
25
+ validateDate(releaseDate);
26
+ } catch (error) {
27
+ cli.error('Invalid date format. Please use the format yyyy/mm/dd.');
28
+ process.exit(1);
29
+ }
30
+
31
+ // checkout on the next-security-release branch
32
+ checkoutOnSecurityReleaseBranch(cli, this.repository);
33
+
34
+ // update the release date in the vulnerabilities.json file
35
+ const updatedVulnerabilitiesFiles = await this.updateVulnerabilitiesJSON(releaseDate, { cli });
36
+
37
+ const commitMessage = `chore: update the release date to ${releaseDate}`;
38
+ commitAndPushVulnerabilitiesJSON(updatedVulnerabilitiesFiles,
39
+ commitMessage, { cli, repository: this.repository });
40
+ cli.ok('Done!');
41
+ }
42
+
43
+ readVulnerabilitiesJSON(vulnerabilitiesJSONPath) {
44
+ const exists = fs.existsSync(vulnerabilitiesJSONPath);
45
+
46
+ if (!exists) {
47
+ this.cli.error(`The file vulnerabilities.json does not exist at ${vulnerabilitiesJSONPath}`);
48
+ process.exit(1);
49
+ }
50
+
51
+ return JSON.parse(fs.readFileSync(vulnerabilitiesJSONPath, 'utf8'));
52
+ }
53
+
54
+ getVulnerabilitiesJSONPath() {
55
+ return path.join(process.cwd(),
56
+ NEXT_SECURITY_RELEASE_FOLDER, 'vulnerabilities.json');
57
+ }
58
+
59
+ async updateVulnerabilitiesJSON(releaseDate) {
60
+ const vulnerabilitiesJSONPath = this.getVulnerabilitiesJSONPath();
61
+ const content = this.readVulnerabilitiesJSON(vulnerabilitiesJSONPath);
62
+ content.releaseDate = releaseDate;
63
+
64
+ fs.writeFileSync(vulnerabilitiesJSONPath, JSON.stringify(content, null, 2));
65
+
66
+ this.cli.ok(`Updated the release date in vulnerabilities.json: ${releaseDate}`);
67
+ return [vulnerabilitiesJSONPath];
68
+ }
69
+
70
+ async addReport(reportId) {
71
+ const { cli } = this;
72
+ const credentials = await auth({
73
+ github: true,
74
+ h1: true
75
+ });
76
+
77
+ const req = new Request(credentials);
78
+ // checkout on the next-security-release branch
79
+ checkoutOnSecurityReleaseBranch(cli, this.repository);
80
+
81
+ // get h1 report
82
+ const { data: report } = await req.getReport(reportId);
83
+ const { id, attributes: { title, cve_ids }, relationships: { severity, reporter } } = report;
84
+ // if severity is not set on h1, set it to TBD
85
+ const reportLevel = severity ? severity.data.attributes.rating : 'TBD';
86
+
87
+ // get the affected versions
88
+ const supportedVersions = await getSupportedVersions();
89
+ const versions = await cli.prompt('Which active release lines this report affects?', {
90
+ questionType: 'input',
91
+ defaultAnswer: supportedVersions
92
+ });
93
+
94
+ // get the team summary from h1 report
95
+ const summaryContent = await getSummary(id, req);
96
+
97
+ const entry = {
98
+ id,
99
+ title,
100
+ cve_ids,
101
+ severity: reportLevel,
102
+ summary: summaryContent ?? '',
103
+ affectedVersions: versions.split(',').map((v) => v.replace('v', '').trim()),
104
+ reporter: reporter.data.attributes.username
105
+ };
106
+
107
+ const vulnerabilitiesJSONPath = this.getVulnerabilitiesJSONPath();
108
+ const content = this.readVulnerabilitiesJSON(vulnerabilitiesJSONPath);
109
+ content.reports.push(entry);
110
+ fs.writeFileSync(vulnerabilitiesJSONPath, JSON.stringify(content, null, 2));
111
+ this.cli.ok(`Updated vulnerabilities.json with the report: ${id}`);
112
+ const commitMessage = `chore: added report ${id} to vulnerabilities.json`;
113
+ commitAndPushVulnerabilitiesJSON(vulnerabilitiesJSONPath,
114
+ commitMessage, { cli, repository: this.repository });
115
+ cli.ok('Done!');
116
+ }
117
+
118
+ removeReport(reportId) {
119
+ const { cli } = this;
120
+ // checkout on the next-security-release branch
121
+ checkoutOnSecurityReleaseBranch(cli, this.repository);
122
+ const vulnerabilitiesJSONPath = this.getVulnerabilitiesJSONPath();
123
+ const content = this.readVulnerabilitiesJSON(vulnerabilitiesJSONPath);
124
+ const found = content.reports.some((report) => report.id === reportId);
125
+ if (!found) {
126
+ cli.error(`Report with id ${reportId} not found in vulnerabilities.json`);
127
+ process.exit(1);
128
+ }
129
+ content.reports = content.reports.filter((report) => report.id !== reportId);
130
+ fs.writeFileSync(vulnerabilitiesJSONPath, JSON.stringify(content, null, 2));
131
+ this.cli.ok(`Updated vulnerabilities.json with the report: ${reportId}`);
132
+
133
+ const commitMessage = `chore: remove report ${reportId} from vulnerabilities.json`;
134
+ commitAndPushVulnerabilitiesJSON(vulnerabilitiesJSONPath,
135
+ commitMessage, { cli, repository: this.repository });
136
+ cli.ok('Done!');
137
+ }
138
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@node-core/utils",
3
- "version": "4.2.3",
3
+ "version": "4.4.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
- "@listr2/prompt-adapter-enquirer": "^1.0.2",
38
- "@node-core/caritat": "^1.2.1",
37
+ "@listr2/prompt-adapter-enquirer": "^2.0.1",
38
+ "@node-core/caritat": "^1.3.0",
39
39
  "@pkgjs/nv": "^0.2.2",
40
- "branch-diff": "^2.1.5",
40
+ "branch-diff": "^3.0.2",
41
41
  "chalk": "^5.3.0",
42
- "changelog-maker": "^3.2.6",
42
+ "changelog-maker": "^4.0.1",
43
43
  "cheerio": "^1.0.0-rc.12",
44
44
  "clipboardy": "^4.0.0",
45
45
  "core-validate-commit": "^4.0.0",
46
46
  "figures": "^6.0.1",
47
- "ghauth": "^6.0.0",
48
- "inquirer": "^9.2.11",
47
+ "ghauth": "^6.0.1",
48
+ "inquirer": "^9.2.12",
49
49
  "js-yaml": "^4.1.0",
50
- "listr2": "^7.0.2",
50
+ "listr2": "^8.0.1",
51
51
  "lodash": "^4.17.21",
52
52
  "log-symbols": "^6.0.0",
53
- "ora": "^7.0.1",
54
- "replace-in-file": "^7.0.2",
55
- "undici": "^5.27.2",
53
+ "ora": "^8.0.1",
54
+ "replace-in-file": "^7.1.0",
55
+ "undici": "^6.3.0",
56
56
  "which": "^4.0.0",
57
57
  "yargs": "^17.7.2"
58
58
  },
59
59
  "devDependencies": {
60
- "@reporters/github": "^1.5.3",
61
- "c8": "^8.0.1",
62
- "eslint": "^8.53.0",
60
+ "@reporters/github": "^1.5.4",
61
+ "c8": "^9.0.0",
62
+ "eslint": "^8.56.0",
63
63
  "eslint-config-standard": "^17.1.0",
64
- "eslint-plugin-import": "^2.29.0",
65
- "eslint-plugin-n": "^16.2.0",
64
+ "eslint-plugin-import": "^2.29.1",
65
+ "eslint-plugin-n": "^16.6.1",
66
66
  "eslint-plugin-promise": "^6.1.1",
67
67
  "sinon": "^17.0.1"
68
68
  }
@@ -1,97 +0,0 @@
1
- ## Planning
2
-
3
- * [X] Open an [issue](https://github.com/nodejs-private/node-private) titled
4
- `Next Security Release`, and put this checklist in the description.
5
-
6
- * [ ] Get agreement on the list of vulnerabilities to be addressed:
7
- %REPORTS%
8
-
9
- * [ ] PR release announcements in [private](https://github.com/nodejs-private/nodejs.org-private):
10
- * [ ] pre-release: %PRE_RELEASE_PRIV%
11
- * [ ] post-release: %POS_RELEASE_PRIV%
12
- * List vulnerabilities in order of descending severity
13
- * Ask the HackerOne reporter if they would like to be credited on the
14
- security release blog page
15
-
16
- * [ ] Get agreement on the planned date for the release: %RELEASE_DATE%
17
-
18
- * [ ] Get release team volunteers for all affected lines:
19
- %AFFECTED_LINES%
20
-
21
- ## Announcement (one week in advance of the planned release)
22
-
23
- * [ ] Verify that GitHub Actions are working as normal: <https://www.githubstatus.com/>.
24
-
25
- * [ ] Check that all vulnerabilities are ready for release integration:
26
- * PRs against all affected release lines or cherry-pick clean
27
- * Approved
28
- * (optional) Approved by the reporter
29
- * Build and send the binary to the reporter according to its architecture
30
- and ask for a review. This step is important to avoid insufficient fixes
31
- between Security Releases.
32
- * Have CVEs
33
- * Make sure that dependent libraries have CVEs for their issues. We should
34
- only create CVEs for vulnerabilities in Node.js itself. This is to avoid
35
- having duplicate CVEs for the same vulnerability.
36
- * Described in the pre/post announcements
37
-
38
- * [ ] Pre-release announcement to nodejs.org blog: TBD
39
- (Re-PR the pre-approved branch from nodejs-private/nodejs.org-private to
40
- nodejs/nodejs.org)
41
-
42
- * [ ] Pre-release announcement [email](https://groups.google.com/forum/#!forum/nodejs-sec): TBD
43
- * Subject: `Node.js security updates for all active release lines, Month Year`
44
-
45
- * [ ] CC `oss-security@lists.openwall.com` on pre-release
46
- * [ ] Forward the email you receive to `oss-security@lists.openwall.com`.
47
-
48
- * [ ] Create a new issue in [nodejs/tweet](https://github.com/nodejs/tweet/issues)
49
-
50
- * [ ] Request releaser(s) to start integrating the PRs to be released.
51
-
52
- * [ ] Notify [docker-node](https://github.com/nodejs/docker-node/issues) of upcoming security release date: TBD
53
-
54
- * [ ] Notify build-wg of upcoming security release date by opening an issue
55
- in [nodejs/build](https://github.com/nodejs/build/issues) to request WG members are available to fix any CI issues: TBD
56
-
57
- ## Release day
58
-
59
- * [ ] [Lock CI](https://github.com/nodejs/build/blob/HEAD/doc/jenkins-guide.md#before-the-release)
60
-
61
- * [ ] The releaser(s) run the release process to completion.
62
-
63
- * [ ] [Unlock CI](https://github.com/nodejs/build/blob/HEAD/doc/jenkins-guide.md#after-the-release)
64
-
65
- * [ ] Post-release announcement to Nodejs.org blog:
66
- * (Re-PR the pre-approved branch from nodejs-private/nodejs.org-private to
67
- nodejs/nodejs.org)
68
-
69
- * [ ] Post-release announcement in reply email: TBD
70
-
71
- * [ ] Notify `#nodejs-social` about the release.
72
-
73
- * [ ] Comment in [docker-node][] issue that release is ready for integration.
74
- The docker-node team will build and release docker image updates.
75
-
76
- * [ ] For every H1 report resolved:
77
- * Close as Resolved
78
- * Request Disclosure
79
- * Request publication of H1 CVE requests
80
- * (Check that the "Version Fixed" field in the CVE is correct, and provide
81
- links to the release blogs in the "Public Reference" section)
82
-
83
- * [ ] PR machine-readable JSON descriptions of the vulnerabilities to the
84
- [core](https://github.com/nodejs/security-wg/tree/HEAD/vuln/core)
85
- vulnerability DB.
86
- * For each vulnerability add a `#.json` file, one can copy an existing
87
- [json](https://github.com/nodejs/security-wg/blob/0d82062d917cb9ddab88f910559469b2b13812bf/vuln/core/78.json)
88
- file, and increment the latest created file number and use that as the name
89
- of the new file to be added. For example, `79.json`.
90
-
91
- * [ ] Close this issue
92
-
93
- * [ ] Make sure the PRs for the vulnerabilities are closed.
94
-
95
- * [ ] PR in that you stewarded the release in
96
- [Security release stewards](https://github.com/nodejs/node/blob/HEAD/doc/contributing/security-release-process.md#security-release-stewards).
97
- If necessary add the next rotation of the steward rotation.