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
13 changes: 12 additions & 1 deletion .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,14 @@ jobs:
with:
fetch-depth: 2

- name: Set up Node.js
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: "22.23.4"

- name: Verify Node.js security baseline
run: node scripts/check-node-version.cjs

- name: Check if version changed
id: version
run: |
Expand All @@ -54,7 +62,10 @@ jobs:
- name: Set up Node.js
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: "22"
node-version: "22.23.4"

- name: Verify Node.js security baseline
run: node scripts/check-node-version.cjs

- name: Install dependencies
run: npm ci
Expand Down
6 changes: 5 additions & 1 deletion .github/workflows/performance.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,14 @@ jobs:
- name: Set up Node.js
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: "22"
node-version: "22.23.4"
cache: npm
cache-dependency-path: pr/package-lock.json

- name: Verify Node.js security baseline
working-directory: pr
run: node scripts/check-node-version.cjs

- name: Install and build candidate
working-directory: pr
run: |
Expand Down
22 changes: 18 additions & 4 deletions .github/workflows/pr-validation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,10 @@ jobs:
- name: Set up Node.js
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: "22"
node-version: "22.23.4"

- name: Verify Node.js security baseline
run: node scripts/check-node-version.cjs

- name: Install dependencies
run: npm ci
Expand All @@ -42,7 +45,7 @@ jobs:
strategy:
fail-fast: false
matrix:
node-version: ["22", "24"]
node-version: ["22.23.4", "24.22.1"]

steps:
- name: Check out repository
Expand All @@ -53,6 +56,12 @@ jobs:
with:
node-version: ${{ matrix.node-version }}

- name: Verify Node.js security baseline
run: node scripts/check-node-version.cjs

- name: Test Node.js security baseline guard
run: node --test test/integration/node-version.test.mjs

- name: Install dependencies
run: npm ci

Expand All @@ -79,9 +88,12 @@ jobs:
- name: Set up Node.js
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: "22"
node-version: "22.23.4"
cache: npm

- name: Verify Node.js security baseline
run: node scripts/check-node-version.cjs

- name: Install dependencies
run: npm ci

Expand All @@ -104,7 +116,9 @@ jobs:
- name: Set up Node.js
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: "22"
node-version: "22.23.4"
- name: Verify Node.js security baseline
run: node scripts/check-node-version.cjs
- name: Install dependencies
run: npm ci
- name: Build
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
## [Unreleased]

