Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 8fc962f

Browse filesBrowse files
richardlauaduh95
authored andcommitted
tools: fix root certificate updater
Determine the NSS version from actual Firefox releases, instead of attempting to parse a wiki page (which is sensitive to formatting changes and relies on the page being up to date). PR-URL: #55681 Reviewed-By: Luigi Pinca <luigipinca@gmail.com> Reviewed-By: Michaël Zasso <targos@protonmail.com> Reviewed-By: Rafael Gonzaga <rafael.nunu@hotmail.com>
1 parent 7bfd295 commit 8fc962f
Copy full SHA for 8fc962f

File tree

Expand file treeCollapse file tree

1 file changed

+69
-117
lines changed
Open diff view settings
Filter options
Expand file treeCollapse file tree

1 file changed

+69
-117
lines changed
Open diff view settings
Collapse file

‎tools/dep_updaters/update-root-certs.mjs‎

Copy file name to clipboardExpand all lines: tools/dep_updaters/update-root-certs.mjs
+69-117Lines changed: 69 additions & 117 deletions
Original file line numberDiff line numberDiff line change
@@ -8,109 +8,78 @@ import { pipeline } from 'node:stream/promises';
88
import { fileURLToPath } from 'node:url';
99
import { parseArgs } from 'node:util';
1010

11-
// Constants for NSS release metadata.
12-
const kNSSVersion = 'version';
13-
const kNSSDate = 'date';
14-
const kFirefoxVersion = 'firefoxVersion';
15-
const kFirefoxDate = 'firefoxDate';
16-
1711
const __filename = fileURLToPath(import.meta.url);
18-
const now = new Date();
19-
20-
const formatDate = (d) => {
21-
const iso = d.toISOString();
22-
return iso.substring(0, iso.indexOf('T'));
23-
};
2412

2513
const getCertdataURL = (version) => {
2614
const tag = `NSS_${version.replaceAll('.', '_')}_RTM`;
27-
const certdataURL = `https://hg.mozilla.org/projects/nss/raw-file/${tag}/lib/ckfw/builtins/certdata.txt`;
15+
const certdataURL = `https://raw.githubusercontent.com/nss-dev/nss/refs/tags/${tag}/lib/ckfw/builtins/certdata.txt`;
2816
return certdataURL;
2917
};
3018

