@node-core/utils 5.1.0 → 5.2.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/components/git/security.js +18 -0
- package/lib/github/templates/security-pre-release.md +4 -0
- package/lib/prepare_security.js +1 -2
- package/lib/request.js +3 -2
- package/lib/security-release/security-release.js +33 -16
- package/lib/update_security_release.js +39 -1
- package/lib/voting_session.js +13 -3
- package/package.json +1 -1
@@ -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%.
|
package/lib/prepare_security.js
CHANGED
@@ -248,8 +248,7 @@ export default class PrepareSecurityRelease {
|
|
248
248
|
});
|
249
249
|
|
250
250
|
try {
|
251
|
-
const
|
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(
|
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(
|
90
|
+
return this.json(prUrl, options);
|
90
91
|
}
|
91
92
|
|
92
93
|
async createPullRequest(title, body, { owner, repo, head, base }) {
|
@@ -87,8 +87,8 @@ export async function getSupportedVersions() {
|
|
87
87
|
return supportedVersions;
|
88
88
|
}
|
89
89
|
|
90
|
-
export
|
91
|
-
const { data } =
|
90
|
+
export function getSummary(report) {
|
91
|
+
const { data } = report;
|
92
92
|
const summaryList = data?.relationships?.summaries?.data;
|
93
93
|
if (!summaryList?.length) return;
|
94
94
|
const summaries = summaryList.filter((summary) => summary?.attributes?.category === 'team');
|
@@ -139,17 +139,25 @@ export async function createIssue(title, content, repository, { cli, req }) {
|
|
139
139
|
}
|
140
140
|
}
|
141
141
|
|
142
|
-
export
|
142
|
+
export function getReportSeverity(report) {
|
143
143
|
const {
|
144
|
-
|
145
|
-
relationships: { severity, weakness, reporter }
|
144
|
+
relationships: { severity, weakness }
|
146
145
|
} = report;
|
147
|
-
const link = `https://hackerone.com/reports/${id}`;
|
148
146
|
const reportSeverity = {
|
149
147
|
rating: severity?.data?.attributes?.rating || '',
|
150
148
|
cvss_vector_string: severity?.data?.attributes?.cvss_vector_string || '',
|
151
149
|
weakness_id: weakness?.data?.id || ''
|
152
150
|
};
|
151
|
+
return reportSeverity;
|
152
|
+
}
|
153
|
+
|
154
|
+
export async function pickReport(report, { cli, req }) {
|
155
|
+
const {
|
156
|
+
id, attributes: { title, cve_ids },
|
157
|
+
relationships: { reporter, custom_field_values }
|
158
|
+
} = report;
|
159
|
+
const link = `https://hackerone.com/reports/${id}`;
|
160
|
+
const reportSeverity = getReportSeverity(report);
|
153
161
|
|
154
162
|
cli.separator();
|
155
163
|
cli.info(`Report: ${link} - ${title} (${reportSeverity?.rating})`);
|
@@ -165,19 +173,27 @@ export async function pickReport(report, { cli, req }) {
|
|
165
173
|
defaultAnswer: await getSupportedVersions()
|
166
174
|
});
|
167
175
|
|
168
|
-
let
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
});
|
173
|
-
|
174
|
-
if (!patchAuthors) {
|
175
|
-
patchAuthors = [];
|
176
|
+
let prURL = '';
|
177
|
+
let patchAuthors = [];
|
178
|
+
if (custom_field_values.data.length) {
|
179
|
+
prURL = custom_field_values.data[0].attributes.value;
|
180
|
+
const { user } = await req.getPullRequest(prURL);
|
181
|
+
patchAuthors = [user.login];
|
176
182
|
} else {
|
177
|
-
patchAuthors =
|
183
|
+
patchAuthors = await cli.prompt(
|
184
|
+
'Add github username of the authors of the patch (split by comma if multiple)', {
|
185
|
+
questionType: 'input',
|
186
|
+
defaultAnswer: ''
|
187
|
+
});
|
188
|
+
|
189
|
+
if (!patchAuthors) {
|
190
|
+
patchAuthors = [];
|
191
|
+
} else {
|
192
|
+
patchAuthors = patchAuthors.split(',').map((p) => p.trim());
|
193
|
+
}
|
178
194
|
}
|
179
195
|
|
180
|
-
const summaryContent =
|
196
|
+
const summaryContent = getSummary(report);
|
181
197
|
|
182
198
|
return {
|
183
199
|
id,
|
@@ -186,6 +202,7 @@ export async function pickReport(report, { cli, req }) {
|
|
186
202
|
severity: reportSeverity,
|
187
203
|
summary: summaryContent ?? '',
|
188
204
|
patchAuthors,
|
205
|
+
prURL,
|
189
206
|
affectedVersions: versions.split(',').map((v) => v.replace('v', '').trim()),
|
190
207
|
link,
|
191
208
|
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
|
+
let 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
|
+
report = {
|
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
|
|
package/lib/voting_session.js
CHANGED
@@ -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
|
-
|
128
|
-
|
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
|
}
|