|
| 1 | +import nv from '@pkgjs/nv'; |
| 2 | +import auth from './auth.js'; |
| 3 | +import Request from './request.js'; |
| 4 | + |
| 5 | +const TEMPLATE = ` |
| 6 | +## Planning |
| 7 | +
|
| 8 | +* [X] Open an [issue](https://github.com/nodejs-private/node-private) titled |
| 9 | + \`Next Security Release\`, and put this checklist in the description. |
| 10 | +
|
| 11 | +* [ ] Get agreement on the list of vulnerabilities to be addressed: |
| 12 | +%REPORTS% |
| 13 | +
|
| 14 | +* [ ] PR release announcements in [private](https://github.com/nodejs-private/nodejs.org-private): |
| 15 | + * [ ] pre-release: %PRE_RELEASE_PRIV% |
| 16 | + * [ ] post-release: %POS_RELEASE_PRIV% |
| 17 | + * List vulnerabilities in order of descending severity |
| 18 | + * Ask the HackerOne reporter if they would like to be credited on the |
| 19 | + security release blog page |
| 20 | +
|
| 21 | +* [ ] Get agreement on the planned date for the release: %RELEASE_DATE% |
| 22 | +
|
| 23 | +* [ ] Get release team volunteers for all affected lines: |
| 24 | +%AFFECTED_LINES% |
| 25 | +
|
| 26 | +## Announcement (one week in advance of the planned release) |
| 27 | +
|
| 28 | +* [ ] Verify that GitHub Actions are working as normal: <https://www.githubstatus.com/>. |
| 29 | +
|
| 30 | +* [ ] Check that all vulnerabilities are ready for release integration: |
| 31 | + * PRs against all affected release lines or cherry-pick clean |
| 32 | + * Approved |
| 33 | + * (optional) Approved by the reporter |
| 34 | + * Build and send the binary to the reporter according to its architecture |
| 35 | + and ask for a review. This step is important to avoid insufficient fixes |
| 36 | + between Security Releases. |
| 37 | + * Have CVEs |
| 38 | + * Make sure that dependent libraries have CVEs for their issues. We should |
| 39 | + only create CVEs for vulnerabilities in Node.js itself. This is to avoid |
| 40 | + having duplicate CVEs for the same vulnerability. |
| 41 | + * Described in the pre/post announcements |
| 42 | +
|
| 43 | +* [ ] Pre-release announcement to nodejs.org blog: TBD |
| 44 | + (Re-PR the pre-approved branch from nodejs-private/nodejs.org-private to |
| 45 | + nodejs/nodejs.org) |
| 46 | +
|
| 47 | +* [ ] Pre-release announcement [email](https://groups.google.com/forum/#!forum/nodejs-sec): TBD |
| 48 | + * Subject: \`Node.js security updates for all active release lines, Month Year\` |
| 49 | +
|
| 50 | +* [ ] CC \`oss-security@lists.openwall.com\` on pre-release |
| 51 | + * [ ] Forward the email you receive to \`oss-security@lists.openwall.com\`. |
| 52 | +
|
| 53 | +* [ ] Create a new issue in [nodejs/tweet](https://github.com/nodejs/tweet/issues) |
| 54 | +
|
| 55 | +* [ ] Request releaser(s) to start integrating the PRs to be released. |
| 56 | +
|
| 57 | +* [ ] Notify [docker-node](https://github.com/nodejs/docker-node/issues) of upcoming security release date: TBD |
| 58 | +
|
| 59 | +* [ ] Notify build-wg of upcoming security release date by opening an issue |
| 60 | + in [nodejs/build](https://github.com/nodejs/build/issues) to request WG members are available to fix any CI issues: TBD |
| 61 | +
|
| 62 | +## Release day |
| 63 | +
|
| 64 | +* [ ] [Lock CI](https://github.com/nodejs/build/blob/HEAD/doc/jenkins-guide.md#before-the-release) |
| 65 | +
|
| 66 | +* [ ] The releaser(s) run the release process to completion. |
| 67 | +
|
| 68 | +* [ ] [Unlock CI](https://github.com/nodejs/build/blob/HEAD/doc/jenkins-guide.md#after-the-release) |
| 69 | +
|
| 70 | +* [ ] Post-release announcement to Nodejs.org blog: https://github.com/nodejs/nodejs.org/pull/5447 |
| 71 | + * (Re-PR the pre-approved branch from nodejs-private/nodejs.org-private to |
| 72 | + nodejs/nodejs.org) |
| 73 | +
|
| 74 | +* [ ] Post-release announcement in reply email: TBD |
| 75 | +
|
| 76 | +* [ ] Create a new issue in nodejs/tweet |
| 77 | +
|
| 78 | +* [ ] Comment in [docker-node][] issue that release is ready for integration. |
| 79 | + The docker-node team will build and release docker image updates. |
| 80 | +
|
| 81 | +* [ ] For every H1 report resolved: |
| 82 | + * Close as Resolved |
| 83 | + * Request Disclosure |
| 84 | + * Request publication of H1 CVE requests |
| 85 | + * (Check that the "Version Fixed" field in the CVE is correct, and provide |
| 86 | + links to the release blogs in the "Public Reference" section) |
| 87 | +
|
| 88 | +* [ ] PR machine-readable JSON descriptions of the vulnerabilities to the |
| 89 | + [core](https://github.com/nodejs/security-wg/tree/HEAD/vuln/core) |
| 90 | + vulnerability DB. https://github.com/nodejs/security-wg/pull/1029 |
| 91 | + * For each vulnerability add a \`#.json\` file, one can copy an existing |
| 92 | + [json](https://github.com/nodejs/security-wg/blob/0d82062d917cb9ddab88f910559469b2b13812bf/vuln/core/78.json) |
| 93 | + file, and increment the latest created file number and use that as the name |
| 94 | + of the new file to be added. For example, \`79.json\`. |
| 95 | +
|
| 96 | +* [ ] Close this issue |
| 97 | +
|
| 98 | +* [ ] Make sure the PRs for the vulnerabilities are closed. |
| 99 | +
|
| 100 | +* [ ] PR in that you stewarded the release in |
| 101 | + [Security release stewards](https://github.com/nodejs/node/blob/HEAD/doc/contributing/security-release-process.md#security-release-stewards). |
| 102 | + If necessary add the next rotation of the steward rotation. |
| 103 | +`; |
| 104 | + |
| 105 | +export default class SecurityReleaseSteward { |
| 106 | + constructor(cli) { |
| 107 | + this.cli = cli; |
| 108 | + } |
| 109 | + |
| 110 | + async start() { |
| 111 | + const { cli } = this; |
| 112 | + const credentials = await auth({ |
| 113 | + github: true, |
| 114 | + h1: true |
| 115 | + }); |
| 116 | + |
| 117 | + const req = new Request(credentials); |
| 118 | + const create = await cli.prompt( |
| 119 | + 'Create the Next Security Release issue?', |
| 120 | + { defaultAnswer: true }); |
| 121 | + if (create) { |
| 122 | + const issue = new SecurityReleaseIssue(req); |
| 123 | + const content = await issue.buildIssue(cli); |
| 124 | + const { repository: { id } } = await req.gql('RepositoryId', { |
| 125 | + owner: 'nodejs-private', |
| 126 | + repo: 'node-private' |
| 127 | + }); |
| 128 | + const data = await req.gql('CreateIssue', { |
| 129 | + repoId: id, |
| 130 | + title: 'Next Security Release', |
| 131 | + body: content |
| 132 | + }); |
| 133 | + cli.ok('Created: ' + data.createIssue.issue.url); |
| 134 | + } |
| 135 | + } |
| 136 | +} |
| 137 | + |
| 138 | +class SecurityReleaseIssue { |
| 139 | + constructor(req) { |
| 140 | + this.req = req; |
| 141 | + this.content = ''; |
| 142 | + this.title = 'Next Security Release'; |
| 143 | + this.affectedLines = {}; |
| 144 | + } |
| 145 | + |
| 146 | + async buildIssue(cli) { |
| 147 | + this.content = TEMPLATE; |
| 148 | + cli.info('Getting triaged H1 reports...'); |
| 149 | + const reports = await this.req.getTriagedReports(); |
| 150 | + await this.fillReports(cli, reports); |
| 151 | + |
| 152 | + this.fillAffectedLines(Object.keys(this.affectedLines)); |
| 153 | + |
| 154 | + const target = await cli.prompt('Enter target date in YYYY-MM-DD format:', { |
| 155 | + questionType: 'input', |
| 156 | + defaultAnswer: 'TBD' |
| 157 | + }); |
| 158 | + this.fillTargetDate(target); |
| 159 | + |
| 160 | + return this.content; |
| 161 | + } |
| 162 | + |
| 163 | + async fillReports(cli, reports) { |
| 164 | + const supportedVersions = (await nv('supported')) |
| 165 | + .map((v) => v.versionName + '.x') |
| 166 | + .join(','); |
| 167 | + |
| 168 | + let reportsContent = ''; |
| 169 | + for (const report of reports.data) { |
| 170 | + const { id, attributes: { title }, relationships: { severity } } = report; |
| 171 | + const reportLevel = severity.data.attributes.rating; |
| 172 | + cli.separator(); |
| 173 | + cli.info(`Report: ${id} - ${title} (${reportLevel})`); |
| 174 | + const include = await cli.prompt( |
| 175 | + 'Would you like to include this report to the next security release?', |
| 176 | + { defaultAnswer: true }); |
| 177 | + if (!include) { |
| 178 | + continue; |
| 179 | + } |
| 180 | + |
| 181 | + reportsContent += |
| 182 | + ` * **[${id}](https://hackerone.com/bugs?subject=nodejs&report_id=${id}) - ${title} (TBD) - (${reportLevel})**\n`; |
| 183 | + const versions = await cli.prompt('Which active release lines this report affects?', { |
| 184 | + questionType: 'input', |
| 185 | + defaultAnswer: supportedVersions |
| 186 | + }); |
| 187 | + for (const v of versions.split(',')) { |
| 188 | + if (!this.affectedLines[v]) this.affectedLines[v] = true; |
| 189 | + reportsContent += ` * ${v} - TBD\n`; |
| 190 | + } |
| 191 | + } |
| 192 | + this.content = this.content.replace('%REPORTS%', reportsContent); |
| 193 | + } |
| 194 | + |
| 195 | + fillAffectedLines(affectedLines) { |
| 196 | + let affected = ''; |
| 197 | + for (const line of affectedLines) { |
| 198 | + affected += ` * ${line} - TBD\n`; |
| 199 | + } |
| 200 | + this.content = |
| 201 | + this.content.replace('%AFFECTED_LINES%', affected); |
| 202 | + } |
| 203 | + |
| 204 | + fillTargetDate(date) { |
| 205 | + this.content = this.content.replace('%RELEASE_DATE%', date); |
| 206 | + } |
| 207 | +} |
0 commit comments