From 44ac49feb9083ad4a1ead8ed4bef805ecddf1ce7 Mon Sep 17 00:00:00 2001 From: smail Date: Tue, 8 Sep 2026 13:18:17 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E5=96=84=E8=87=AA=E5=AE=9A=E4=B9=89?= =?UTF-8?q?=E8=AF=81=E4=B9=A6=E8=B7=AF=E5=BE=84=E6=A0=A1=E9=AA=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/ConfigSettings/ConfigForm/ConfigForm.js | 9 ++++++++- test/frontend/config-form.test.mjs | 6 ++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/frontend/src/views/components/ConfigSettings/ConfigForm/ConfigForm.js b/frontend/src/views/components/ConfigSettings/ConfigForm/ConfigForm.js index 98f3c42..13bc426 100644 --- a/frontend/src/views/components/ConfigSettings/ConfigForm/ConfigForm.js +++ b/frontend/src/views/components/ConfigSettings/ConfigForm/ConfigForm.js @@ -72,6 +72,10 @@ const certificateManagement = config => { } const projectManagedCertificate = config => certificateManagement(config) === 'project' const externalManagedCertificate = config => !projectManagedCertificate(config) +const absoluteCertificatePath = value => typeof value === 'string' + && value === value.trim() + && /^(?:[a-z]:[\\/]|\\\\|\/)/i.test(value) + && !/(^|[\\/])\.\.(?:[\\/]|$)/.test(value) const normalizedDomain = value => value.toLowerCase().replace(/\.$/, '') const validDomain = value => { if (typeof value !== 'string' || value !== value.trim()) return false @@ -368,7 +372,10 @@ export function validateFormConfig(section, config) { if (!controlled(tls[name])) errors[`tls.${name}`] = '请填写受管存储中的相对目录,不允许绝对路径或上级目录' } for (const name of ['certificateRef', 'privateKeyRef']) { - if (tls[name] && (!controlled(tls[name]) || !tls[name].startsWith(`${directory}/`))) errors[`tls.${name}`] = '文件引用必须位于当前证书目录内' + if (!tls[name]) continue + const managedReference = controlled(tls[name]) && tls[name].startsWith(`${directory}/`) + const externalReference = management === 'custom' && absoluteCertificatePath(tls[name]) + if (!managedReference && !externalReference) errors[`tls.${name}`] = '文件引用必须是受管存储引用或自定义服务器绝对路径' } if (!!tls.certificateRef !== !!tls.privateKeyRef) errors['tls.privateKeyRef'] = '证书与私钥引用必须成对填写' } diff --git a/test/frontend/config-form.test.mjs b/test/frontend/config-form.test.mjs index 480ed12..13f5352 100644 --- a/test/frontend/config-form.test.mjs +++ b/test/frontend/config-form.test.mjs @@ -319,6 +319,12 @@ test('证书管理方式只展示三种业务模式并同步底层托管配置', } assert.ok(validateFormConfig('ssl', { tls: { ...tls, privateKeyRef: '' } })['tls.privateKeyRef']) assert.ok(validateFormConfig('ssl', { tls: { ...tls, certificateRef: 'config/ssl/outside.pem' } })['tls.certificateRef']) + for (const certificateRef of ['C:\\certs\\site.crt', 'D:/certs/site.crt', '/etc/ssl/site.crt', '\\\\server\\share\\site.crt']) { + assert.deepEqual(validateFormConfig('ssl', { tls: { ...tls, certificateRef, privateKeyRef: certificateRef.replace(/(?:crt|pem)$/, 'key') } }), {}) + } + for (const certificateRef of ['C:\\certs\\..\\site.crt', '/etc/ssl/../site.crt', 'C:relative\\site.crt']) { + assert.ok(validateFormConfig('ssl', { tls: { ...tls, certificateRef } })['tls.certificateRef']) + } let updated = readFormConfig(updateFormConfig(JSON.stringify({ tls }), 'ssl', ['tls', 'management'], 'project')) assert.equal(updated.tls.mode, tls.mode) assert.equal(updated.tls.customDirectory, tls.customDirectory)