dsh-crossterm 0.8.0 → 0.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +14 -0
- package/README.md +17 -16
- package/lib/bus.js +1 -1
- package/lib/cloud/home.js +4 -1
- package/lib/cloud/layout.js +3 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# dsh-crossterm 变更记录
|
|
2
2
|
|
|
3
|
+
## 0.8.1 — 2026-10-19
|
|
4
|
+
|
|
5
|
+
### 从旧版升到 0.8.0 时,签名密钥可能一直生成不出来
|
|
6
|
+
|
|
7
|
+
- 症状(发版后复查发现):0.7.6 及更早的布局缓存 `layout.json` 里没有 `secret` 字段,
|
|
8
|
+
`ensureLayout()` 命中缓存就直接返回,`relaySecret` 一直没写进 `_account.json`;
|
|
9
|
+
中转站 `secretNow()` 拿到空串,按「账号还没有密钥」处理,于是**未签名信封照样被放行** —— 验签等于没开。
|
|
10
|
+
缓存 10 分钟过期后会自愈,但这段窗口不该存在。
|
|
11
|
+
- 改法:`lib/cloud/layout.js` 的缓存捷径加上 `&& cached.secret`,没有密钥就走完整流程(生成并写回 `_account.json`);
|
|
12
|
+
`lib/cloud/home.js` 的 `getSecret()` 拿不到密钥时再强制重算一次(`getLayout(true)`)。
|
|
13
|
+
- 探针:`probe-security-080.mjs` 21 项仍全 PASS;`probe-cloud-home.mjs` 42 项全 PASS。
|
|
14
|
+
- 要不要跟着动手:**不用** —— 装上即可,只影响「刚从旧版升上来且已有布局缓存」的那一小段时间。
|
|
15
|
+
|
|
16
|
+
|
|
3
17
|
## 0.8.0 — 2026-10-19
|
|
4
18
|
|
|
5
19
|
### 安全加固:堵住路径穿越、接口裸奔、伪造发件人(来源:YOCE_C 复核报告)
|
package/README.md
CHANGED
|
@@ -28,24 +28,25 @@ A 终端看不到 B 终端里有哪个会话、在干什么,也没法把一条
|
|
|
28
28
|
> `locatePkgJson()` 对子路径行返回 undefined),而插件页顶部标题与这一行共用同一份 `locale/zh.json` 的 meta,
|
|
29
29
|
> 所以它单独做总开关,标题带「(总开关)」;卡片头用 `shortTitle` 显示短名「跨端互通」。
|
|
30
30
|
|
|
31
|
-
**本版更新(0.
|
|
31
|
+
**本版更新(0.8.1)**
|
|
32
32
|
|
|
33
|
-
-
|
|
34
|
-
|
|
35
|
-
|
|
33
|
+
- **补一个升级边角**:从旧版升上来时布局缓存里没有签名密钥,原来会一直不生成(验签暂时形同虚设);
|
|
34
|
+
现在检测到没有密钥就重算一遍并写进 `_account.json`。装上即可,无需额外操作。
|
|
35
|
+
|
|
36
|
+
**上一版(0.8.0)— 安全加固(来源:辅助机复核报告)**
|
|
37
|
+
|
|
38
|
+
- **附件名只认纯文件名**:以前信封里的附件名带 `..\..\` 能写到收件箱外面,现在一律取 basename 并校验最终路径仍在目标目录内。
|
|
39
|
+
- **本地接口要令牌**:写操作(更新/备份/检查更新/云同步开关/配置云盘)要带 `x-crossterm-token`(`<busDir>\http-token`,0600),
|
|
40
|
+
带外站 `Origin` 的请求 403 —— 防网页 CSRF。卡片自动取令牌,不用手动配。
|
|
41
|
+
- **云端信封带 HMAC-SHA256 签名**:密钥 `relaySecret` 自动生成在 `_account.json`,收信侧验签,签名缺失或不符就留件不收。
|
|
42
|
+
- **入站限额**:信封 >512 KB 跳过、附件最多 20 个、单附件 >64 MB 拒收;信封自带的云端路径不再采信。
|
|
43
|
+
- **投递顺序修正**:按信封 id 去重、先归档 `done/` 成功再记已处理。
|
|
44
|
+
- **要跟着动手**:账号生成 `relaySecret` 后,<0.8.0 设备发的未签名信封会被拒收;请把所有设备升到 ≥0.8.0。
|
|
45
|
+
|
|
46
|
+
**更早(0.7.6)**
|
|
47
|
+
|
|
48
|
+
- 跨设备邮件末尾不再重复给两条回复提示,只留一条。
|
|
36
49
|
- 邮件格式契约补第 4 条硬规则:**尾行只给一次**。
|
|
37
|
-
- 要不要跟着动手:**不用** —— 只删一句重复文案,协议与数据格式没变。
|
|
38
|
-
|
|
39
|
-
**上一版(0.7.5)**
|
|
40
|
-
|
|
41
|
-
- **邮件来源分四档**:同终端同工作区 → 「跨会话邮件」;同终端不同工作区 → **「跨工作区邮件」**;
|
|
42
|
-
同一台机器别的 DSH 进程 → 「跨终端邮件」;云端中转站投进来的(别的设备)→ **「跨设备邮件」**。
|
|
43
|
-
以前只有两档,云端来的信被标成「跨终端」。
|
|
44
|
-
- **抬头那行改成「发件人:」**:以前写「会话:xxx」,容易被读成「收件会话」;现在明确是发信的那个会话,
|
|
45
|
-
云端来的信还多一行「经由:云端中转站」。`term_inbox` 清单同步改成「发件人:」。
|
|
46
|
-
- **云端邮件与本机邮件同一套渲染**:中转站投递不再手写抬头,正文照样落盘 `mail/`、附件清单照样列、结尾照样给回信提示。
|
|
47
|
-
- **邮件格式写进说明**:新增「三、收信长什么样:邮件格式(固定契约)」一节,列清字段顺序、四档标签判定与四个例子。
|
|
48
|
-
- 要不要跟着动手:**不用** —— 纯展示层,协议与数据格式没变。
|
|
49
50
|
|
|
50
51
|
## 一、安装与卸载
|
|
51
52
|
|
package/lib/bus.js
CHANGED
package/lib/cloud/home.js
CHANGED
|
@@ -44,7 +44,10 @@ export function createCloudHome({ cfg, device, deviceInfo, pan, busDir, deliver,
|
|
|
44
44
|
getLayout: () => getLayout(false),
|
|
45
45
|
getSecret: async () => {
|
|
46
46
|
const l = await getLayout(false)
|
|
47
|
-
|
|
47
|
+
if (l && l.secret) return l.secret
|
|
48
|
+
// 布局是旧缓存(没有 relaySecret)时强制重算一次,把账号密钥补出来
|
|
49
|
+
const fresh = await getLayout(true)
|
|
50
|
+
return (fresh && fresh.secret) || ''
|
|
48
51
|
},
|
|
49
52
|
localDir: relayDir,
|
|
50
53
|
stateFile: relayState,
|
package/lib/cloud/layout.js
CHANGED
|
@@ -268,7 +268,9 @@ export async function ensureRules(pan, paths, info = {}) {
|
|
|
268
268
|
export async function ensureLayout({ pan, cfg = {}, device = {}, cacheFile, force = false, log = () => {} }) {
|
|
269
269
|
const nodeId = sanitize(device.nodeId || 'device')
|
|
270
270
|
const cached = readCache(cacheFile)
|
|
271
|
-
|
|
271
|
+
// 缓存里没有 secret(0.7.6 之前的缓存、或刚升级上来)时不能走捷径:
|
|
272
|
+
// 走一遍完整流程才能生成 relaySecret,否则验签会以为「账号还没有密钥」而放过未签名信封。
|
|
273
|
+
if (!force && cached && cached.secret && cached.deviceId === nodeId && Date.now() - Number(cached.at || 0) < CACHE_MS && cached.paths) {
|
|
272
274
|
// 路径是「算出来的」而不是状态:升级后新增的路径(规则文档、收件箱说明)必须重新算,
|
|
273
275
|
// 直接沿用旧缓存的 paths 会让 ensureRules 拿到 undefined、规则永远写不出去。
|
|
274
276
|
const paths = buildPaths({
|