统一 Node 22 与后端安全运行基线(07-15-production-trust-closeout-cleanup)

这个提交包含在:
2026-07-16 05:17:18 +08:00
父节点 dcf34d7ac6
当前提交 abb6a22f5c
共修改 28 个文件,包含 2250 行新增和 3012 行删除
+36 -6
查看文件
@@ -17,7 +17,7 @@ function resolveEmailBaseUrl(config) {
let cachedTransportKey = null;
let cachedTransporter = null;
function getTransporter(config) {
function getTransporter(config, createTransport = nodemailer.createTransport) {
if (!config.emailEnabled) {
return null;
}
@@ -41,7 +41,7 @@ function getTransporter(config) {
}
cachedTransportKey = key;
cachedTransporter = nodemailer.createTransport({
cachedTransporter = createTransport({
host: config.smtpHost,
port: config.smtpPort,
secure: config.smtpSecure,
@@ -86,6 +86,36 @@ function uniq(values) {
return Array.from(new Set((values ?? []).filter(Boolean)));
}
const SAFE_SMTP_COMMANDS = new Set([
'CONN',
'EHLO',
'HELO',
'MAIL FROM',
'RCPT TO',
'DATA',
'AUTH',
]);
function buildEmailErrorLog(error) {
const result = { errorCode: 'email_delivery_failed' };
const providerCode = String(error?.code ?? '').trim();
if (/^[A-Za-z0-9_-]{1,40}$/.test(providerCode)) {
result.providerCode = providerCode;
}
const smtpCommand = String(error?.command ?? '').trim().toUpperCase();
if (SAFE_SMTP_COMMANDS.has(smtpCommand)) {
result.smtpCommand = smtpCommand;
}
const smtpResponseCode = Number(error?.responseCode);
if (Number.isInteger(smtpResponseCode) && smtpResponseCode >= 100 && smtpResponseCode <= 999) {
result.smtpResponseCode = smtpResponseCode;
}
return result;
}
async function sendNotificationEmailSafe(params) {
try {
const emailResult = await sendNotificationEmail(params);
@@ -95,7 +125,7 @@ async function sendNotificationEmailSafe(params) {
} catch (error) {
params.logger?.error?.(
{
err: error,
...buildEmailErrorLog(error),
requestId: params.requestId,
notificationType: params.type,
recipients: params.recipientEmployeeIds?.length ?? 0,
@@ -118,12 +148,13 @@ async function sendNotificationEmail({
body,
type = 'notification',
targetPath,
createTransport,
}) {
if (!config.emailEnabled) {
return { skipped: true, sent: 0, failed: 0 };
}
const transporter = getTransporter(config);
const transporter = getTransporter(config, createTransport);
if (!transporter) {
logger?.warn?.({ requestId }, 'Email enabled but transporter not configured');
return { skipped: true, sent: 0, failed: 0 };
@@ -184,10 +215,9 @@ async function sendNotificationEmail({
failed += 1;
logger?.error?.(
{
err: error,
...buildEmailErrorLog(error),
requestId,
employeeId: employee.id,
email: employee.email,
notificationType: type,
},
'Email send failed',