@lvxianchao/dsh-web-guard 2.1.0 → 2.1.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 +8 -0
- package/README.md +12 -1
- package/README.zh.md +12 -1
- package/lib/i18n.js +2 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 2.1.1 — 2026-11-05
|
|
4
|
+
|
|
5
|
+
### Docs
|
|
6
|
+
|
|
7
|
+
- README and the admin page now describe the persisted session cache and the dead-session redirect; both still claimed sessions lived in memory and that a restart signed everyone out.
|
|
8
|
+
- Screenshots re-shot with the corrected wording.
|
|
9
|
+
|
|
10
|
+
|
|
3
11
|
## 2.1.0 — 2026-11-05
|
|
4
12
|
|
|
5
13
|
### Added
|
package/README.md
CHANGED
|
@@ -173,10 +173,21 @@ node -e "import('./lib/auth.js').then(m=>console.log(m.hashPassword(process.argv
|
|
|
173
173
|
- Session tokens are stored server-side as SHA-256 digests; cookies are `HttpOnly`, `SameSite=Lax`, and `Secure` when TLS is on.
|
|
174
174
|
- Login POSTs are checked for a same-hostname `Origin`. The session cookie's `SameSite` attribute is the primary CSRF defence.
|
|
175
175
|
- Repeated failures from one IP trigger a temporary lock (in-memory, cleared on restart).
|
|
176
|
-
-
|
|
176
|
+
- **The session table is cached on disk** (`<profile>/data/dsh-web-guard-sessions-<port>.json`, mode `0600`, atomic tmp+rename), so **restarting DSH no longer signs signed-in browsers out**. The file holds SHA-256 digests, never tokens — neither the cache nor a heap dump can mint a working cookie. The filename carries the listener port, so a shadow instance cannot overwrite the live one.
|
|
177
|
+
- `/logout` ends the current browser session. To revoke everything (a suspected leak, say), use the admin page's **sign out all devices** — **a restart is not a revocation any more**.
|
|
177
178
|
|
|
178
179
|
**This is not multi-user.** One password, one privilege level. The harness agent can execute code, so anyone who gets in has code execution on that host — which is the product's design, not a bug in the gate.
|
|
179
180
|
|
|
181
|
+
## When the session is gone
|
|
182
|
+
|
|
183
|
+
If the session disappears (password changed, sign-out-all, or no activity within `sessionDays`), an open tab does not sit on "reconnecting" forever:
|
|
184
|
+
|
|
185
|
+
- it probes `/__guard/session` when the tab becomes visible, when the network comes back, after a WebSocket close, and every 30 seconds
|
|
186
|
+
- only an explicit **401** triggers a redirect — a failed fetch (offline, server restarting) never does
|
|
187
|
+
- the redirect carries `?next=`, so you land back where you were after signing in
|
|
188
|
+
|
|
189
|
+
Restarts (upgrades, config changes) are invisible: the session table is restored from disk and the page reconnects on its own.
|
|
190
|
+
|
|
180
191
|
## Admin page
|
|
181
192
|
|
|
182
193
|
A shield button appears at the bottom-right of the harness UI once you are signed in. It opens `/__guard/admin` (bookmark the URL if you prefer):
|
package/README.zh.md
CHANGED
|
@@ -167,10 +167,21 @@ node -e "import('./lib/auth.js').then(m=>console.log(m.hashPassword(process.argv
|
|
|
167
167
|
- 会话 token 服务端只存 SHA-256 摘要;cookie 是 `HttpOnly` + `SameSite=Lax`,启用 TLS 时加 `Secure`。
|
|
168
168
|
- 登录 POST 校验 `Origin` 的 hostname 与请求 `Host` 一致(只比 hostname,因为反代常剥端口)。CSRF 的第一道防线仍是 `SameSite`。
|
|
169
169
|
- 同一 IP 连续失败会临时锁定(内存计数,重启清零)。
|
|
170
|
-
-
|
|
170
|
+
- **会话表现在会落盘**(`<profile>/data/dsh-web-guard-sessions-<port>.json`,`0600`,临时文件 + rename 原子写入),所以**重启 DSH 不会把已登录的浏览器踢下线**。文件里存的同样是 SHA-256 摘要,不是 token——拿到这个文件或内存 dump 都无法伪造 cookie。文件名带监听端口,影子实例不会覆盖正式实例。
|
|
171
|
+
- `/logout` 只退当前浏览器。要全部吊销(比如怀疑泄露),用管理页的**退出所有设备**:**重启不再等于吊销**。
|
|
171
172
|
|
|
172
173
|
**这不是多用户系统**:一个密码,一个权限级别。DSH 的 agent 本身能执行代码,任何登录者都拥有该机器的执行权限——这是产品设计,不是闸门的缺陷。
|
|
173
174
|
|
|
175
|
+
## 掉线之后
|
|
176
|
+
|
|
177
|
+
会话没了(改过密码、点了退出所有设备、或超过 `sessionDays` 没活动)时,已经开着的标签页不会卡在"重新连接中":
|
|
178
|
+
|
|
179
|
+
- 切回标签页、网络恢复、WebSocket 断开、以及每 30 秒,页面都会向 `/__guard/session` 探一次
|
|
180
|
+
- 只有拿到明确的 **401** 才跳转(断网导致的请求失败不会误判)
|
|
181
|
+
- 跳转目标带 `?next=`,登录后回到原来那个地址
|
|
182
|
+
|
|
183
|
+
需要重启(升级、改配置)则完全无感:会话表会从磁盘恢复,页面自己重连回来。
|
|
184
|
+
|
|
174
185
|
## 管理页
|
|
175
186
|
|
|
176
187
|
登录后 DSH 界面右下角会出现一个盾牌按钮,点开就是 `/__guard/admin`(也可以直接把地址收藏起来):
|
package/lib/i18n.js
CHANGED
|
@@ -114,7 +114,7 @@ const ZH = {
|
|
|
114
114
|
signOutAll: '退出所有设备',
|
|
115
115
|
confirmSignOutAll: '将退出所有设备上的登录,包括当前这个。继续?',
|
|
116
116
|
secSessions: '活跃会话',
|
|
117
|
-
hintSessions: '
|
|
117
|
+
hintSessions: '每个浏览器一条记录。会话落盘保存,重启 DSH 不会把人踢下线;要全员下线请点「退出所有设备」。',
|
|
118
118
|
colSource: '来源',
|
|
119
119
|
colBrowser: '浏览器',
|
|
120
120
|
colLastSeen: '最后活动',
|
|
@@ -169,7 +169,7 @@ const EN = {
|
|
|
169
169
|
signOutAll: 'Sign out everywhere',
|
|
170
170
|
confirmSignOutAll: 'This signs out every device, including this one. Continue?',
|
|
171
171
|
secSessions: 'Active sessions',
|
|
172
|
-
hintSessions: 'One row per browser. Sessions
|
|
172
|
+
hintSessions: 'One row per browser. Sessions are persisted, so restarting DSH keeps everyone signed in — use "Sign out all devices" to revoke them.',
|
|
173
173
|
colSource: 'Source',
|
|
174
174
|
colBrowser: 'Browser',
|
|
175
175
|
colLastSeen: 'Last seen',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lvxianchao/dsh-web-guard",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.1",
|
|
4
4
|
"description": "Password login for the DeepSeek Harness Web UI, so it can be exposed on the internet: sign in from anywhere instead of chasing one-off tokens, with reverse-proxy quirks handled.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|