Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,15 @@ jobs:

- uses: actions/setup-node@v4
with:
Comment thread
cbeaudin-robotiq marked this conversation as resolved.
Resolved
node-version: 20
node-version-file: .nvmrc
check-latest: true
cache: npm

# Fails on a Node.js whose bundled Brotli is vulnerable to
# CVE-2026-111497 (see FIXED in scripts/check-node-version.js).
- name: Check Node.js version
Comment thread
cbeaudin-robotiq marked this conversation as resolved.
Resolved
run: node scripts/check-node-version.js

- name: Install dependencies
run: npm ci

Expand Down
8 changes: 7 additions & 1 deletion .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,15 @@ jobs:

- uses: actions/setup-node@v4
with:
node-version: 20
node-version-file: .nvmrc
check-latest: true
cache: npm

# Fails on a Node.js whose bundled Brotli is vulnerable to
# CVE-2026-111497 (see FIXED in scripts/check-node-version.js).
- name: Check Node.js version
run: node scripts/check-node-version.js

- name: Install dependencies
run: npm ci

Expand Down
1 change: 1 addition & 0 deletions .nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
22
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"license": "BSD-3-Clause",
"scripts": {
"docusaurus": "docusaurus",
"generate": "node scripts/fetch-profile-readme.js",
"generate": "node scripts/check-node-version.js && node scripts/fetch-profile-readme.js",
"prestart": "npm run generate",
"start": "docusaurus start",
"prebuild": "npm run generate",
Expand Down Expand Up @@ -39,6 +39,6 @@
]
},
"engines": {
"node": ">=20.0"
"node": "^22.23.4 || ^24.22.1 || ^26.12.1"
}
}
37 changes: 37 additions & 0 deletions scripts/check-node-version.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// Fails if the running Node.js bundles a Brotli decoder vulnerable to
// CVE-2026-111497 ("Node2Shell"): a crafted Brotli stream can corrupt
// memory in whatever decompresses it, and the build decompresses one —
// fetch() in fetch-profile-readme.js accepts `Content-Encoding: br`.
// Runs first in ci.yml and deploy.yml (before npm ci),
// and again via npm run generate.
//
// Only release lines with a fix are accepted, each from its first fixed
// version. Node 20 never got a fix, and odd lines (23, 25) are end of life.
// To move to a new line, add its first fixed version here, then update
// engines in package.json and .nvmrc.
// https://www.aikido.dev/blog/python-packages-vulnerable-node2shell-brotli-cve-2026-111497

const FIXED = {
22: [22, 23, 4],
24: [24, 22, 1],
26: [26, 12, 1],
};

function isFixed(version) {
const parts = version.replace(/^v/, '').split('.').map(Number);
const minimum = FIXED[parts[0]];
if (!minimum) return false;
for (let i = 1; i < 3; i++) {
if (parts[i] !== minimum[i]) return parts[i] > minimum[i];
}
return true;
}

if (require.main === module) {
if (!isFixed(process.version)) {
const accepted = Object.values(FIXED).map((v) => `>=${v.join('.')}`).join(', ');
console.error(`check-node-version.js: Node ${process.version} is vulnerable to CVE-2026-111497; use one of ${accepted}`);
process.exit(1);
}
console.log(`check-node-version.js: Node ${process.version} is not affected by CVE-2026-111497`);
}
Loading