fix: address review report across security, architecture, quality, tests
Implements the fixes from docs/REVIEW_REPORT.md (4-agent parallel review). typecheck clean; 341 tests pass (16 files, +113); build:web ok; coverage thresholds (80%) enforced in vitest.config.ts. Critical: - multi-device approval race: release held approval only when the last client detaches (closing one mirror no longer cancels another's prompt) - unbounded session creation (DoS): Config.maxSessions cap (env MAX_SESSIONS), enforced in manager via the existing M4 exit(-1) path - signal-handler leak: named SIGINT/SIGTERM/uncaughtException refs removed in close() - terminal-session initialInput timer tracked + cleared on dispose - tabs.addEntry null-as-cast type hole removed (build session before entry) Should-fix: - security-headers middleware + Origin/CSRF guard on DELETE /live-sessions[/:id] - history.ts converted to fs/promises (async /sessions handler) - removed dead clientDims map + blur protocol message end-to-end - per-connection WS message rate limit (Config.maxMsgsPerSec) - /sessions behavior kept; documented as accepted LAN risk (TECH_DOC §7) Tests: - new tmux / preview-grid / terminal-session (jsdom) / tabs (jsdom) suites - extended history/config/manager/integration coverage incl. regressions Hygiene: - parsePositiveInt -> parseNonNegativeInt; ALLOWED_ORIGINS scheme validation - log-injection sanitize; isLoopback handles 127.0.0.0/8 + IPv4-mapped - operational constants moved into Config - extracted public/preview-grid.ts (DRY launcher/manage) - doc sweeps: ARCHITECTURE §8 runtime-handle exception, stale comments
This commit is contained in:
@@ -421,11 +421,11 @@ client → {resize, cols, rows} // 独立消息,不混进 input
|
||||
1. 服务端不解析 ANSI/终端语义,PTY 字节是不透明 blob(但 RingBuffer 淘汰须按 chunk/码点边界,不可任意字节切割,M2)。
|
||||
2. WS 断开 **永不** kill PTY —— 只 detach;`ws.send` 失败也只 detach 该 ws,不动 PTY(M5)。
|
||||
3. `parseClientMessage` 永不抛异常,非法输入走 `ParseResult.ok=false`。
|
||||
4. 会话 meta 不可变;运行时句柄(ws/detachedAt/lastOutputAt/exitedAt)单独可变。
|
||||
5. 一个会话同一时刻最多一个附着 ws;转发前先重置 `attachedWs` 指针再 close 旧 ws。
|
||||
4. 会话 meta(`SessionMeta`:id/createdAt/shellPath)全 `readonly` 不可变;**运行时句柄字段是明确的例外,允许就地变更**——`clients`(Set)、`detachedAt`、`lastOutputAt`、`exitedAt`/`exitCode`、`claudeStatus`、`cwd` 是每会话的可变运行时状态(H6)。这是对全局"绝不 mutate"风格的**有意例外**:不可变快照与可变句柄被刻意分离持有,在每次按键时复制一个 Set 是纯粹的浪费。新增运行时状态时,把它放在 `Session`(可变区)而非 `SessionMeta`。
|
||||
5. (v0.4 放宽)一个会话可有**多个**并发附着 ws(多设备镜像共享):新 attach **JOIN(不踢)**;output/exit/status 广播给所有 client;任意 client 可输入(共享控制);PTY 尺寸 **latest-writer-wins**(最近 fit/focus 的设备驱动)。原"同一时刻最多一个 ws"已不再成立。
|
||||
6. 任何 `ws.send` 前必须 `readyState === OPEN`(M5)。
|
||||
7. Origin 校验在 upgrade 阶段完成(`noServer` + `handleUpgrade`),失败 401,不进入 WS 逻辑;路径限 `/term`(L3)。
|
||||
8. 无硬编码:所有可变参数走 `config.ts`(端口、shell、TTL、scrollback、maxPayload、WS 路径、allowedOrigins)。
|
||||
7. Origin 校验在 upgrade 阶段完成(`noServer` + `handleUpgrade`),失败 401,不进入 WS 逻辑;路径限 `/term`(L3)。**状态变更的 HTTP 路由(`DELETE /live-sessions[/:id]`)也复用同一 Origin 白名单做 CSRF 守卫(异源/缺失 → 403)。**
|
||||
8. 无硬编码:所有可变参数走 `config.ts`(端口、shell、TTL、scrollback、maxPayload、WS 路径、allowedOrigins、maxSessions、maxMsgsPerSec、permTimeoutMs、reapIntervalMs、previewBytes)。
|
||||
9. `input.data` 原样透传,不做内容过滤。
|
||||
10. PTY 退出后 `writeInput`/`resize` 静默忽略;exitedAt 会话保留至被 attach 回放或 reapIdle 回收(L1/L4)。
|
||||
11. 服务端不假设前端已防抖:`resize` 在服务端做幂等(值未变则跳过);WS 帧受 `maxPayload` 上限约束(L5)。
|
||||
|
||||
Reference in New Issue
Block a user