dsh-remote-web 0.7.5 → 0.7.6-beta.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-remote-web",
3
- "version": "0.7.5",
3
+ "version": "0.7.6-beta.2",
4
4
  "description": "公网远程控制 DeepSeek Harness(dsh web):安装即得专属加密地址,人在外面也能用手机访问电脑上的 dsh——无需同一局域网/WiFi、无需公网 IP 与内网穿透,全程加密;手机端 100% 还原电脑体验(对话/工具/审批/设置)。技术用户可选自建服务,流量走自己的服务器。Remote control DeepSeek Harness (dsh web) from anywhere over the public internet — install, get an encrypted URL, use it from your phone on any network (dual-half cordis plugin: Settings panel + same-origin /dsh-remote routes). (2026-09 由 dsh-remote-ui 更名 / renamed from dsh-remote-ui; dsh-remote 的 dsh web 插件半,不是纯 UI/皮肤插件)",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
@@ -2302,7 +2302,7 @@ async function initE2ee(token) {
2302
2302
  const storeEnabled = process.env.DSH_BRIDGE_E2EE_SESSIONS !== "0";
2303
2303
  const store = e2ee.attachStore(storeDir, { enabled: storeEnabled });
2304
2304
  if (store.restored > 0) {
2305
- console.log(`[bridge] 🔓 已恢复 ${store.restored} 个 E2EE 会话(加密落盘;手机重启/桥重启后免重新解锁)`);
2305
+ console.log(`[bridge] 🔓 已恢复 ${store.restored} 个 E2EE 会话(加密落盘;安全策略=v2:全部标记「需重新握手」,不复用旧计数)`);
2306
2306
  }
2307
2307
  if (store.discarded) {
2308
2308
  // 丢弃 = 手机手里的旧票必然对不上:后续 ws-open/HTTP 会带 retryable 的 rehandshake 信号,
@@ -792,14 +792,17 @@ export class E2eeService {
792
792
  profile: String(row.profile || this.profile),
793
793
  epoch: Number(row.epoch) || 0
794
794
  });
795
- session.verified = row.verified === true;
795
+ // ★ 2026-10-24 安全升级:从磁盘恢复的会话**一律标记未验证**,必须重新握手后才接受数据流。
796
+ // 原因:落盘的收发计数可能落后于手机侧(桥在 flush 与退出之间重启过),直接复用旧计数
797
+ // 会导致 nonce 重放/解不开 → 生产「无法解密」。宁可让手机自动重握手一次,也不冒错帧风险。
798
+ session.verified = false;
799
+ session.restoredNeedsHandshake = true;
796
800
  session.createdAt = Number(row.createdAt) || usedAt;
797
801
  session.usedAt = usedAt;
798
802
  this.sessions.set(sessId, session);
799
- const guards = { p2b: new SequenceGuard(), b2p: new SequenceGuard() };
800
- if (Number.isInteger(row.p2b)) guards.p2b.last = row.p2b;
801
- if (Number.isInteger(row.b2p)) guards.b2p.last = row.b2p;
802
- this.ctrlCounters.set(sessId, guards);
803
+ // ★ 计数同样**不恢复**:恢复的会话未验证,重新握手会从 0 重新计数;
804
+ // 继承旧计数只会在下一次探测时误判「nonce 重放」。
805
+ this.ctrlCounters.set(sessId, { p2b: new SequenceGuard(), b2p: new SequenceGuard() });
803
806
  } catch { /* 单条坏 → 跳过,不影响其它会话 */ }
804
807
  }
805
808
  this.prune();
@@ -932,7 +935,9 @@ export class E2eeService {
932
935
  // 会话持久化原语(2026-10-23;方案/信任边界见 E2eeService.attachStore 上方长注释)
933
936
  // ============================================================
934
937
 
935
- export const SESSION_STORE_VERSION = 1;
938
+ // 2026-10-24 → 2:0.7.5 的 v1 库恢复后直接复用旧计数,生产出现「nonce 重放/无法解密」。
939
+ // 升级到 v2 时**旧库一律作废**(restore 里版本不符会 clearSessionStore),手机重新握手一次。
940
+ export const SESSION_STORE_VERSION = 2;
936
941
  export const SESSION_STORE_FILE = ".e2ee-sessions.enc";
937
942
  /** 会话库 AAD(域分隔;换格式/换用途必须改它,旧文件自动解不开 → 丢弃重握手)。 */
938
943
  export const SESSION_STORE_AAD = Buffer.from(`${E2EE_DOMAIN}\x00sessions-store-v1`, "utf8");