在线 Demo 系统设计:让文章里的代码真的跑起来
本文目录
“点击运行”会让不可信代码、用户输入、浏览器 API、网络和服务器资源进入同一个边界。先问 Demo 是否必须执行任意代码;大多数教学示例可用预置组件、静态数据或白名单渲染,没必要提供通用代码执行器。
风险分层
- 只读预览:文章中渲染固定图片、视频、3D 模型或 Mermaid;不运行作者任意 JavaScript。
- 白名单交互:Canvas、颜色、表单或简单计算只在页面本地运行,限制输入大小和运行时间。
- 独立 iframe:将演示放在不同 origin,使用 sandbox 与最小权限;按需配置 CSP、允许源、下载/弹窗策略。不要同时授予 allow-scripts 和 allow-same-origin 来削弱隔离。
- 受控后端 API:服务端只提供特定动作和固定数据,不接受用户代码或任意 URL 代理。
- 任意代码沙箱:需要隔离运行时、临时文件系统、网络默认禁用、CPU/内存/时间配额、并发上限和销毁机制。普通容器并不自动抵御宿主内核漏洞;高风险场景应使用更强隔离或专门托管服务。
文章 Demo 数据模型
用结构化 block 描述 type、title、说明、默认参数、尺寸和版本。type 必须来自服务端白名单;参数按各自 schema 校验。禁止 eval、动态 import 用户 URL、从文章中拼接 script 标签或允许评论区创建可执行 block。给每个 block 配失败占位、加载状态、复制代码开关和移动端高度上限。
消息跨 iframe 时,校验精确 origin、source window、消息 type、字段范围、长度与速率;不要使用通配 origin 把敏感数据广播给任意 frame。跨域接口不应携带主站 Cookie,也不应将管理员 token 注入演示环境。
性能与隐私
IntersectionObserver 进入视口前只展示静态海报,用户主动运行后再加载大依赖。限制模型权重和图片体积,显示预计下载大小,支持取消和超时;失败可退回源码和截图。用户本地文件默认不上传,若必须上传则先说明用途、保留时长、删除和访问权限。
安全验收
- 试图读取父页 DOM/localStorage、跳转顶层页面、弹窗、发起跨域请求、下载或突破 iframe sandbox。
- 伪造 postMessage origin、字段类型、超长 payload 和高频事件。
- 强制超时、断网、内存压力和快速切换路由,确认 worker/iframe/对象 URL 被清理。
- 访客只能看到公开 Demo;草稿、隐藏文章和密钥不被静态资源或 sourcemap 暴露。
- 测手机视口和键盘操作;Demo 失败时文章仍可阅读。
把允许运行的能力做小、做窄,才有条件在博客中持续增加 Demo,而不是每篇文章都变成新的安全审计项目。
组件生命周期和加载预算
为 Demo 规定初始 JS/CSS/媒体预算;超过预算时拆成用户点击后加载的 chunk,并显示下载大小与替代阅读方式。iframe/worker 使用独立实例和显式销毁路径;路由切换、折叠和重复点击都要清理。父页面只传启动参数和展示结果,不向沙箱传管理员身份或整页状态。正在执行的计算需有停止按钮,并支持键盘焦点、暂停和 reduced motion。
Demo schema 带版本和类型白名单,编辑预览与线上阅读使用同一解析规则。未知版本退回源码,不猜测解释;固定 fixture 要标明“示例数据”,不能伪装成真实生产结果。升级 schema 时保留迁移路径和回退策略。
资源回收要有可重复验证的检查:打开/关闭 Demo 多次后监听器数量不增长,退出页面后网络请求和音频停止,图表/模型释放 GPU 与对象 URL。对下载内容提供取消并处理部分文件;切换文章时忽略旧请求迟到的结果。低性能设备应限制并发解码和纹理尺寸,必要时提供静态替代,避免用户误以为页面卡死。
Live Notes
留言提交后会先进入审核,审核通过后对其他读者公开。