## 为什么不是直接改 auth_mode 默认值
ed25519 的**私钥必须在控制端**,被控端安装时拿不到公钥。硬改默认值会让新装的控制端
连不上(还得手工改配置回退)—— 比「有条件就走零信任 + 无条件时明确告警」更糟。
所以这里的"默认"是**优先顺序**,不是死值。
## 做法
install.ps1 新增 -ClientAclFile(默认探测脚本同目录 client-acl.json,内容即
`nodeagent keygen` 打印的那一条 acl.clients JSON):
- 有且含 client_id+pubkey → auth_mode=ed25519,写 acl{default_effect=deny, clients=[…]},
绿色确认 + 打印 --auth-mode ed25519 连接示例
- 无 / 缺字段 → 保持 psk,但输出**醒目黄色告警**:说明 client_id 可自报的固有风险
+ 三步切换指引(keygen → 存 client-acl.json → 重跑 install.cmd)
install.cmd 无需改动(install.ps1 自行探测)。
## 验证(真机,不是推理)
- **PowerShell 5.1 语法校验**:PARSE_OK,BOM 保留(EF BB BF)
- **等效逻辑三 CASE**:
A 有文件 → auth_mode=ed25519 + default_effect=deny + clients 完整(allow/deny/note 保留)
B 无文件 → auth_mode=psk 且**不写 acl 字段**
C 缺 pubkey → WARN 并回退 psk(不写半截 ACL)
- 编码守卫:ps1-encoding 24 例全过(BOM + 无 U+FFFD)
- 新增 tests/unit/install-zerotrust.test.mjs(8 例源码断言:参数、探测路径、
useZeroTrust 条件化、deny 兜底、字段校验、告警文案、连接示例、ConvertTo-Json -Depth 6)
## 文档
docs/SECURITY.md 新增 §4.7 并更新 §5 清单 —— 把 v23 的三项(握手封禁/会话上限/
安装期密钥)与本次 ed25519 默认化一并标为已完成,只留「审计链外部锚定」待做。
## 数据
单测 250 → **258** 全绿;7 包构建通过。
58 lines
2.8 KiB
JavaScript
58 lines
2.8 KiB
JavaScript
import { test } from 'node:test';
|
||
import assert from 'node:assert/strict';
|
||
import { readFileSync } from 'node:fs';
|
||
import { join } from 'node:path';
|
||
import { fileURLToPath } from 'node:url';
|
||
|
||
/**
|
||
* 零信任优先安装(v24)的**源码断言**。
|
||
*
|
||
* 为什么用源码断言:install.ps1 是 5.1 脚本,Node 侧无法直接执行它;
|
||
* 而这里要守的是「关键决策逻辑不许被误删」—— 一旦有人重构掉 acl 写入或
|
||
* 告警文案,测试立刻红。运行时的正确性已在真机用等效片段验证过
|
||
* (CASE A ed25519+deny / CASE B psk 无 acl / CASE C 缺字段回退 psk)。
|
||
*/
|
||
|
||
const ROOT = fileURLToPath(new URL('../../', import.meta.url));
|
||
const ps1 = readFileSync(join(ROOT, 'scripts', 'install.ps1'), 'utf8');
|
||
|
||
test('install.ps1 有 ClientAclFile 参数(零信任入口)', () => {
|
||
assert.match(ps1, /\[string\]\$ClientAclFile/, '缺少 -ClientAclFile 参数');
|
||
});
|
||
|
||
test('install.ps1 默认探测脚本同目录的 client-acl.json', () => {
|
||
assert.match(ps1, /client-acl\.json/, '缺少默认探测路径');
|
||
assert.match(ps1, /\$scriptDir/, '应用 $scriptDir 兜底(-Command 调用时 $PSScriptRoot 为空)');
|
||
});
|
||
|
||
test('install.ps1 有 useZeroTrust 判定且 auth_mode 条件化', () => {
|
||
assert.match(ps1, /\$useZeroTrust\s*=/, '缺少 useZeroTrust 判定');
|
||
assert.match(ps1, /auth_mode\s*=\s*if \(\$useZeroTrust\)/, 'auth_mode 必须按零信任开关条件化');
|
||
});
|
||
|
||
test('零信任分支写入 acl 且 default_effect=deny(兜底拒绝)', () => {
|
||
assert.match(ps1, /default_effect\s*=\s*"deny"/, 'default_effect 必须是 deny(未登记一律拒)');
|
||
assert.match(ps1, /clients\s*=\s*@\(\$aclEntry\)/, '缺少 clients 数组写入');
|
||
});
|
||
|
||
test('缺 client_id/pubkey 时忽略并回退(不写半截 ACL)', () => {
|
||
assert.match(ps1, /-not \$aclEntry\.client_id -or -not \$aclEntry\.pubkey/, '缺少字段校验');
|
||
assert.match(ps1, /\$aclEntry = \$null/, '校验失败必须置空以回退');
|
||
});
|
||
|
||
test('psk 路径必须给出醒目安全告警(不能静默降级)', () => {
|
||
assert.match(ps1, /SECURITY WARNING/, 'psk 分支缺少醒目告警');
|
||
assert.match(ps1, /SELF-REPORTED/, '告警须说明 client_id 可自报这一固有问题');
|
||
assert.match(ps1, /nodeagent keygen/, '告警须给出切换命令');
|
||
assert.match(ps1, /Yellow/, '告警须用醒目颜色(Yellow)');
|
||
});
|
||
|
||
test('零信任路径给出正确的连接指引(--id + --auth-mode)', () => {
|
||
assert.match(ps1, /--auth-mode ed25519/, '缺少 ed25519 连接示例');
|
||
assert.match(ps1, /Zero-trust is ON/, '缺少启用确认输出');
|
||
});
|
||
|
||
test('安装写入的 JSON 用 -Depth 6(acl 嵌套不会被截断)', () => {
|
||
assert.match(ps1, /ConvertTo-Json -Depth 6/, 'acl 是嵌套结构,Depth 不足会被截成字符串');
|
||
});
|