Skip to content

Commit 78ad337

Browse files
refactor, feat: patch authors and rebase when pushing (#797)
* refactor: renamed createVulnerabilitiesJSON * refactor: merge into one class * refactor: create pickReport function to avoid duplication * refactor: remove confusing cli and req inputs * feat: add rebase when pushing * feat: add patch authors
1 parent b5d99ca commit 78ad337

File tree

5 files changed

+171
-187
lines changed

5 files changed

+171
-187
lines changed

components/git/security.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import CLI from '../../lib/cli.js';
2-
import SecurityReleaseSteward from '../../lib/prepare_security.js';
2+
import PrepareSecurityRelease from '../../lib/prepare_security.js';
33
import UpdateSecurityRelease from '../../lib/update_security_release.js';
44
import SecurityBlog from '../../lib/security_blog.js';
55
import SecurityAnnouncement from '../../lib/security-announcement.js';
@@ -138,7 +138,7 @@ async function requestCVEs() {
138138
async function startSecurityRelease(argv) {
139139
const logStream = process.stdout.isTTY ? process.stdout : process.stderr;
140140
const cli = new CLI(logStream);
141-
const release = new SecurityReleaseSteward(cli);
141+
const release = new PrepareSecurityRelease(cli);
142142
return release.start();
143143
}
144144

lib/prepare_security.js

Lines changed: 74 additions & 128 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import nv from '@pkgjs/nv';
21
import fs from 'node:fs';
32
import path from 'node:path';
43
import auth from './auth.js';
@@ -10,100 +9,89 @@ import {
109
PLACEHOLDERS,
1110
checkoutOnSecurityReleaseBranch,
1211
commitAndPushVulnerabilitiesJSON,
13-
getSummary,
1412
validateDate,
1513
promptDependencies,
16-
getSupportedVersions
14+
getSupportedVersions,
15+
pickReport
1716
} from './security-release/security-release.js';
1817
import _ from 'lodash';
1918

20-
export default class SecurityReleaseSteward {
19+
export default class PrepareSecurityRelease {
2120
repository = NEXT_SECURITY_RELEASE_REPOSITORY;
21+
title = 'Next Security Release';
2222
constructor(cli) {
2323
this.cli = cli;
2424
}
2525

2626
async start() {
27-
const { cli } = this;
2827
const credentials = await auth({
2928
github: true,
3029
h1: true
3130
});
3231

33-
const req = new Request(credentials);
34-
const release = new PrepareSecurityRelease(req);
35-
const releaseDate = await release.promptReleaseDate(cli);
32+
this.req = new Request(credentials);
33+
const releaseDate = await this.promptReleaseDate();
3634
if (releaseDate !== 'TBD') {
3735
validateDate(releaseDate);
3836
}
39-
40-
const createVulnerabilitiesJSON = await release.promptVulnerabilitiesJSON(cli);
37+
const createVulnerabilitiesJSON = await this.promptVulnerabilitiesJSON();
4138

4239
let securityReleasePRUrl;
4340
if (createVulnerabilitiesJSON) {
44-
securityReleasePRUrl = await this.createVulnerabilitiesJSON(
45-
req, release, releaseDate, { cli });
41+
securityReleasePRUrl = await this.startVulnerabilitiesJSONCreation(releaseDate);
4642
}
4743

48-
const createIssue = await release.promptCreateRelaseIssue(cli);
44+
const createIssue = await this.promptCreateRelaseIssue();
4945

5046
if (createIssue) {
51-
const content = await release.buildIssue(releaseDate, securityReleasePRUrl);
52-
await release.createIssue(content, { cli });
47+
const content = await this.buildIssue(releaseDate, securityReleasePRUrl);
48+
await createIssue(
49+
this.title, content, this.repository, { cli: this.cli, repository: this.repository });
5350
};
5451

55-
cli.ok('Done!');
52+
this.cli.ok('Done!');
5653
}
5754

58-
async createVulnerabilitiesJSON(req, release, releaseDate, { cli }) {
55+
async startVulnerabilitiesJSONCreation(releaseDate) {
5956
// checkout on the next-security-release branch
60-
checkoutOnSecurityReleaseBranch(cli, this.repository);
57+
checkoutOnSecurityReleaseBranch(this.cli, this.repository);
6158

6259
// choose the reports to include in the security release
63-
const reports = await release.chooseReports(cli);
64-
const depUpdates = await release.getDependencyUpdates({ cli });
60+
const reports = await this.chooseReports();
61+
const depUpdates = await this.getDependencyUpdates();
6562
const deps = _.groupBy(depUpdates, 'name');
6663

6764
// create the vulnerabilities.json file in the security-release repo
68-
const filePath = await release.createVulnerabilitiesJSON(reports, deps, releaseDate, { cli });
65+
const filePath = await this.createVulnerabilitiesJSON(reports, deps, releaseDate);
66+
6967
// review the vulnerabilities.json file
70-
const review = await release.promptReviewVulnerabilitiesJSON(cli);
68+
const review = await this.promptReviewVulnerabilitiesJSON();
7169

7270
if (!review) {
73-
cli.info(`To push the vulnerabilities.json file run:
71+
this.cli.info(`To push the vulnerabilities.json file run:
7472
- git add ${filePath}
7573
- git commit -m "chore: create vulnerabilities.json for next security release"
7674
- git push -u origin ${NEXT_SECURITY_RELEASE_BRANCH}
77-
- open a PR on ${release.repository.owner}/${release.repository.repo}`);
75+
- open a PR on ${this.repository.owner}/${this.repository.repo}`);
7876
return;
7977
};
8078

8179
// commit and push the vulnerabilities.json file
8280
const commitMessage = 'chore: create vulnerabilities.json for next security release';
83-
commitAndPushVulnerabilitiesJSON(filePath, commitMessage, { cli, repository: this.repository });
81+
commitAndPushVulnerabilitiesJSON(filePath,
82+
commitMessage,
83+
{ cli: this.cli, repository: this.repository });
8484

85-
const createPr = await release.promptCreatePR(cli);
85+
const createPr = await this.promptCreatePR();
8686

8787
if (!createPr) return;
8888

8989
// create pr on the security-release repo
90-
return release.createPullRequest(req, { cli });
90+
return this.createPullRequest();
9191
}
92-
}
9392

94-
class PrepareSecurityRelease {
95-
repository = NEXT_SECURITY_RELEASE_REPOSITORY;
96-
title = 'Next Security Release';
97-
98-
constructor(req, repository) {
99-
this.req = req;
100-
if (repository) {
101-
this.repository = repository;
102-
}
103-
}
104-
105-
promptCreatePR(cli) {
106-
return cli.prompt(
93+
promptCreatePR() {
94+
return this.cli.prompt(
10795
'Create the Next Security Release PR?',
10896
{ defaultAnswer: true });
10997
}
@@ -125,31 +113,32 @@ class PrepareSecurityRelease {
125113
}
126114
}
127115

128-
async promptReleaseDate(cli) {
116+
async promptReleaseDate() {
129117
const nextWeekDate = new Date();
130118
nextWeekDate.setDate(nextWeekDate.getDate() + 7);
131119
// Format the date as YYYY/MM/DD
132120
const formattedDate = nextWeekDate.toISOString().slice(0, 10).replace(/-/g, '/');
133-
return cli.prompt('Enter target release date in YYYY/MM/DD format (TBD if not defined yet):', {
134-
questionType: 'input',
135-
defaultAnswer: formattedDate
136-
});
121+
return this.cli.prompt(
122+
'Enter target release date in YYYY/MM/DD format (TBD if not defined yet):', {
123+
questionType: 'input',
124+
defaultAnswer: formattedDate
125+
});
137126
}
138127

139-
async promptVulnerabilitiesJSON(cli) {
140-
return cli.prompt(
128+
async promptVulnerabilitiesJSON() {
129+
return this.cli.prompt(
141130
'Create the vulnerabilities.json?',
142131
{ defaultAnswer: true });
143132
}
144133

145-
async promptCreateRelaseIssue(cli) {
146-
return cli.prompt(
134+
async promptCreateRelaseIssue() {
135+
return this.cli.prompt(
147136
'Create the Next Security Release issue?',
148137
{ defaultAnswer: true });
149138
}
150139

151-
async promptReviewVulnerabilitiesJSON(cli) {
152-
return cli.prompt(
140+
async promptReviewVulnerabilitiesJSON() {
141+
return this.cli.prompt(
153142
'Please review vulnerabilities.json and press enter to proceed.',
154143
{ defaultAnswer: true });
155144
}
@@ -161,67 +150,21 @@ class PrepareSecurityRelease {
161150
return content;
162151
}
163152

164-
async createIssue(content, { cli }) {
165-
const data = await this.req.createIssue(this.title, content, this.repository);
166-
if (data.html_url) {
167-
cli.ok(`Created: ${data.html_url}`);
168-
} else {
169-
cli.error(data);
170-
process.exit(1);
171-
}
172-
}
173-
174-
async chooseReports(cli) {
175-
cli.info('Getting triaged H1 reports...');
153+
async chooseReports() {
154+
this.cli.info('Getting triaged H1 reports...');
176155
const reports = await this.req.getTriagedReports();
177-
const supportedVersions = (await nv('supported'))
178-
.map((v) => `${v.versionName}.x`)
179-
.join(',');
180156
const selectedReports = [];
181157

182158
for (const report of reports.data) {
183-
const {
184-
id, attributes: { title, cve_ids },
185-
relationships: { severity, weakness, reporter }
186-
} = report;
187-
const link = `https://hackerone.com/reports/${id}`;
188-
const reportSeverity = {
189-
rating: severity?.data?.attributes?.rating || '',
190-
cvss_vector_string: severity?.data?.attributes?.cvss_vector_string || '',
191-
weakness_id: weakness?.data?.id || ''
192-
};
193-
194-
cli.separator();
195-
cli.info(`Report: ${link} - ${title} (${reportSeverity?.rating})`);
196-
const include = await cli.prompt(
197-
'Would you like to include this report to the next security release?',
198-
{ defaultAnswer: true });
199-
if (!include) {
200-
continue;
201-
}
202-
203-
const versions = await cli.prompt('Which active release lines this report affects?', {
204-
questionType: 'input',
205-
defaultAnswer: supportedVersions
206-
});
207-
const summaryContent = await getSummary(id, this.req);
208-
209-
selectedReports.push({
210-
id,
211-
title,
212-
cveIds: 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-
});
159+
const rep = await pickReport(report, { cli: this.cli, req: this.req });
160+
if (!rep) continue;
161+
selectedReports.push(rep);
219162
}
220163
return selectedReports;
221164
}
222165

223-
async createVulnerabilitiesJSON(reports, dependencies, releaseDate, { cli }) {
224-
cli.separator('Creating vulnerabilities.json...');
166+
async createVulnerabilitiesJSON(reports, dependencies, releaseDate) {
167+
this.cli.separator('Creating vulnerabilities.json...');
225168
const file = JSON.stringify({
226169
releaseDate,
227170
reports,
@@ -237,14 +180,14 @@ class PrepareSecurityRelease {
237180

238181
const fullPath = path.join(folderPath, 'vulnerabilities.json');
239182
fs.writeFileSync(fullPath, file);
240-
cli.ok(`Created ${fullPath} `);
183+
this.cli.ok(`Created ${fullPath} `);
241184

242185
return fullPath;
243186
}
244187

245-
async createPullRequest(req, { cli }) {
188+
async createPullRequest() {
246189
const { owner, repo } = this.repository;
247-
const response = await req.createPullRequest(
190+
const response = await this.req.createPullRequest(
248191
this.title,
249192
'List of vulnerabilities to be included in the next security release',
250193
{
@@ -257,49 +200,52 @@ class PrepareSecurityRelease {
257200
);
258201
const url = response?.html_url;
259202
if (url) {
260-
cli.ok(`Created: ${url}`);
203+
this.cli.ok(`Created: ${url}`);
261204
return url;
262205
}
263206
if (response?.errors) {
264207
for (const error of response.errors) {
265-
cli.error(error.message);
208+
this.cli.error(error.message);
266209
}
267210
} else {
268-
cli.error(response);
211+
this.cli.error(response);
269212
}
270213
process.exit(1);
271214
}
272215

273-
async getDependencyUpdates({ cli }) {
216+
async getDependencyUpdates() {
274217
const deps = [];
275-
cli.log('\n');
276-
cli.separator('Dependency Updates');
277-
const updates = await cli.prompt('Are there dependency updates in this security release?', {
278-
defaultAnswer: true,
279-
questionType: 'confirm'
280-
});
218+
this.cli.log('\n');
219+
this.cli.separator('Dependency Updates');
220+
const updates = await this.cli.prompt('Are there dependency updates in this security release?',
221+
{
222+
defaultAnswer: true,
223+
questionType: 'confirm'
224+
});
281225

282226
if (!updates) return deps;
283227

284228
const supportedVersions = await getSupportedVersions();
285229

286230
let asking = true;
287231
while (asking) {
288-
const dep = await promptDependencies(cli);
232+
const dep = await promptDependencies(this.cli);
289233
if (!dep) {
290234
asking = false;
291235
break;
292236
}
293237

294-
const name = await cli.prompt('What is the name of the dependency that has been updated?', {
295-
defaultAnswer: '',
296-
questionType: 'input'
297-
});
238+
const name = await this.cli.prompt(
239+
'What is the name of the dependency that has been updated?', {
240+
defaultAnswer: '',
241+
questionType: 'input'
242+
});
298243

299-
const versions = await cli.prompt('Which release line does this dependency update affect?', {
300-
defaultAnswer: supportedVersions,
301-
questionType: 'input'
302-
});
244+
const versions = await this.cli.prompt(
245+
'Which release line does this dependency update affect?', {
246+
defaultAnswer: supportedVersions,
247+
questionType: 'input'
248+
});
303249

304250
try {
305251
const prUrl = dep.replace('https://github.com/', 'https://api.github.com/repos/').replace('pull', 'pulls');
@@ -311,7 +257,7 @@ class PrepareSecurityRelease {
311257
title,
312258
affectedVersions: versions.split(',').map((v) => v.replace('v', '').trim())
313259
});
314-
cli.separator();
260+
this.cli.separator();
315261
} catch (error) {
316262
this.cli.error('Invalid PR url. Please provide a valid PR url.');
317263
this.cli.error(error);

0 commit comments

Comments
 (0)