31-
const normalizeTD = (text = '') => {
32-
// Remove whitespace and any HTML tags.
33-
return text?.trim().replace(/<.*?>/g, '');
34-
};
35-
const getReleases = (text) => {
36-
const releases = [];
37-
const tableRE = /<table [^>]+>([\S\s]*?)<\/table>/g;
38-
const tableRowRE = /<tr ?[^>]*>([\S\s]*?)<\/tr>/g;
39-
const tableHeaderRE = /<th ?[^>]*>([\S\s]*?)<\/th>/g;
40-
const tableDataRE = /<td ?[^>]*>([\S\s]*?)<\/td>/g;
41-
for (const table of text.matchAll(tableRE)) {
42-
const columns = {};
43-
const matches = table[1].matchAll(tableRowRE);
44-
// First row has the table header.
45-
let row = matches.next();
46-
if (row.done) {
47-
continue;
48-
}
49-
const headers = Array.from(row.value[1].matchAll(tableHeaderRE), (m) => m[1]);
50-
if (headers.length > 0) {
51-
for (let i = 0; i < headers.length; i++) {
52-
if (/NSS version/i.test(headers[i])) {
53-
columns[kNSSVersion] = i;
54-
} else if (/Release.*from branch/i.test(headers[i])) {
55-
columns[kNSSDate] = i;
56-
} else if (/Firefox version/i.test(headers[i])) {
57-
columns[kFirefoxVersion] = i;
58-
} else if (/Firefox release date/i.test(headers[i])) {
59-
columns[kFirefoxDate] = i;
60-
}
61-
}
62-
}
63-
// Filter out "NSS Certificate bugs" table.
64-
if (columns[kNSSDate] === undefined) {
65-
continue;
66-
}
67-
// Scrape releases.
68-
row = matches.next();
69-
while (!row.done) {
70-
const cells = Array.from(row.value[1].matchAll(tableDataRE), (m) => m[1]);
71-
const release = {};
72-
release[kNSSVersion] = normalizeTD(cells[columns[kNSSVersion]]);
73-
release[kNSSDate] = new Date(normalizeTD(cells[columns[kNSSDate]]));
74-
release[kFirefoxVersion] = normalizeTD(cells[columns[kFirefoxVersion]]);
75-
release[kFirefoxDate] = new Date(normalizeTD(cells[columns[kFirefoxDate]]));
76-
releases.push(release);
77-
row = matches.next();
78-
}
19+
const getFirefoxReleases = async (everything = false) => {
20+
const releaseDataURL = `https://nucleus.mozilla.org/rna/all-releases.json${everything ? '?all=true' : ''}`;
21+
if (values.verbose) {
22+
console.log(`Fetching Firefox release data from ${releaseDataURL}.`);
23+
}
24+
const releaseData = await fetch(releaseDataURL);
25+
if (!releaseData.ok) {
26+
console.error(`Failed to fetch ${releaseDataURL}: ${releaseData.status}: ${releaseData.statusText}.`);
27+
process.exit(-1);
7928
}
80-
return releases;
29+
return (await releaseData.json()).filter((release) => {
30+
// We're only interested in public releases of Firefox.
31+
return (release.product === 'Firefox' && release.channel === 'Release' && release.is_public === true);
32+
}).sort((a, b) => {
33+
// Sort results by release date.
34+
return new Date(b.release_date) - new Date(a.release_date);
35+
});
8136
};
8237

83-
const getLatestVersion = async (releases) => {
84-
const arrayNumberSortDescending = (x, y, i) => {
85-
if (x[i] === undefined && y[i] === undefined) {
86-
return 0;
87-
} else if (x[i] === y[i]) {
88-
return arrayNumberSortDescending(x, y, i + 1);
89-
}
90-
return (y[i] ?? 0) - (x[i] ?? 0);
91-
};
92-
const extractVersion = (t) => {
93-
return t[kNSSVersion].split('.').map((n) => parseInt(n));
94-
};
95-
const releaseSorter = (x, y) => {
96-
return arrayNumberSortDescending(extractVersion(x), extractVersion(y), 0);
97-
};
98-
// Return the most recent certadata.txt that exists on the server.
99-
const sortedReleases = releases.sort(releaseSorter).filter(pastRelease);
100-
for (const candidate of sortedReleases) {
101-
const candidateURL = getCertdataURL(candidate[kNSSVersion]);
102-
if (values.verbose) {
103-
console.log(`Trying ${candidateURL}`);
38+
const getFirefoxRelease = async (version) => {
39+
let releases = await getFirefoxReleases();
40+
let found;
41+
if (version === undefined) {
42+
// No version specified. Find the most recent.
43+
if (releases.length > 0) {
44+
found = releases[0];
45+
} else {
46+
if (values.verbose) {
47+
console.log('Unable to find release data for Firefox. Searching full release data.');
48+
}
49+
releases = await getFirefoxReleases(true);
50+
found = releases[0];
10451
}
105-
const response = await fetch(candidateURL, { method: 'HEAD' });
106-
if (response.ok) {
107-
return candidate[kNSSVersion];
52+
} else {
53+
// Search for the specified release.
54+
found = releases.find((release) => release.version === version);
55+
if (found === undefined) {
56+
if (values.verbose) {
57+
console.log(`Unable to find release data for Firefox ${version}. Searching full release data.`);
58+
}
59+
releases = await getFirefoxReleases(true);
60+
found = releases.find((release) => release.version === version);
10861
}
10962
}
63+
return found;
11064
};
11165

112-
const pastRelease = (r) => {
113-
return r[kNSSDate] < now;
66+
const getNSSVersion = async (release) => {
67+
const latestFirefox = release.version;
68+
const firefoxTag = `FIREFOX_${latestFirefox.replace('.', '_')}_RELEASE`;
69+
const tagInfoURL = `https://hg.mozilla.org/releases/mozilla-release/raw-file/${firefoxTag}/security/nss/TAG-INFO`;
70+
if (values.verbose) {
71+
console.log(`Fetching NSS tag from ${tagInfoURL}.`);
72+
}
73+
const tagInfo = await fetch(tagInfoURL);
74+
if (!tagInfo.ok) {
75+
console.error(`Failed to fetch ${tagInfoURL}: ${tagInfo.status}: ${tagInfo.statusText}`);
76+
}
77+
const tag = await tagInfo.text();
78+
if (values.verbose) {
79+
console.log(`Found tag ${tag}.`);
80+
}
81+
// Tag will be of form `NSS_x_y_RTM`. Convert to `x.y`.
82+
return tag.split('_').slice(1, -1).join('.');
11483
};
11584

11685
const options = {
@@ -135,9 +104,9 @@ const {
135104
});
136105

137106
if (values.help) {
138-
console.log(`Usage: ${basename(__filename)} [OPTION]... [VERSION]...`);
107+
console.log(`Usage: ${basename(__filename)} [OPTION]... [RELEASE]...`);
139108
console.log();
140-
console.log('Updates certdata.txt to NSS VERSION (most recent release by default).');
109+
console.log('Updates certdata.txt to NSS version contained in Firefox RELEASE (default: most recent release).');
141110
console.log('');
142111
console.log(' -f, --file=FILE writes a commit message reflecting the change to the');
143112
console.log(' specified FILE');
@@ -146,29 +115,11 @@ if (values.help) {
146115
process.exit(0);
147116
}
148117

149-
const scheduleURL = 'https://wiki.mozilla.org/NSS:Release_Versions';
150-
if (values.verbose) {
151-
console.log(`Fetching NSS release schedule from ${scheduleURL}`);
152-
}
153-
const schedule = await fetch(scheduleURL);
154-
if (!schedule.ok) {
155-
console.error(`Failed to fetch ${scheduleURL}: ${schedule.status}: ${schedule.statusText}`);
156-
process.exit(-1);
157-
}
158-
const scheduleText = await schedule.text();
159-
const nssReleases = getReleases(scheduleText);
160-
118+
const firefoxRelease = await getFirefoxRelease(positionals[0]);
161119
// Retrieve metadata for the NSS release being updated to.
162-
const version = positionals[0] ?? await getLatestVersion(nssReleases);
163-
const release = nssReleases.find((r) => {
164-
return new RegExp(`^${version.replace('.', '\\.')}\\b`).test(r[kNSSVersion]);
165-
});
166-
if (!pastRelease(release)) {
167-
console.warn(`Warning: NSS ${version} is not due to be released until ${formatDate(release[kNSSDate])}`);
168-
}
120+
const version = await getNSSVersion(firefoxRelease);
169121
if (values.verbose) {
170-
console.log('Found NSS version:');
171-
console.log(release);
122+
console.log(`Updating to NSS version ${version}`);
172123
}
173124

174125
// Fetch certdata.txt and overwrite the local copy.
@@ -213,14 +164,15 @@ const added = [ ...diff.matchAll(certsAddedRE) ].map((m) => m[1]);
213164
const removed = [ ...diff.matchAll(certsRemovedRE) ].map((m) => m[1]);
214165

215166
const commitMsg = [
216-
`crypto: update root certificates to NSS ${release[kNSSVersion]}`,
167+
`crypto: update root certificates to NSS ${version}`,
217168
'',
218-
`This is the certdata.txt[0] from NSS ${release[kNSSVersion]}, released on ${formatDate(release[kNSSDate])}.`,
219-
'',
220-
`This is the version of NSS that ${release[kFirefoxDate] < now ? 'shipped' : 'will ship'} in Firefox ${release[kFirefoxVersion]} on`,
221-
`${formatDate(release[kFirefoxDate])}.`,
169+
`This is the certdata.txt[0] from NSS ${version}.`,
222170
'',
223171
];
172+
if (firefoxRelease) {
173+
commitMsg.push(`This is the version of NSS that shipped in Firefox ${firefoxRelease.version} on ${firefoxRelease.release_date}.`);
174+
commitMsg.push('');
175+
}
224176
if (added.length > 0) {
225177
commitMsg.push('Certificates added:');
226178
commitMsg.push(...added.map((cert) => `- ${cert}`));
@@ -234,7 +186,7 @@ if (removed.length > 0) {
234186
commitMsg.push(`[0] ${certdataURL}`);
235187
const delimiter = randomUUID();
236188
const properties = [
237-
`NEW_VERSION=${release[kNSSVersion]}`,
189+
`NEW_VERSION=${version}`,
238190
`COMMIT_MSG<<${delimiter}`,
239191
...commitMsg,
240192
delimiter,

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.