deepseek-harness-intelligent-ui 0.2.9 → 0.2.10
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 +13 -0
- package/README.md +5 -3
- package/README.zh-CN.md +5 -3
- package/client/client.js +42 -37
- package/package.json +3 -2
- package/renderer/renderer.js +40 -35
- package/src/config.js +47 -12
- package/src/tools/compose.js +6 -5
- package/src/tools/patch.js +1 -2
- package/src/tools/render.js +1 -2
- package/src/tools/widget.js +7 -6
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,19 @@ All notable changes to this project are documented here. The format follows
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [0.2.10]
|
|
10
|
+
|
|
11
|
+
### Changed
|
|
12
|
+
- Widget policy settings (`cdnAllowlist`, `allowNetwork`, `maxWidgetHeight`, `maxWidgetBytes`, `sendPromptMaxChars`, `openLinkAllowedOrigins`) are now live settings: they can be edited from the official settings forms, including DeepSeek Harness Remote clients, and apply to the next tool call without reloading the plugin.
|
|
13
|
+
- Settings descriptions are available in Simplified Chinese.
|
|
14
|
+
|
|
15
|
+
### Security
|
|
16
|
+
|
|
17
|
+
- Separated popup, top-navigation, form and download probes in the sandbox audit. A form check now requires an actual CSP violation and no request; download checks use browser events with a positive control. Cross-origin download links can still issue self-navigation requests even when saving the response is blocked.
|
|
18
|
+
- Widget sandbox boundary audit ([#19](https://github.com/dreamfarer-space/deepseek-harness-intelligent-ui/issues/19)), with the measured matrix in [docs/widget-csp-audit.md](docs/widget-csp-audit.md) and a regression suite in `npm run e2e:security` covering every resource type and request channel (`img`, `script`, `style`, `font`, `media`, `fetch`, XHR, WebSocket, `EventSource`, `sendBeacon`, `<a ping>`, `iframe`, `object`, form, download, popup, navigation).
|
|
19
|
+
- **Fixed:** host messages now use a document-bound `MessageChannel` established by the bootstrap before generated code. A navigated document cannot receive host RPC results, HTML or theme updates, even if it delays its `load` event indefinitely. A later iframe `load` still removes foreign UI; it is a best-effort visual signal, not the data boundary. Rebuilds rotate credentials and reset lifecycle in a layout effect; stale async replies cannot reach a new channel. Self-navigation itself and data in its initial URL remain possible.
|
|
20
|
+
- Documented that `allowNetwork=false` stops script-visible network access (`fetch`, XHR, WebSocket, `EventSource`, `sendBeacon`, `<a ping>`) but not resource-element requests to an **allowlisted** origin, and that this implementation emits origin-only CSP sources without path restrictions. The `allowNetwork` setting description says so, and an empty `cdnAllowlist` remains the way to block external resource requests, but cannot stop self-navigation.
|
|
21
|
+
|
|
9
22
|
## [0.2.9]
|
|
10
23
|
|
|
11
24
|
### Fixed
|
package/README.md
CHANGED
|
@@ -256,8 +256,10 @@ Fields follow the OpenIntelligentUI streaming contract, **in this order**: `titl
|
|
|
256
256
|
<img src="https://raw.githubusercontent.com/dreamfarer-space/deepseek-harness-intelligent-ui/main/docs/assets/readme/security.svg" alt="Widgets run in an opaque-origin sandboxed iframe under a strict CSP; access to the host page is blocked and only token-checked postMessage calls reach the rate-limited bridge" width="100%">
|
|
257
257
|
|
|
258
258
|
- **Widget isolation:** `<iframe sandbox="allow-scripts allow-forms">` with `srcdoc` and **no** `allow-same-origin`. The E2E verifies in a browser that `parent.document`, `localStorage` and `document.cookie` throw `SecurityError`, `fetch` is blocked, and `window.origin === "null"`.
|
|
259
|
-
- **CSP:** `default-src 'none'`; scripts, styles, fonts and images only from `cdnAllowlist`; `connect-src 'none'` unless `allowNetwork`; `form-action 'none'`; `base-uri 'none'`.
|
|
260
|
-
- **
|
|
259
|
+
- **CSP:** `default-src 'none'`; scripts, styles, fonts and images only from `cdnAllowlist`; `connect-src 'none'` unless `allowNetwork`; `form-action 'none'`; `base-uri 'none'`. A per-widget sandbox boundary audit with the measured matrix is in [docs/widget-csp-audit.md](docs/widget-csp-audit.md) and runs as `npm run e2e:security`.
|
|
260
|
+
- **What `allowNetwork=false` does and does not stop:** it blocks `fetch`, XHR, WebSocket, `EventSource`, `sendBeacon` and `<a ping>`. It does **not** stop `<img>`, `<script src>`, `<link rel=stylesheet>`, `@font-face` or `<video>` from requesting an **allowlisted** origin, and this implementation emits origin-only `cdnAllowlist` sources, without path restrictions — so an allowlisted CDN can still see widget data in a resource URL. Set `cdnAllowlist: []` to block external resource requests; the offline bundle already does. This does not stop a widget navigating its own frame or sending data in that initial navigation URL.
|
|
261
|
+
- **Navigation:** a document-bound MessageChannel prevents a replacement document from receiving host data. A later iframe `load` removes the foreign UI; an unfinished response can delay this visual removal. The initial self-navigation request is still possible. The iframe sandbox blocks top-level navigation, `window.open` and file downloads. A cross-origin download link can still trigger a self-navigation request before the browser blocks saving the response.
|
|
262
|
+
- **Messages:** a one-time handshake validates the iframe source and fresh document token before accepting its transferred port. All later messages use that port and validate token/shape; window RPCs and stale ports are rejected (verified in E2E).
|
|
261
263
|
- **`sendPrompt` limits:** refused for 1 s after mount, at most one per 2 s, capped at `sendPromptMaxChars`, queued as a normal user message.
|
|
262
264
|
- **`openLink`:** https only, optionally restricted to `openLinkAllowedOrigins`.
|
|
263
265
|
- **Catalog:** text renders as React elements, never raw HTML; `Plot` expressions are compiled over a fixed math table with no `eval`, `Function` or property access; new-session launches and host actions need an explicit native click.
|
|
@@ -279,7 +281,7 @@ The bundle patch inserts the plugin row automatically. To change settings, add t
|
|
|
279
281
|
| `enableWidget` | `true` | Register `ui_render_widget` |
|
|
280
282
|
| `injectGuidance` | `true` | Add the "when to use UI" system-prompt section |
|
|
281
283
|
| `cdnAllowlist` | esm.sh, jsdelivr, unpkg | Origins widgets (and Math/Mermaid) may load resources from |
|
|
282
|
-
| `allowNetwork` | `false` | Let widgets `fetch` and use WebSocket to those origins |
|
|
284
|
+
| `allowNetwork` | `false` | Let widgets `fetch` and use WebSocket to those origins. Off still allows resource elements to reach them; use an empty `cdnAllowlist` to block external resource requests (self-navigation is still possible) |
|
|
283
285
|
| `maxWidgetHeight` | `2400` | Maximum frame height (px) |
|
|
284
286
|
| `maxWidgetBytes` | `400000` | Reject larger widgets |
|
|
285
287
|
| `sendPromptMaxChars` | `4000` | Longest message a widget, button, form or choice may send |
|
package/README.zh-CN.md
CHANGED
|
@@ -256,8 +256,10 @@ npx deepseek-harness-intelligent-ui doctor # 只诊断,不做修改
|
|
|
256
256
|
<img src="https://raw.githubusercontent.com/dreamfarer-space/deepseek-harness-intelligent-ui/main/docs/assets/readme/security.zh.svg" alt="组件运行在不透明源沙箱 iframe 中并受严格 CSP 约束;访问宿主页面会被拦截,只有通过 token 校验的 postMessage 才能到达限流的 Bridge" width="100%">
|
|
257
257
|
|
|
258
258
|
- **隔离:** `<iframe sandbox="allow-scripts allow-forms">` 配合 `srcdoc`,**不加** `allow-same-origin`。E2E 在浏览器中实测:`parent.document`、`localStorage`、`document.cookie` 均抛出 `SecurityError`,`fetch` 被拦截,`window.origin` 为 `"null"`。
|
|
259
|
-
- **CSP:** `default-src 'none'`;脚本、样式、字体、图片只能来自 `cdnAllowlist`;未开启 `allowNetwork` 时 `connect-src 'none'`;`form-action 'none'`、`base-uri 'none'`。
|
|
260
|
-
-
|
|
259
|
+
- **CSP:** `default-src 'none'`;脚本、样式、字体、图片只能来自 `cdnAllowlist`;未开启 `allowNetwork` 时 `connect-src 'none'`;`form-action 'none'`、`base-uri 'none'`。逐通道实测矩阵见 [docs/widget-csp-audit.md](docs/widget-csp-audit.md),并可运行 `npm run e2e:security`。
|
|
260
|
+
- **`allowNetwork=false` 能挡与不能挡什么:** 它能拦截 `fetch`、XHR、WebSocket、`EventSource`、`sendBeacon` 和 `<a ping>`;但**不能**阻止 `<img>`、`<script src>`、`<link rel=stylesheet>`、`@font-face`、`<video>` 向**允许名单内**的来源发起请求,而本实现将 `cdnAllowlist` 转为仅含来源的 CSP 条目,不限制资源路径——允许名单内的 CDN 仍可能在资源 URL 中看到组件数据。要阻止外部资源请求,请设 `cdnAllowlist: []`(离线包已如此);它仍不能阻止组件自身导航,以及在首次导航 URL 中携带数据。
|
|
261
|
+
- **导航:** 绑定原文档的 MessageChannel 可阻止替换文档接收宿主数据。后续 iframe `load` 会移除外部页面;未完成的响应可延迟这一步界面移除。首次自身导航请求仍可能发生。iframe sandbox 已阻止顶层导航、`window.open` 和文件下载。跨来源下载链接仍可能先触发自身导航请求,再由浏览器阻止保存响应。
|
|
262
|
+
- **消息校验:** 首次握手校验 iframe 来源与每个文档的新 token,再接收其转移的端口。后续消息仅走该端口并校验 token/格式;窗口 RPC 与旧端口均被拒绝(E2E 实测)。
|
|
261
263
|
- **sendPrompt 限制:** 挂载后 1 秒内拒绝,2 秒内最多一次,长度受 `sendPromptMaxChars` 限制,以普通用户消息排队发送。
|
|
262
264
|
- **openLink:** 只允许 https,可用 `openLinkAllowedOrigins` 进一步限制。
|
|
263
265
|
- **组件目录:** 文本作为 React 元素渲染,不作为 HTML;`Plot` 表达式只能调用固定的数学函数,无法使用 `eval`、`Function` 或访问属性;新开会话和宿主动作都要求用户原生点击。
|
|
@@ -279,7 +281,7 @@ npx deepseek-harness-intelligent-ui doctor # 只诊断,不做修改
|
|
|
279
281
|
| `enableWidget` | `true` | 注册 `ui_render_widget` |
|
|
280
282
|
| `injectGuidance` | `true` | 注入“何时使用 UI”的系统提示 |
|
|
281
283
|
| `cdnAllowlist` | esm.sh、jsdelivr、unpkg | 组件(以及 Math/Mermaid)可加载资源的来源 |
|
|
282
|
-
| `allowNetwork` | `false` | 允许组件对上述来源发起 `fetch` 和 WebSocket |
|
|
284
|
+
| `allowNetwork` | `false` | 允许组件对上述来源发起 `fetch` 和 WebSocket。关闭时资源类元素仍可访问这些来源;要阻止外部资源请求(仍可发生自身导航)请把 `cdnAllowlist` 设为空 |
|
|
283
285
|
| `maxWidgetHeight` | `2400` | iframe 最大高度(px) |
|
|
284
286
|
| `maxWidgetBytes` | `400000` | 超过此大小的组件会被拒绝 |
|
|
285
287
|
| `sendPromptMaxChars` | `4000` | 组件、按钮、表单、选择题单条消息的最大长度 |
|