preview-sandbox/html-preview-sandbox
GitHub: preview-sandbox/html-preview-sandbox
一个用于在浏览器和 Node 环境中安全预览不受信任 HTML 的前端库,通过 DOMPurify 消毒、CSP 策略和沙盒化 iframe 构建多层防御 pipeline。
Stars: 1 | Forks: 0
# html-preview-sandbox
[](https://www.npmjs.com/package/html-preview-sandbox)
[](https://github.com/preview-sandbox/html-preview-sandbox/actions/workflows/ci.yml)
[](https://www.npmjs.com/package/html-preview-sandbox#provenance)
[](LICENSE)
在沙盒化的 iframe 中安全预览不受信任的交互式 HTML。
`html-preview-sandbox` 帮助应用程序渲染不受信任的 HTML 文件、用户上传内容以及 AI 生成的报告,而无需授予它们完整的浏览器权限。它结合了 DOMPurify 消毒、CSP 预设、不透明源的沙盒化 iframe、外部链接中介以及宿主回调。
**[在托管的 Playground 中试用 →](https://preview-sandbox.github.io/html-preview-sandbox/playground/)**
## 安装
```
npm install html-preview-sandbox
```
## 快速开始
```
import { createPreview } from 'html-preview-sandbox';
const preview = createPreview(document.querySelector('#preview'), {
csp: 'strict',
onOpenExternal(url) {
window.open(url, '_blank', 'noopener,noreferrer');
},
onCspViolation(report) {
console.warn('CSP blocked:', report);
},
onSanitize(report) {
console.info('Sanitize report:', report);
},
});
await preview.render(fileOrHtmlString);
```
## 运行时入口点
该包仅支持 **ESM**,并且 Node/默认入口需要 Node 18+。
它通过包导出条件提供独立的 Node 和浏览器构建:
- Node/默认导入:使用 DOMPurify 和 jsdom。
- 浏览器导入:使用 DOMPurify 和真实的浏览器 `window`。
- 显式浏览器导入:`html-preview-sandbox/browser`。
大多数现代打包器会自动解析 `browser` 条件。示例和 Playground 运行的是 `dist/index.browser.js`,因此从仓库中打开它们之前,请先构建该包。
当你的运行时或打包器不支持 `browser` 条件时,请使用显式的浏览器子路径:
```
import { createPreview } from 'html-preview-sandbox/browser';
```
## 消毒器选择
本项目目前使用 **DOMPurify** 作为消毒层。
- 浏览器运行时:DOMPurify 针对真实的浏览器 `window` 运行。
- Node 运行时:DOMPurify 与 jsdom 一起运行。
- 当前实现中未使用 `sanitize-html`。
DOMPurify 仍然只是一层。预览还依赖于 CSP、沙盒化 iframe、桥接处理、外部 URL 过滤以及可选的宿主导航拦截。
## 它是什么
- 用于不受信任的 HTML 文件和字符串的客户端预览 pipeline。
- 一种在降低宿主应用风险的同时保留有用交互性的方法。
- 一个包含 Web 示例和 Playground 的小型核心包。
## 它不是什么
- 不是浏览器:它不浏览 URL 或支持多页面导航。
- 不是附件系统:下载、解密、缓存和权限检查属于宿主应用。
- 不是 DOMPurify 的替代品:消毒只是深度防御 pipeline 中的一层。
- 不能保证所有 HTML 都是安全的:请参阅 `THREAT_MODEL.md`。
## Pipeline
```
input -> decode -> sanitize -> CSP policy -> bridge -> sandboxed iframe
```
默认沙盒有意省略了 `allow-same-origin`,从而为预览的文档提供一个不透明的源。外部链接和 `window.open` 调用会通过回调转发给宿主。在纯 Web 环境中,由 JavaScript 驱动的 `window.location` 导航无法被完全拦截;Electron 或其他宿主可以添加更强的导航控制。
能够观察 iframe 导航的宿主集成可以调用 `preview.notifyNavigationAttempt(url)`。核心渲染器将恢复上次受信任的 `srcdoc`,并通过相同的外部链接白名单转发该 URL。
这三个 CSP 预设是按**数据外泄能力**分层的,而不是按它们加载哪些资源分层的:
- `offline`:完全无网络。仅限内联 script/style 和 `data:`/`blob:` 资源。
- `strict`(默认):阻止攻击者可读取的数据外泄通道 —— `connect-src 'none'`、`form-action 'none'`,并且没有通配符 `img-src`/`media-src`。可以加载固定的静态 CDN/字体主机,并且允许内联 script 加上 `unsafe-eval`,因为它们除了已允许的内联 script 外没有增加任何攻击者能力,并且没有提供攻击者可读取的数据外泄途径。这并非“零网络” —— 对白名单主机的请求仍会离开本机(参见 `THREAT_MODEL.md` 中的残余风险说明)。
- `balanced`:开放 `https:` 图像/媒体和 `connect-src https:`。这是一个通用的数据外泄攻击面,因此仅将其用于半受信任的内容。
默认情况下,自定义沙盒 token 会被过滤。诸如 `allow-same-origin`、`allow-downloads` 和顶级导航权限之类的高风险 token 会被忽略,除非设置了 `allowUnsafeSandboxTokens`。
外部链接同样是失败即关闭的。默认协议白名单为 `http:`、`https:`、`mailto:` 和 `tel:`。宿主可以通过 `externalProtocols` 缩小其范围,并使用 `allowExternalUrl` 添加域或产品规则。
## 文档
- [集成指南](docs/INTEGRATION.md)
- [使用文档(中文)](docs/README.zh-CN.md)
- [安全模型](docs/SECURITY_MODEL.md)
- [消毒器决策](docs/SANITIZER_DECISION.md)
- [浏览器支持](docs/BROWSER_SUPPORT.md)
- [项目结构](docs/PROJECT_STRUCTURE.md)
- [分支与发布](docs/BRANCHING.md)
- [路线图](docs/ROADMAP.md)
- [威胁模型](THREAT_MODEL.md)
- [安全策略](SECURITY.md)
## 项目结构
包源代码位于 `src/` 中并构建到 `dist/`。测试和本地示例针对构建输出运行,因此本地检查与使用者收到的包相匹配。
```
src/ TypeScript package source
dist/ Generated ESM bundles and declarations
playground/ Local security inspection workbench
examples/ Minimal integration examples
fixtures/ HTML regression inputs
test/ Node and Playwright tests
docs/ Integration, architecture, and security notes
```
## 测试
```
npm run check:types
npm test
npm run test:browser
npm run build
```
类型检查会验证 TypeScript 源代码和生成的公共 API 接口。Node 测试套件涵盖了解码、CSP 生成、注入顺序、协议过滤和消毒报告。Playwright 套件验证了真实的浏览器 iframe 沙盒和外部链接桥接,以及 Web 示例、文件上传示例、Web Component 和 Playground 行为。
在 CI 中,在 `npm run test:browser` 之前安装 Playwright 浏览器:
```
npx playwright install --with-deps chrome
```
在发布更改之前运行默认的本地质量门禁:
```
npm run check
```
## Playground
构建包后,使用任何静态文件服务器托管仓库根目录,然后打开 `/playground/`。
```
npm run build
npm run serve
```
然后访问 `http://localhost:4173/playground/`。
Playground 是一个三面板工作台,包含 HTML 输入、沙盒化预览,以及一个用于检查消毒器移除项、CSP 违规、外部请求和当前策略的检查器。它还包含示例 payload 以及用于 CSP 预设、外部协议和宿主后缀过滤的控件。你可以将 HTML 文件拖放到编辑器上,切换“已消毒的 HTML”视图以检查 pipeline 生成的确切文档,并使用“分享”将输入 + 预设编码为可共享的 URL。
### 托管的 Playground
`.github/workflows/pages.yml` 会在每次推送到 `main` 分支时将 Playground 部署到 GitHub Pages。该工作流构建库并组装一个镜像 Playground 相对导入的站点,因此无需进行任何代码更改。一次性设置:在仓库中,转到 **Settings → Pages → Source** 并选择 **GitHub Actions**。首次成功运行后,即可在 `https://preview-sandbox.github.io/html-preview-sandbox/playground/` 访问 Playground。
## 状态
`v0.1.0` 已发布到 npm,并附带构建来源证明。这是最初的 TypeScript 实现,API 预计在 1.0 版本之前会进一步演进。
标签:CSP策略, HTML渲染, Web组件, XSS防御, 沙箱隔离, 特征检测, 自动化攻击