From 045e3b08b130afad4fb0c49cddd23581066158b3 Mon Sep 17 00:00:00 2001 From: jkooe <62494645+jkooe@users.noreply.github.com> Date: Mon, 5 Oct 2026 00:25:44 +0800 Subject: [PATCH] =?UTF-8?q?fix(sec):=20=E5=AE=89=E8=A3=85=E5=8C=85?= =?UTF-8?q?=E4=B8=8D=E5=86=8D=E6=89=93=E5=8C=85=20PSK.txt=EF=BC=88?= =?UTF-8?q?=E8=AF=A5=20zip=20=E4=BC=9A=E4=BD=9C=E4=B8=BA=E5=85=AC=E5=BC=80?= =?UTF-8?q?=20Release=20=E8=B5=84=E4=BA=A7=E5=8F=91=E5=B8=83=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 问题 `nodeagent-win-x64.zip` 里含 `PSK.txt`(64 位预共享密钥)。而这个 zip 正是 GitHub Release 的公开资产 —— 等于**把被控端的预共享密钥公开**。若谁用该 Release 包安装, 其密钥即为公开值,同网段的任何人都能凭此接入。 ## 修复 - `pack.mjs` 打 zip 时用 `-x "PSK.txt"` 排除(并注明原因,防日后被加回) - 不影响安装:`install.cmd` 在 PSK.txt 缺失时**本地自动生成**密钥(只留在被控端安装目录), 这正是它头注释里既有的行为 - 新增单测守卫:断言打 zip 命令必须带 `-x PSK.txt`(防止回归) ## 兼容性 PATCH 级:仅打包内容变化,不涉及协议/能力/参数。已安装的机器不受影响。 --- scripts/pack.mjs | 11 ++++++++--- tests/unit/ps1-encoding.test.mjs | 8 ++++++++ 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/scripts/pack.mjs b/scripts/pack.mjs index cc7ad97..9a4517e 100644 --- a/scripts/pack.mjs +++ b/scripts/pack.mjs @@ -11,8 +11,10 @@ * ├── control.cmd 控制台菜单(启动/停止/状态/日志/卸载) * ├── install.ps1 底层安装脚本(由 install.cmd 调用) * ├── control.ps1 底层控制脚本(由 control.cmd 调用) - * ├── uninstall.ps1 底层卸载脚本 - * └── PSK.txt 预共享密钥(首次运行时由 install.cmd 生成) + * └── uninstall.ps1 底层卸载脚本 + * + * 注意:PSK.txt **不打进 zip**(zip 会作为公开 Release 资产发布,含密钥=公开密钥); + * 它在被控端首次运行 install.cmd 时生成,只留在本机安装目录。 */ import { execFileSync, execSync } from 'node:child_process'; import { @@ -191,7 +193,10 @@ async function main() { log('\n=== ⑤ 打 zip ==='); const zipOut = join(OUT_DIR, 'nodeagent-win-x64.zip'); rmSync(zipOut, { force: true }); - execSync(`cd "${PKG_DIR}" && zip -q -r "${zipOut}" .`, { stdio: 'inherit' }); + // ⚠️ 必须排除 PSK.txt:本 zip 会作为**公开的 GitHub Release 资产**发布, + // 把预共享密钥打进包里等于公开密钥。install.cmd 在文件缺失时会自动生成新密钥 + // (生成在安装目录,只留在被控端本机),所以排除它不影响安装。 + execSync(`cd "${PKG_DIR}" && zip -q -r "${zipOut}" . -x "PSK.txt"`, { stdio: 'inherit' }); const size = execSync(`du -sh "${zipOut}"`, { encoding: 'utf8' }).split('\t')[0]; log(`\n✓ 完成: ${zipOut}(${size.trim()})`); diff --git a/tests/unit/ps1-encoding.test.mjs b/tests/unit/ps1-encoding.test.mjs index 65a5189..efe217d 100644 --- a/tests/unit/ps1-encoding.test.mjs +++ b/tests/unit/ps1-encoding.test.mjs @@ -210,3 +210,11 @@ test('install.cmd:管理员探测用 fltmc 而非 net session(不依赖 Serv 'install.cmd 不应再用 net session 探测(依赖 Server 服务,可能误判)', ); }); + +// ---------- 安全:公开的安装包不得含密钥 ---------- +test('pack.mjs 打 zip 必须排除 PSK.txt(zip 作为公开 Release 资产发布)', () => { + const pack = readFileSync(join(ROOT, 'scripts', 'pack.mjs'), 'utf8'); + const zipLine = pack.split('\n').find((l) => l.includes('zip -q -r')); + assert.ok(zipLine, '找不到打 zip 的命令行'); + assert.match(zipLine, /-x\s+"?PSK\.txt"?/, '必须 -x 排除 PSK.txt,否则预共享密钥随公开资产泄露'); +});