diff --git a/scripts/control.ps1 b/scripts/control.ps1 index 68eb408..313ef7b 100644 --- a/scripts/control.ps1 +++ b/scripts/control.ps1 @@ -97,7 +97,10 @@ function Show-Status { $inp = if ($cfg.allow_input) { "已启用" } else { "已禁用" } Write-Host " 输入控制 : $inp" $scheme = if ($cfg.tls) { "wss" } else { "ws" } - Write-Host " 协议 : $scheme://0.0.0.0:$Port" + Write-Host " 协议 : ${scheme}://0.0.0.0:$Port" + # ⚠️ 变量名必须用 ${ } 包起来:裸写「变量名后紧跟冒号」会被 PowerShell 当成 + # drive 引用(如 env:),属**解析期**错误 → 整个脚本都跑不起来, + # stop/start/status 三个子命令会全部失效(2026-10-11 真机踩到) } catch { Write-Warn "配置文件解析失败: $cfgPath" } diff --git a/tests/unit/security-hardening.test.mjs b/tests/unit/security-hardening.test.mjs index 8e07fc0..cb6128b 100644 --- a/tests/unit/security-hardening.test.mjs +++ b/tests/unit/security-hardening.test.mjs @@ -1,6 +1,7 @@ import { test } from 'node:test'; import assert from 'node:assert/strict'; import { readFileSync } from 'node:fs'; +import { execFileSync } from 'node:child_process'; /** * v2.0.0 连接层防护的**规则**测试(server.ts 的纯逻辑部分)。 @@ -104,3 +105,29 @@ test('自重启任务清理:清理动作带 -ErrorAction SilentlyContinue( '清理动作需容错,否则个别任务异常会中断整个重启流程', ); }); + +// ---------- PowerShell 语法类守卫($ident: 危险变量引用) ---------- + +test('PowerShell 脚本不得含 `$ident:` 形式(会被当 drive 引用 → 整个脚本解析失败)', () => { + // 2026-10-11 真机踩到:control.ps1 里写了 "…$scheme://0.0.0.0:$Port", + // PowerShell 把 `$scheme:` 当 drive 引用 → 报 "变量引用无效", + // 且因为是**解析期**错误,stop/start/status 三个子命令**全部不可用**。 + // 修法是写成 ${scheme}。这条守卫防同类再犯。 + const files = execFileSync('git', ['ls-files'], { encoding: 'utf8' }) + .split('\n') + .filter((f) => f.endsWith('.ps1') || f.endsWith('.psm1')); + assert.ok(files.length >= 3, '应找到若干 .ps1(install/control/rescue 等)'); + + const ALLOW = new Set(['env', 'nuget', 'psitem', 'PSScriptRoot']); + const BAD = /\$([A-Za-z_][A-Za-z0-9_]*):/g; + const offenders = []; + for (const f of files) { + const text = readFileSync(new URL(`../../${f}`, import.meta.url), 'utf8'); + text.split('\n').forEach((line, i) => { + for (const m of line.matchAll(BAD)) { + if (!ALLOW.has(m[1])) offenders.push(`${f}:${i + 1} $${m[1]}:`); + } + }); + } + assert.deepEqual(offenders, [], `发现危险变量引用(改用 \${name}):\n${offenders.join('\n')}`); +});