### Bugs Fixed
- Require Node.js 22.23.4+, 24.22.1+, or 26.12.1+ on their respective release lines for security fixes.
- **Breaking change on Unix (Linux/macOS):** Agent365 offline buffering/replay now requires an explicitly configured, existing, absolute `durableDelivery.storageDirectory`. No automatic Unix temporary-directory fallback or root creation. Missing/invalid roots are logged and disable only disk buffering/replay; online export continues. Customers provision and maintain ownership, permissions (normally a private service-account-owned `0700` root), ACLs, and path-replacement protection; the SDK does not inspect or repair Unix access controls. Windows behavior, explicit-root cache identities, record compatibility, and retention limits are unchanged. Old temporary backlogs are not automatically migrated; see [storage provisioning and migration](A365_DOCUMENTATION.md#storage-provisioning-and-migration).

### Features Added
Expand Down
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
]
},
"engines": {
"node": ">=22.0.0"
"node": "^22.23.4 || ^24.22.1 || >=26.12.1"
Comment thread
JacksonWeber marked this conversation as resolved.
Resolved
},
"files": [
"dist/",
Expand Down
4 changes: 2 additions & 2 deletions samples/agent365-s2s/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion samples/agent365-s2s/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"type": "module",
"description": "Agent365 service-to-service observability sample",
"engines": {
"node": ">=22.0.0"
"node": "^22.23.4 || ^24.22.1 || >=26.12.1"
},
"scripts": {
"build": "npm run clean && tsc -p tsconfig.json",
Expand Down
4 changes: 3 additions & 1 deletion samples/aks-langchain/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
FROM node:22-bookworm-slim
FROM node:22.23.4-bookworm-slim

RUN node -e "if (process.versions.node !== '22.23.4') throw new Error('Expected Node.js 22.23.4');"

ENV NODE_ENV=production \
PORT=3000
Expand Down
2 changes: 1 addition & 1 deletion samples/aks-langchain/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion samples/aks-langchain/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"private": true,
"type": "module",
"engines": {
"node": ">=22.0.0"
"node": "^22.23.4 || ^24.22.1 || >=26.12.1"
},
"scripts": {
"start": "node --import ./telemetry.mjs app.mjs"
Expand Down
3 changes: 3 additions & 0 deletions samples/fabric/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
"name": "fabric-demo",
"private": true,
"type": "module",
"engines": {
"node": "^22.23.4 || ^24.22.1 || >=26.12.1"
},
"scripts": {
"start": "OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318 node --import @microsoft/opentelemetry/loader app.mjs"
},
Expand Down
4 changes: 2 additions & 2 deletions samples/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion samples/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"version": "1.0.0",
"description": "Microsoft OpenTelemetry distribution samples for TypeScript",
"engines": {
"node": ">=22.0.0"
"node": "^22.23.4 || ^24.22.1 || >=26.12.1"
},
"scripts": {
"build": "tsc",
Expand Down
32 changes: 32 additions & 0 deletions scripts/check-node-version.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

const requiredNodeVersion = "^22.23.4 || ^24.22.1 || >=26.12.1";

function isPatchedNodeVersion(version) {
const match = /^v?(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/.exec(version);
if (!match) {
return false;
}

const [major, minor, patch] = match.slice(1).map(Number);
const minimum = { 22: [23, 4], 24: [22, 1], 26: [12, 1] }[major];
return (
major > 26 ||
(minimum !== undefined && (minor > minimum[0] || (minor === minimum[0] && patch >= minimum[1])))
);
}

if (require.main === module) {
if (!isPatchedNodeVersion(process.versions.node)) {
console.error(
`Node.js ${process.versions.node} does not meet the October 21, 2026 security baseline. ` +
`Required: ${requiredNodeVersion}. Upgrade the selected Node.js runtime.`,
);
process.exitCode = 1;
} else {
console.log(`Node.js ${process.versions.node} meets the October 21, 2026 security baseline.`);
}
}

module.exports = { isPatchedNodeVersion, requiredNodeVersion };
117 changes: 117 additions & 0 deletions test/integration/node-version.test.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

import assert from "node:assert/strict";
import { spawnSync } from "node:child_process";
import { readFileSync } from "node:fs";
import { test } from "node:test";
import { fileURLToPath } from "node:url";
import { runInNewContext } from "node:vm";
import { isPatchedNodeVersion, requiredNodeVersion } from "../../scripts/check-node-version.cjs";

test("accepts the October security releases and subsequent stable versions", () => {
for (const version of [
"22.23.4",
"22.23.5",
"22.24.0",
"24.22.1",
"24.22.2",
"24.23.0",
"26.12.1",
"26.12.2",
"26.13.0",
"27.0.0",
"v22.23.4",
]) {
assert.equal(isPatchedNodeVersion(version), true, version);
}
});

test("rejects vulnerable patches, end-of-life lines, and prereleases", () => {
for (const version of [
"20.99.99",
"22.0.0",
"22.22.99",
"22.23.3",
"23.99.99",
"24.21.99",
"24.22.0",
"25.99.99",
"26.11.99",
"26.12.0",
"22.23.4-rc.1",
"27.0.0-rc.1",
"",
"22",
"22.23",
"022.23.4",
"not-a-version",
]) {
assert.equal(isPatchedNodeVersion(version), false, version);
}
});

test("checks the running executable, not a supplied version argument or environment override", () => {
const script = fileURLToPath(new URL("../../scripts/check-node-version.cjs", import.meta.url));
for (const version of ["0.0.0", "99.0.0"]) {
const result = spawnSync(process.execPath, [script, version], {
encoding: "utf8",
env: { ...process.env, NODE_VERSION: version },
});
assert.ifError(result.error);
const expected = isPatchedNodeVersion(process.versions.node) ? 0 : 1;
assert.equal(result.status, expected, result.stderr);
assert.ok(`${result.stdout}${result.stderr}`.includes(process.versions.node));
}
});

test("reports failure for an old selected runtime even with patched-version overrides", () => {
const entry = {};
const errors = [];
const runtime = {
versions: { node: "24.18.0" },
argv: ["node", "check-node-version.cjs", "24.22.1"],
env: { NODE_VERSION: "24.22.1" },
};
const script = readFileSync(
new URL("../../scripts/check-node-version.cjs", import.meta.url),
"utf8",
);
runInNewContext(script, {
require: { main: entry },
module: entry,
process: runtime,
console: {
error: (message) => errors.push(message),
log: () => assert.fail("Reported success"),
},
});
assert.equal(runtime.exitCode, 1);
assert.match(errors[0], /Node\.js 24\.18\.0 does not meet/);
});

test("keeps repository package and linked lockfile runtime requirements aligned", () => {
for (const directory of [
"",
"samples/",
"samples/agent365-s2s/",
"samples/aks-langchain/",
"samples/fabric/",
]) {
const manifest = JSON.parse(
readFileSync(new URL(`../../${directory}package.json`, import.meta.url), "utf8"),
);
assert.equal(manifest.engines.node, requiredNodeVersion, directory || "root");
if (directory === "samples/fabric/") {
continue;
}
const lock = JSON.parse(
readFileSync(new URL(`../../${directory}package-lock.json`, import.meta.url), "utf8"),
);
for (const [path, entry] of Object.entries(lock.packages)) {
if (path === "" || path === ".." || path === "../..") {
assert.equal(entry.engines.node, requiredNodeVersion, `${directory}${path}`);
}
}
}
});
Loading