@node-core/utils 5.14.1 → 5.15.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 +12 -2
- package/bin/ncu-ci.js +11 -2
- package/lib/config.js +10 -0
- package/lib/security-announcement.js +17 -3
- package/lib/security-release/security-release.js +7 -0
- package/lib/security_blog.js +29 -3
- package/lib/update_security_release.js +17 -6
- package/lib/voting_session.js +1 -0
- package/package.json +2 -2
package/README.md
CHANGED
@@ -89,6 +89,14 @@ After the token is generated, create an rc file with the following content:
|
|
89
89
|
Note: you could use `ncu-config` to configure these variables, but it's not
|
90
90
|
recommended to leave your tokens in your command line history.
|
91
91
|
|
92
|
+
If you have `gpg` installed and setup on your local machine, it is recommended
|
93
|
+
to store an encrypted version of this file:
|
94
|
+
|
95
|
+
```console
|
96
|
+
$ gpg --default-recipient-self --encrypt ~/.ncurc
|
97
|
+
$ rm ~/.ncurc
|
98
|
+
```
|
99
|
+
|
92
100
|
### Setting up Jenkins credentials
|
93
101
|
|
94
102
|
The `git-node` and `ncu-ci` commands need to query the Node.js Jenkins API for
|
@@ -104,8 +112,9 @@ To obtain the Jenkins API token
|
|
104
112
|
3. Enter an identifiable name (for example, `node-core-utils`) for this
|
105
113
|
token in the inbox that appears, and click `GENERATE`.
|
106
114
|
4. Copy the generated token.
|
107
|
-
5. Add it into your `ncurc` file (`~/.ncurc` or `$XDG_CONFIG_HOME/ncurc
|
108
|
-
with `jenkins_token` as key,
|
115
|
+
5. Add it into your `ncurc` file (`~/.ncurc` or `$XDG_CONFIG_HOME/ncurc`, or
|
116
|
+
`~/.ncurc.gpg` or `$XDG_CONFIG_HOME/ncurc.gpg`) with `jenkins_token` as key,
|
117
|
+
like this:
|
109
118
|
|
110
119
|
```json
|
111
120
|
{
|
@@ -125,6 +134,7 @@ Put the following entries into your
|
|
125
134
|
```
|
126
135
|
# node-core-utils configuration file
|
127
136
|
.ncurc
|
137
|
+
.ncurc.gpg
|
128
138
|
# node-core-utils working directory
|
129
139
|
.ncu
|
130
140
|
```
|
package/bin/ncu-ci.js
CHANGED
@@ -111,8 +111,8 @@ const args = yargs(hideBin(process.argv))
|
|
111
111
|
builder: (yargs) => {
|
112
112
|
yargs
|
113
113
|
.positional('prid', {
|
114
|
-
describe: 'ID of the PR',
|
115
|
-
type: '
|
114
|
+
describe: 'ID of the PR or URL to the PR or its head commit',
|
115
|
+
type: 'string'
|
116
116
|
})
|
117
117
|
.option('certify-safe', {
|
118
118
|
describe: 'SHA of the commit that is expected to be at the tip of the PR head. ' +
|
@@ -574,6 +574,15 @@ async function main(command, argv) {
|
|
574
574
|
// Prepare queue.
|
575
575
|
switch (command) {
|
576
576
|
case 'run': {
|
577
|
+
const maybeURL = URL.parse(argv.prid);
|
578
|
+
if (maybeURL?.host === 'github.com') {
|
579
|
+
const [, owner, repo, , prid, , commit_sha] = maybeURL.pathname.split('/');
|
580
|
+
argv.owner ||= owner;
|
581
|
+
argv.repo ||= repo;
|
582
|
+
argv.certifySafe ||= commit_sha;
|
583
|
+
argv.prid = prid;
|
584
|
+
}
|
585
|
+
argv.prid = Number(argv.prid);
|
577
586
|
const jobRunner = new RunPRJobCommand(cli, request, argv);
|
578
587
|
return jobRunner.start();
|
579
588
|
}
|
package/lib/config.js
CHANGED
@@ -2,6 +2,8 @@ import path from 'node:path';
|
|
2
2
|
import os from 'node:os';
|
3
3
|
|
4
4
|
import { readJson, writeJson } from './file.js';
|
5
|
+
import { existsSync } from 'node:fs';
|
6
|
+
import { spawnSync } from 'node:child_process';
|
5
7
|
|
6
8
|
export const GLOBAL_CONFIG = Symbol('globalConfig');
|
7
9
|
export const PROJECT_CONFIG = Symbol('projectConfig');
|
@@ -25,6 +27,14 @@ export function getMergedConfig(dir, home) {
|
|
25
27
|
|
26
28
|
export function getConfig(configType, dir) {
|
27
29
|
const configPath = getConfigPath(configType, dir);
|
30
|
+
const encryptedConfigPath = configPath + '.gpg';
|
31
|
+
if (existsSync(encryptedConfigPath)) {
|
32
|
+
const { status, stdout } =
|
33
|
+
spawnSync('gpg', ['--decrypt', encryptedConfigPath]);
|
34
|
+
if (status === 0) {
|
35
|
+
return JSON.parse(stdout.toString('utf-8'));
|
36
|
+
}
|
37
|
+
}
|
28
38
|
try {
|
29
39
|
return readJson(configPath);
|
30
40
|
} catch (cause) {
|
@@ -1,9 +1,12 @@
|
|
1
|
+
import fs from 'node:fs';
|
1
2
|
import {
|
2
3
|
NEXT_SECURITY_RELEASE_REPOSITORY,
|
3
4
|
checkoutOnSecurityReleaseBranch,
|
4
5
|
getVulnerabilitiesJSON,
|
6
|
+
getVulnerabilitiesJSONPath,
|
5
7
|
validateDate,
|
6
8
|
formatDateToYYYYMMDD,
|
9
|
+
commitAndPushVulnerabilitiesJSON,
|
7
10
|
createIssue
|
8
11
|
} from './security-release/security-release.js';
|
9
12
|
import auth from './auth.js';
|
@@ -40,10 +43,21 @@ export default class SecurityAnnouncement {
|
|
40
43
|
validateDate(content.releaseDate);
|
41
44
|
const releaseDate = new Date(content.releaseDate);
|
42
45
|
|
43
|
-
await Promise.all([
|
46
|
+
const [dockerIssue, buildIssue] = await Promise.all([
|
44
47
|
this.createDockerNodeIssue(releaseDate),
|
45
48
|
this.createBuildWGIssue(releaseDate)
|
46
49
|
]);
|
50
|
+
|
51
|
+
content.buildIssue = buildIssue;
|
52
|
+
content.dockerIssue = dockerIssue;
|
53
|
+
|
54
|
+
const vulnerabilitiesJSONPath = getVulnerabilitiesJSONPath();
|
55
|
+
fs.writeFileSync(vulnerabilitiesJSONPath, JSON.stringify(content, null, 2));
|
56
|
+
const commitMessage = 'chore: add build and docker issue link';
|
57
|
+
commitAndPushVulnerabilitiesJSON([vulnerabilitiesJSONPath],
|
58
|
+
commitMessage, { cli: this.cli, repository: this.repository });
|
59
|
+
|
60
|
+
this.cli.ok('Added docker and build issue in vulnerabilities.json');
|
47
61
|
}
|
48
62
|
|
49
63
|
async createBuildWGIssue(releaseDate) {
|
@@ -53,7 +67,7 @@ export default class SecurityAnnouncement {
|
|
53
67
|
};
|
54
68
|
|
55
69
|
const { title, content } = this.createPreleaseAnnouncementIssue(releaseDate, 'build');
|
56
|
-
|
70
|
+
return createIssue(title, content, repository, { cli: this.cli, req: this.req });
|
57
71
|
}
|
58
72
|
|
59
73
|
createPreleaseAnnouncementIssue(releaseDate, team) {
|
@@ -71,6 +85,6 @@ export default class SecurityAnnouncement {
|
|
71
85
|
};
|
72
86
|
|
73
87
|
const { title, content } = this.createPreleaseAnnouncementIssue(releaseDate, 'docker');
|
74
|
-
|
88
|
+
return createIssue(title, content, repository, { cli: this.cli, req: this.req });
|
75
89
|
}
|
76
90
|
}
|
@@ -107,6 +107,12 @@ export function getVulnerabilitiesJSON(cli) {
|
|
107
107
|
return file;
|
108
108
|
}
|
109
109
|
|
110
|
+
export function getVulnerabilitiesJSONPath() {
|
111
|
+
const vulnerabilitiesJSONPath = path.join(process.cwd(),
|
112
|
+
NEXT_SECURITY_RELEASE_FOLDER, 'vulnerabilities.json');
|
113
|
+
return vulnerabilitiesJSONPath;
|
114
|
+
}
|
115
|
+
|
110
116
|
export function validateDate(releaseDate) {
|
111
117
|
const value = new Date(releaseDate).valueOf();
|
112
118
|
if (Number.isNaN(value) || value < 0) {
|
@@ -135,6 +141,7 @@ export async function createIssue(title, content, repository, { cli, req }) {
|
|
135
141
|
const data = await req.createIssue(title, content, repository);
|
136
142
|
if (data.html_url) {
|
137
143
|
cli.ok(`Created: ${data.html_url}`);
|
144
|
+
return data.html_url;
|
138
145
|
} else {
|
139
146
|
cli.error(data);
|
140
147
|
process.exit(1);
|
package/lib/security_blog.js
CHANGED
@@ -6,7 +6,8 @@ import {
|
|
6
6
|
PLACEHOLDERS,
|
7
7
|
checkoutOnSecurityReleaseBranch,
|
8
8
|
validateDate,
|
9
|
-
SecurityRelease
|
9
|
+
SecurityRelease,
|
10
|
+
commitAndPushVulnerabilitiesJSON,
|
10
11
|
} from './security-release/security-release.js';
|
11
12
|
import auth from './auth.js';
|
12
13
|
import Request from './request.js';
|
@@ -56,16 +57,41 @@ export default class SecurityBlog extends SecurityRelease {
|
|
56
57
|
const endDate = new Date(data.annoucementDate);
|
57
58
|
endDate.setDate(endDate.getDate() + 7);
|
58
59
|
|
60
|
+
const link = `https://nodejs.org/en/blog/vulnerability/${fileName}`;
|
59
61
|
this.updateWebsiteBanner(site, {
|
60
62
|
startDate: data.annoucementDate,
|
61
63
|
endDate: endDate.toISOString(),
|
62
64
|
text: `New security releases to be made available ${data.releaseDate}`,
|
63
|
-
link
|
65
|
+
link,
|
64
66
|
type: 'warning'
|
65
67
|
});
|
66
|
-
|
67
68
|
fs.writeFileSync(file, preRelease);
|
69
|
+
|
68
70
|
cli.ok(`Announcement file created and banner has been updated. Folder: ${nodejsOrgFolder}`);
|
71
|
+
await this.updateAnnouncementLink(link);
|
72
|
+
}
|
73
|
+
|
74
|
+
async updateAnnouncementLink(link) {
|
75
|
+
const vulnerabilitiesJSONPath = this.getVulnerabilitiesJSONPath();
|
76
|
+
const content = this.readVulnerabilitiesJSON(vulnerabilitiesJSONPath);
|
77
|
+
let shouldCommit = false;
|
78
|
+
for (let i = 0; i < content.reports.length; ++i) {
|
79
|
+
if (content.reports[i].announcement !== link) {
|
80
|
+
content.reports[i].announcement = link;
|
81
|
+
shouldCommit = true;
|
82
|
+
}
|
83
|
+
};
|
84
|
+
|
85
|
+
if (shouldCommit) {
|
86
|
+
fs.writeFileSync(vulnerabilitiesJSONPath, JSON.stringify(content, null, 2));
|
87
|
+
const commitMessage = 'chore: add announcement link';
|
88
|
+
commitAndPushVulnerabilitiesJSON([vulnerabilitiesJSONPath],
|
89
|
+
commitMessage, { cli: this.cli, repository: this.repository });
|
90
|
+
|
91
|
+
this.cli.ok('Updated the announcement link in vulnerabilities.json');
|
92
|
+
}
|
93
|
+
|
94
|
+
return [vulnerabilitiesJSONPath];
|
69
95
|
}
|
70
96
|
|
71
97
|
async createPostRelease(nodejsOrgFolder) {
|
@@ -152,6 +152,7 @@ export default class UpdateSecurityRelease extends SecurityRelease {
|
|
152
152
|
for (const cve of cves) {
|
153
153
|
const report = reports.find(report => report.id === cve.reportId);
|
154
154
|
report.cveIds = [cve.cve_identifier];
|
155
|
+
report.patchedVersions = cve.patchedVersions;
|
155
156
|
}
|
156
157
|
}
|
157
158
|
|
@@ -219,12 +220,14 @@ Summary: ${summary}\n`,
|
|
219
220
|
|
220
221
|
if (!create) continue;
|
221
222
|
|
223
|
+
const { h1AffectedVersions, patchedVersions } =
|
224
|
+
await this.calculateVersions(affectedVersions, supportedVersions);
|
222
225
|
const body = {
|
223
226
|
data: {
|
224
227
|
type: 'cve-request',
|
225
228
|
attributes: {
|
226
229
|
team_handle: 'nodejs-team',
|
227
|
-
versions:
|
230
|
+
versions: h1AffectedVersions,
|
228
231
|
metrics: [
|
229
232
|
{
|
230
233
|
vectorString: cvss_vector_string
|
@@ -246,7 +249,7 @@ Summary: ${summary}\n`,
|
|
246
249
|
continue;
|
247
250
|
}
|
248
251
|
const { cve_identifier } = data.attributes;
|
249
|
-
cves.push({ cve_identifier, reportId: id });
|
252
|
+
cves.push({ cve_identifier, reportId: id, patchedVersions });
|
250
253
|
}
|
251
254
|
return cves;
|
252
255
|
}
|
@@ -262,15 +265,23 @@ Summary: ${summary}\n`,
|
|
262
265
|
}
|
263
266
|
}
|
264
267
|
|
265
|
-
async
|
266
|
-
const
|
268
|
+
async calculateVersions(affectedVersions, supportedVersions) {
|
269
|
+
const h1AffectedVersions = [];
|
270
|
+
const patchedVersions = [];
|
267
271
|
for (const affectedVersion of affectedVersions) {
|
268
272
|
const major = affectedVersion.split('.')[0];
|
269
273
|
const latest = supportedVersions.find((v) => v.major === Number(major)).version;
|
270
274
|
const version = await this.cli.prompt(
|
271
275
|
`What is the affected version (<=) for release line ${affectedVersion}?`,
|
272
276
|
{ questionType: 'input', defaultAnswer: latest });
|
273
|
-
|
277
|
+
|
278
|
+
const nextPatchVersion = parseInt(version.split('.')[2]) + 1;
|
279
|
+
const patchedVersion = await this.cli.prompt(
|
280
|
+
`What is the patched version (>=) for release line ${affectedVersion}?`,
|
281
|
+
{ questionType: 'input', defaultAnswer: nextPatchVersion });
|
282
|
+
|
283
|
+
patchedVersions.push(patchedVersion);
|
284
|
+
h1AffectedVersions.push({
|
274
285
|
vendor: 'nodejs',
|
275
286
|
product: 'node',
|
276
287
|
func: '<=',
|
@@ -279,6 +290,6 @@ Summary: ${summary}\n`,
|
|
279
290
|
affected: true
|
280
291
|
});
|
281
292
|
}
|
282
|
-
return
|
293
|
+
return { h1AffectedVersions, patchedVersions };
|
283
294
|
}
|
284
295
|
}
|
package/lib/voting_session.js
CHANGED
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@node-core/utils",
|
3
|
-
"version": "5.
|
3
|
+
"version": "5.15.0",
|
4
4
|
"description": "Utilities for Node.js core collaborators",
|
5
5
|
"type": "module",
|
6
6
|
"engines": {
|
@@ -68,6 +68,6 @@
|
|
68
68
|
"eslint-plugin-promise": "^7.2.1",
|
69
69
|
"globals": "^16.0.0",
|
70
70
|
"neostandard": "^0.12.1",
|
71
|
-
"sinon": "^
|
71
|
+
"sinon": "^21.0.0"
|
72
72
|
}
|
73
73
|
}
|