انتشار امنیتی باید به یک اقدام قابل بررسی تبدیل شود

در ۲۱ اکتبر ۲۰۲۶، یادداشت‌های رسمی Node.js نسخه‌های 22.23.4، 24.22.1 و 26.12.1 را به‌عنوان انتشار امنیتی معرفی کردند. برای تیم عملیات، خواندن شمارهٔ نسخه تنها ابتدای کار است: باید معلوم شود کدام سرویس از کدام مفسر استفاده می‌کند و چگونه نتیجهٔ ارتقا ثبت می‌شود. این مقاله یک کنترل کوچک و قابل آزمون برای تبدیل یک خط مبنای تاریخ‌دار به مرحلهٔ پذیرش استقرار ارائه می‌دهد.

سیاست نمونه برای هر شاخه حداقل نسخهٔ بررسی‌شده را جداگانه نگه می‌دارد. مقایسهٔ نسخه، مقایسهٔ رشته‌ای نیست؛ عدد minor و patch در همان major بررسی می‌شود. شاخهٔ ناشناخته و نسخهٔ prerelease در این سیاست پذیرفته نمی‌شوند، چون دربارهٔ آن‌ها بررسی انجام نشده است. این ردشدن به معنای اثبات آسیب‌پذیری همهٔ نسخه‌های دیگر نیست؛ نتیجه فقط با محدودهٔ سیاستی که تاریخ و منابعش مشخص است ارتباط دارد.

دروازهٔ نسخه با سیاست تاریخ‌دار

فایل سیاست تاریخ بازبینی و حداکثر عمر دارد. بعد از گذشت بازه، دروازه درخواست بازبینی تازه می‌کند تا حداقل نسخهٔ امروز به تضمینی دائمی برای ماه‌های بعد تبدیل نشود. در نمونه، مبنا ۲۵ اکتبر ۲۰۲۶ و بازه سی روز است. پیش از استفادهٔ واقعی، خبرهای امنیتی بعدی و وضعیت پشتیبانی شاخه را بررسی کنید و سیاست را با فرایند مدیریت تغییر خود به‌روزرسانی کنید.

check-node نسخهٔ فرایند جاری را می‌سنجد؛ برای سرویس کانتینری باید همین بررسی داخل تصویر یا محیط اجرای آن سرویس انجام شود. ارتقای Node.js ترمینال میزبان، نسخهٔ داخل یک تصویر قدیمی را عوض نمی‌کند. فایل upgrade-evidence یک فرایند فرزند را با همان executable اجرا می‌کند و نتیجه، معماری و زمان بررسی را ثبت می‌کند. فایل شواهد موجود بازنویسی نمی‌شود تا نتیجهٔ قبلی بی‌صدا از بین نرود.

آزمون‌ها حداقل نسخهٔ هر سه شاخه، patch پیشین، شاخهٔ ناشناخته، prerelease و سیاست منقضی یا آینده‌دار را پوشش می‌دهند. این‌ها کنترل تصمیم دروازه هستند، نه اسکن کامل آسیب‌پذیری وابستگی‌ها یا آزمون سازگاری برنامه. پس از ارتقای runtime، آزمون‌های برنامه، ارتباط TLS، درخواست‌های HTTP و بسته‌های native نیز باید در فرایند استقرار واقعی اجرا شوند. تأیید نسخه جای این پذیرش‌ها را نمی‌گیرد.

فایل‌های کامل، روش اجرا و آزمون

Node.js 22+ برای اجرای ابزارها؛ نسخهٔ runtime باید با سیاست تاریخ‌دار سنجیده شود و آزمون‌ها نیاز به شبکه ندارند. چهار فایل زیر را با همین نام‌ها در یک پوشه ذخیره کنید؛ هیچ بخش کد با سه‌نقطه جایگزین نشده است.

1. سیاست نسخه با تاریخ بازبینی

نام فایل: node-policy.json؛ این فایل را کامل و بدون حذف سطرها ذخیره کنید.

{
  "reviewedOn": "2026-10-25",
  "maximumAgeDays": 30,
  "minimumByMajor": {
    "22": "22.23.4",
    "24": "24.22.1",
    "26": "26.12.1"
  }
}

این اعداد خط مبنای بررسی‌شده در ۲۵ اکتبر ۲۰۲۶ هستند. پیش از استفادهٔ بعدی منابع رسمی و عمر سیاست را بررسی کنید؛ نسخهٔ پذیرفته‌شدهٔ امروز تضمین دائمی نیست.

2. دروازهٔ نسخه با خروجی قابل پردازش

نام فایل: check-node.mjs؛ این فایل را کامل و بدون حذف سطرها ذخیره کنید.

import { readFileSync } from 'node:fs';
import { pathToFileURL } from 'node:url';

