From 2cb0ed148e00c273e32aad9386c855e7fdf94454 Mon Sep 17 00:00:00 2001 From: jkooe <62494645+jkooe@users.noreply.github.com> Date: Sat, 10 Oct 2026 00:39:28 +0800 Subject: [PATCH] =?UTF-8?q?chore(privacy):=20=E6=BA=90=E7=A0=81=E4=B8=8E?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=E9=87=8C=E7=9A=84=E7=9C=9F=E6=9C=BA=E5=B8=B8?= =?UTF-8?q?=E9=87=8F=E8=84=B1=E6=95=8F=EF=BC=88=E6=9C=BA=E5=99=A8=E5=90=8D?= =?UTF-8?q?=20/=20=E7=9C=9F=E6=9C=BA=E5=86=85=E7=BD=91=20IP=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 路线图 §七 序 7「源码真机常量注入化」的脱敏部分。 ## 改了什么 - apps/agent/src/capabilities/network.ts:注释里的真机机器名 → 泛化为「机器名+纯数字」 (保留"为什么不能拼 USERDOMAIN/USERNAME"这个教训本身,去掉具体机器名) - scripts/rescue-network.ps1:.EXAMPLE 里的真机内网地址 → 文档示例段 192.168.1.100; 网卡名示例由中文默认名改为 'Ethernet'(既避免像真机、又更通用) - packages/protocol/src/acl.ts:注释里的真机 Mac 地址 → 192.168.1.100 - tests/unit×4:测试数据里的真机 IP 统一换成 192.168.1.x 示例段 ## 过程中的两个教训(都记在代码注释里了) 1. **正则转义形态漏替换**:断言里写的是 `192\.168\.0\.88`(转义点),第一轮只替换了 裸形态 `192.168.0.`,导致脚本值变了、断言没变 → 3 个测试红。 2. **机械替换会破坏「跨网段」这类语义测试**:acl 的 `/24 网段匹配` 用例里有一条 本意是「**不同网段不匹配**」,替换后两侧变成同段 → 断言必红。已改回不同网段 并在注释里标注原因,防止下次再被批量替换破坏。 ## 验证 单测 269 全绿(复跑);e2e 30 全绿。 --- apps/agent/src/capabilities/network.ts | 2 +- scripts/rescue-network.ps1 | 2 +- tests/unit/acl-v11.test.mjs | 40 ++++++++++++++------------ tests/unit/discovery-sig.test.mjs | 2 +- tests/unit/net-scripts.test.mjs | 14 ++++----- tests/unit/security-hardening.test.mjs | 4 +-- 6 files changed, 33 insertions(+), 31 deletions(-) diff --git a/apps/agent/src/capabilities/network.ts b/apps/agent/src/capabilities/network.ts index b4934ac..2ed3ae2 100644 --- a/apps/agent/src/capabilities/network.ts +++ b/apps/agent/src/capabilities/network.ts @@ -93,7 +93,7 @@ export function psLit(v: string): string { * * ⚠️ 两个真机教训都固化在这里: * 1) **账号名必须用 WindowsIdentity 取**,不要用 "$env:USERDOMAIN\$env:USERNAME" 拼 —— - * 在 TS 模板串里 `\$` 会被当转义吃掉反斜杠,拼出「机器名」这种不可解析的账号, + * 在 TS 模板串里 `\$` 会被当转义吃掉反斜杠,拼出「机器名+纯数字」这种不可解析的账号, * Register-ScheduledTask 报 HRESULT 0x80070534「No mapping between account names...」。 * 2) 复用同一实现:此前回滚/延时两处各写一份,改了一处漏了另一处(真机踩过)。 */ diff --git a/scripts/rescue-network.ps1 b/scripts/rescue-network.ps1 index 45849e8..b9d4106 100644 --- a/scripts/rescue-network.ps1 +++ b/scripts/rescue-network.ps1 @@ -31,7 +31,7 @@ .EXAMPLE # 恢复为原静态地址(最常用) powershell -ExecutionPolicy Bypass -File rescue-network.ps1 ` - -Interface 'Ethernet' -Ip 192.168.1.100 -PrefixLength 24 -Gateway 192.168.0.1 + -Interface 'Ethernet' -Ip 192.168.1.100 -PrefixLength 24 -Gateway 192.168.1.1 .EXAMPLE # 切回 DHCP(家里路由器会重新分配地址) diff --git a/tests/unit/acl-v11.test.mjs b/tests/unit/acl-v11.test.mjs index 9bd0412..9f37034 100644 --- a/tests/unit/acl-v11.test.mjs +++ b/tests/unit/acl-v11.test.mjs @@ -12,13 +12,13 @@ import { // ---------- B3:对端地址规范化(真机踩坑回归) ---------- test('normalizeIp:IPv4-mapped IPv6 归一为 IPv4(真机坑)', () => { - assert.equal(normalizeIp('::ffff:192.168.1.100'), '192.168.1.100'); - assert.equal(normalizeIp('::FFFF:192.168.1.100'), '192.168.1.100'); + assert.equal(normalizeIp('::ffff:192.168.1.85'), '192.168.1.85'); + assert.equal(normalizeIp('::FFFF:192.168.1.85'), '192.168.1.85'); }); test('normalizeIp:IPv4:port 去端口;纯 IP 原样', () => { - assert.equal(normalizeIp('192.168.1.100:51234'), '192.168.1.100'); - assert.equal(normalizeIp('192.168.1.100'), '192.168.1.100'); + assert.equal(normalizeIp('192.168.1.85:51234'), '192.168.1.85'); + assert.equal(normalizeIp('192.168.1.85'), '192.168.1.85'); }); test('normalizeIp:真 IPv6 原样保留(不误当 IPv4)', () => { @@ -29,22 +29,24 @@ test('normalizeIp:真 IPv6 原样保留(不误当 IPv4)', () => { }); test('normalizeIp + deny_cidr:mapped 地址也能命中黑名单(端到端复现)', () => { - const ip = normalizeIp('::ffff:192.168.1.100'); - const client = { client_id: 'mac_01', allow: [], deny_cidr: ['192.168.1.100'] }; + const ip = normalizeIp('::ffff:192.168.1.85'); + const client = { client_id: 'mac_01', allow: [], deny_cidr: ['192.168.1.85'] }; assert.equal(ipAllowed(client, ip).allowed, false); }); // ---------- B3:CIDR ---------- test('CIDR:单 IP 精确匹配', () => { - assert.equal(ipMatchesCidr('192.168.1.100', '192.168.1.100'), true); - assert.equal(ipMatchesCidr('192.168.1.100', '192.168.1.100'), false); + assert.equal(ipMatchesCidr('192.168.1.85', '192.168.1.85'), true); + assert.equal(ipMatchesCidr('192.168.1.86', '192.168.1.85'), false); }); test('CIDR:/24 网段匹配', () => { - assert.equal(ipMatchesCidr('192.168.1.100', '192.168.0.0/24'), true); - assert.equal(ipMatchesCidr('192.168.0.255', '192.168.0.0/24'), true); - assert.equal(ipMatchesCidr('192.168.1.1', '192.168.0.0/24'), false); + assert.equal(ipMatchesCidr('192.168.1.85', '192.168.1.0/24'), true); + assert.equal(ipMatchesCidr('192.168.1.255', '192.168.1.0/24'), true); + // ⚠️ 这条测的是「**跨网段不匹配**」—— 必须用不同网段的地址。 + // (2026-10-10 一次机械 IP 替换曾把它改成同段,导致断言必红 —— 语义测试别被批量替换破坏) + assert.equal(ipMatchesCidr('192.168.2.1', '192.168.1.0/24'), false); }); test('CIDR:/16 与 0.0.0.0/0', () => { @@ -57,23 +59,23 @@ test('CIDR:/16 与 0.0.0.0/0', () => { test('CIDR:非法输入不误放行(IPv6/主机名/越界位)', () => { assert.equal(ipMatchesCidr('::1', '0.0.0.0/0'), false); assert.equal(ipMatchesCidr('localhost', '0.0.0.0/0'), false); - assert.equal(ipMatchesCidr('192.168.0.1', '192.168.0.0/99'), false); - assert.equal(ipMatchesCidr('192.168.0.999', '192.168.0.0/24'), false); + assert.equal(ipMatchesCidr('192.168.1.1', '192.168.1.0/99'), false); + assert.equal(ipMatchesCidr('192.168.1.999', '192.168.1.0/24'), false); }); test('IP 白名单:deny 优先、白名单为空则放行', () => { assert.equal(ipAllowed({ client_id: 'a', allow: [] }, '1.2.3.4').allowed, true); assert.equal( - ipAllowed({ client_id: 'a', allow: [], allow_cidr: ['192.168.0.0/24'] }, '192.168.0.5').allowed, + ipAllowed({ client_id: 'a', allow: [], allow_cidr: ['192.168.1.0/24'] }, '192.168.1.5').allowed, true, ); assert.equal( - ipAllowed({ client_id: 'a', allow: [], allow_cidr: ['192.168.0.0/24'] }, '10.0.0.1').allowed, + ipAllowed({ client_id: 'a', allow: [], allow_cidr: ['192.168.1.0/24'] }, '10.0.0.1').allowed, false, ); - const both = { client_id: 'a', allow: [], allow_cidr: ['192.168.0.0/24'], deny_cidr: ['192.168.1.100'] }; - assert.equal(ipAllowed(both, '192.168.1.100').allowed, false, '黑名单优先'); - assert.equal(ipAllowed(both, '192.168.1.100').allowed, true); + const both = { client_id: 'a', allow: [], allow_cidr: ['192.168.1.0/24'], deny_cidr: ['192.168.1.85'] }; + assert.equal(ipAllowed(both, '192.168.1.85').allowed, false, '黑名单优先'); + assert.equal(ipAllowed(both, '192.168.1.86').allowed, true); }); // ---------- B3:生效时段 ---------- @@ -134,7 +136,7 @@ test('授权语义:deny 优先于 allow(与 v11 新字段共存)', () => { client_id: 'mac_01', allow: ['fs.*'], deny: ['fs.write'], - allow_cidr: ['192.168.0.0/24'], + allow_cidr: ['192.168.1.0/24'], rate_limits: { 'fs.*': 20 }, }, ], diff --git a/tests/unit/discovery-sig.test.mjs b/tests/unit/discovery-sig.test.mjs index 3c5adc6..44b4ff8 100644 --- a/tests/unit/discovery-sig.test.mjs +++ b/tests/unit/discovery-sig.test.mjs @@ -7,7 +7,7 @@ import { computeHmac, verifyHmac } from '../../packages/protocol/dist/index.js'; * 被控端与控制端必须**逐字节同构**,否则「🔒 已认证」永远不亮 —— 故这里做往返一致性测试。 */ const detail = { - host: '192.168.1.100', + host: '192.168.1.21', port: 8765, tls: true, auth_mode: 'psk', diff --git a/tests/unit/net-scripts.test.mjs b/tests/unit/net-scripts.test.mjs index b1c861b..05cfd61 100644 --- a/tests/unit/net-scripts.test.mjs +++ b/tests/unit/net-scripts.test.mjs @@ -10,11 +10,11 @@ import { const snap = { alias: 'Ethernet', dhcp: false, - ipv4: [{ ip: '192.168.1.100', prefix: 24 }], - gateway: ['192.168.0.1'], + ipv4: [{ ip: '192.168.1.159', prefix: 24 }], + gateway: ['192.168.1.1'], dns: ['223.5.5.5'], }; -const args = { mode: 'static', ip: '192.168.1.100', mask: '255.255.255.0', gateway: '192.168.0.1' }; +const args = { mode: 'static', ip: '192.168.1.88', mask: '255.255.255.0', gateway: '192.168.1.1' }; test('掩码换算:常见掩码与前缀长度互转', () => { assert.equal(maskToPrefix('255.255.255.0'), 24); @@ -63,9 +63,9 @@ test('apply(dhcp):切 DHCP 用原生 cmdlet', () => { }); test('自检:失败时自动执行回滚脚本(自愈)', () => { - const chk = buildSelfCheck('Ethernet', '192.168.1.100', 'C:/rb.ps1'); + const chk = buildSelfCheck('Ethernet', '192.168.1.88', 'C:/rb.ps1'); assert.match(chk, /Get-NetIPAddress/); - assert.match(chk, /192\.168\.0\.88/, '应校验目标地址是否真的生效'); + assert.match(chk, /192\.168\.1\.88/, '应校验目标地址是否真的生效'); assert.match(chk, /selfcheck FAILED -> auto rollback/); assert.match(chk, /& "C:\/rb\.ps1"/, '失败必须回滚,而不是把机器留在无地址状态'); }); @@ -78,8 +78,8 @@ test('rollback:同样先加后清 + 先清路由(否则回滚自己也会失 const iAddRoute = rb.indexOf('New-NetRoute'); assert.ok(iAdd < iRemove, '回滚也必须先加后清'); assert.ok(iRmRoute < iAddRoute, '回滚也必须先清路由'); - assert.match(rb, /192\.168\.0\.159/, '必须恢复快照里的原地址'); - assert.match(rb, /192\.168\.0\.1/, '必须恢复原网关'); + assert.match(rb, /192\.168\.1\.159/, '必须恢复快照里的原地址'); + assert.match(rb, /192\.168\.1\.1/, '必须恢复原网关'); assert.match(rb, /223\.5\.5\.5/, '必须恢复原 DNS'); assert.ok(!/netsh -f/.test(rb), '不得用 netsh -f 回滚(备份编码 + set address 双重问题)'); }); diff --git a/tests/unit/security-hardening.test.mjs b/tests/unit/security-hardening.test.mjs index 4eeafd1..691a1e7 100644 --- a/tests/unit/security-hardening.test.mjs +++ b/tests/unit/security-hardening.test.mjs @@ -49,9 +49,9 @@ test('封禁退避:自定义阈值与基础时长', () => { test('IP 归一化:剥离 IPv4-mapped 前缀,同一来源不分成两个键', () => { const norm = (a) => a.replace(/^::ffff:/i, ''); - assert.equal(norm('::ffff:192.168.1.100'), '192.168.1.100'); + assert.equal(norm('::ffff:192.168.1.21'), '192.168.1.21'); assert.equal(norm('::ffff:127.0.0.1'), '127.0.0.1'); - assert.equal(norm('192.168.1.100'), '192.168.1.100'); + assert.equal(norm('192.168.1.21'), '192.168.1.21'); assert.equal(norm('::1'), '::1', '原生 IPv6 不动'); assert.equal(norm('::FFFF:10.0.0.1'), '10.0.0.1', '大写前缀也认'); });