export function assessVersion(version, policy, today = new Date().toISOString().slice(0, 10)) {
  const match = /^v?(\d+)\.(\d+)\.(\d+)$/.exec(version);
  if (!match) return { allowed: false, reason: 'Not a reviewed stable version', version };
  const current = match.slice(1).map(Number);
  const minimum = policy.minimumByMajor[String(current[0])];
  if (!minimum) return { allowed: false, reason: 'Major branch is not in this policy', version };
  const age = (Date.parse(today) - Date.parse(policy.reviewedOn)) / 86400000;
  if (!Number.isFinite(age) || age < 0 || age > policy.maximumAgeDays) {
    return { allowed: false, reason: 'Policy needs a fresh review', version, minimum };
  }
  const required = minimum.split('.').map(Number);
  const allowed = current[1] > required[1] || (current[1] === required[1] && current[2] >= required[2]);
  return { allowed, version, minimum, reviewedOn: policy.reviewedOn,
    reason: allowed ? 'Meets this dated baseline' : 'Below the reviewed baseline' };
}

if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
  const policy = JSON.parse(readFileSync(new URL('./node-policy.json', import.meta.url), 'utf8'));
  const result = assessVersion(process.version, policy);
  console.log(JSON.stringify(result, null, 2));
  process.exitCode = result.allowed ? 0 : 1;
}

node check-node.mjs را اجرا کنید. خروجی صفر عبور از سیاست و خروجی یک توقف است. این فایل فقط نسخهٔ مفسر جاری را می‌سنجد و چیزی نصب یا ارتقا نمی‌دهد.

3. آزمون نسخه‌های مرزی و انقضای سیاست

نام فایل: test-node-policy.mjs؛ این فایل را کامل و بدون حذف سطرها ذخیره کنید.

import test from 'node:test';
import assert from 'node:assert/strict';
import { readFileSync } from 'node:fs';
import { assessVersion } from './check-node.mjs';

const policy = JSON.parse(readFileSync(new URL('./node-policy.json', import.meta.url), 'utf8'));
const check = version => assessVersion(version, policy, '2026-10-25');
test('all reviewed baseline versions pass', () => {
  for (const version of ['22.23.4', '24.22.1', 'v26.12.1', '24.23.0']) {
    assert.equal(check(version).allowed, true);
  }
});
test('previous patches, unknown branches and prereleases are refused', () => {
  for (const version of ['22.23.3', '24.22.0', '26.12.0', '25.9.0', '26.12.1-rc.1', 'not-a-version']) {
    assert.equal(check(version).allowed, false);
  }
});
test('an old or future policy is not treated as current assurance', () => {
  assert.equal(assessVersion('24.22.1', policy, '2026-12-01').allowed, false);
  assert.equal(assessVersion('24.22.1', policy, '2026-10-01').allowed, false);
});

با node --test test-node-policy.mjs سه گروه آزمون اجرا می‌شود. زمان آزمون ثابت است تا نتیجه با تاریخ اجرای لپ‌تاپ یا CI به‌طور اتفاقی تغییر نکند.

4. ثبت شواهد runtime هدف

نام فایل: upgrade-evidence.mjs؛ این فایل را کامل و بدون حذف سطرها ذخیره کنید.

import { spawnSync } from 'node:child_process';
import { readFileSync, writeFileSync } from 'node:fs';
import { assessVersion } from './check-node.mjs';

const probe = spawnSync(process.execPath, ['--version'], { encoding: 'utf8', shell: false });
if (probe.status !== 0) throw new Error('The selected runtime could not report its version');
const policy = JSON.parse(readFileSync(new URL('./node-policy.json', import.meta.url), 'utf8'));
const assessment = assessVersion(probe.stdout.trim(), policy);
const evidence = { checkedAt: new Date().toISOString(), platform: process.platform,
  architecture: process.arch, assessment };
writeFileSync('upgrade-evidence.json', JSON.stringify(evidence, null, 2) + '\n', { flag: 'wx' });
console.log(JSON.stringify(evidence, null, 2));
process.exitCode = assessment.allowed ? 0 : 1;

این فایل را در پوشهٔ کاری آزمایش اجرا کنید. upgrade-evidence.json با حالت انحصاری ایجاد می‌شود و کد خروج با نتیجهٔ دروازه برابر است؛ در runtime قدیمی‌تر خروجی یک طبیعی است.

شواهد اجرای واقعی و برنامهٔ بازبینی

چهار فایل را کنار هم بگذارید و node --test test-node-policy.mjs را اجرا کنید. سپس node check-node.mjs را با runtime هدف اجرا بگیرید؛ خروجی صفر یعنی عبور از همین سیاست و خروجی یک یعنی توقف برای بررسی. روی مفسر قدیمی‌تر، ردشدن نتیجهٔ مورد انتظار است. این آموزش هیچ runtime سیستم را ارتقا نمی‌دهد و فایل سیاست را نیز از شبکه دریافت نمی‌کند؛ تغییر نسخه باید از مسیر کنترل‌شدهٔ استقرار پروژه انجام شود.

چک‌لیست اجرای عملی

  • حداقل نسخه را از یادداشت رسمی همان شاخه بخوانید.
  • فرایند داخل سرویس یا کانتینر واقعی را بسنجید.
  • سیاست منقضی باید بازبینی تازه بخواهد.
  • آزمون برنامه و وابستگی‌های native پس از ارتقا اجرا شود.

منابع

تدوین و راهنمای عملی: دانشنامه لیان.