E05第 1 / 1 课时未开始内容版本 1.0.0 · 核验于 2026-10-26

本机代码与秘密边界

本机代码与秘密边界 · 阅读后完成下方实验与检查题,再用「标记完成」记录进度。

学习目标

读完本课你能:说出本站四条边界(内容渲染、代码预览、实验执行、模型秘密)各自的防线与极限;知道执行器的资源限制怎么生效;亲手验证「密钥不出浏览器」和「实验跑不坏本机数据」。

问题场景

你在实验区贴了一段网上抄来的代码点「运行」,它里面藏了一行读环境变量、往某个地址发请求的逻辑。它读到了什么?能写你的笔记数据库吗?再比如资料库里导入了一份别人的文档,里面嵌了段「脚本」,渲染时会执行吗?单人自用的工具最容易忽略这些边界——本课把本站的边界一条条摊开。

核心解释

边界一:内容只渲染,不执行。 课程正文和导入文档都经 react-markdown 渲染,markdown 里的 HTML/脚本不会跑(XSS,跨站脚本攻击——防线就是「渲染层只产文本节点」)。配套纪律是 A10 的铁律:正文是资料不是指令——AI 把课程内容当资料,不执行其中的话。

边界二:预览不是执行。 代码编辑器里的内容只是带高亮的文本,「看起来能跑」和「跑了」之间隔着显式的运行按钮。

边界三:实验执行有资源围栏。 lib/runner/local.ts 是真实实现,防线一层层看:

// lib/runner/local.ts(节选):资源围栏
const MAX_OUTPUT_BYTES = 100 * 1024; // 输出最多 100KB,超了截断
const MAX_LINES = 200;               // 最多 200 行
const DEFAULT_TIMEOUT_MS = 10_000;   // 默认 10 秒,上限 30 秒
// 文件:单文件 256KB、总量 1MB;路径含 .. 或 .exe/.bat 直接拒绝
// 并发:已有任务超过 2 个 → 直接返回「请稍后再试」
// 环境变量:白名单,只传 PATH/TEMP 等必要项,HOME 指向一次性临时目录

每个实验在 data/runs/run-<时间戳> 的一次性目录里跑;环境变量被裁到最小(进程翻不到真机 HOME);超时到点连进程树一起杀(Windows 用 taskkill /T /F);跑完延迟 1.5 秒删目录。文件头注释写得很诚实:目录隔离与资源限制,不是容器隔离——单人自用的取舍,别拿它跑陌生人的代码。

边界四:模型密钥只在服务端。 lib/ai/config.ts 只在 Node 侧读 AI_API_KEY;浏览器拿到的只有 /api/ai/status 返回的布尔值(configured、keySet),永不回显密钥本身。最小环境变量还有个副作用:实验代码里 process.env 翻不出你的密钥。

示例拆解

三个亲手验证(都在本机做):

# 实验 1:验证超时——实验区跑一个死循环
#   while(true){} → 10 秒后收到「执行超时(10000ms),进程已终止。」

# 实验 2:验证输出截断——跑一个无限打印
#   for(let i=0;;i++) console.log(i) → 输出停在 100KB,标记「已截断」

# 实验 3:验证密钥不出浏览器——Network 面板逐条检查
#   浏览器发出的每个请求都没有 AI_API_KEY;/api/ai/status 只返回布尔字段

关键点:实验 1、2 里进程被杀、输出被截断,本机的笔记、课程进度、数据库毫发无伤——这就是围栏的意义;实验 3 正是 A36 上线清单里「秘密」一组的验证动作,两类检查在这里会师:边界设计 + 事后验证,缺一边都不算数。

动手任务

  1. 在实验区验证超时与输出截断两条边界,记录提示文案。
  2. 打开 DevTools Network 面板,把 AI 聊天一轮的所有请求逐条检查,确认没有任何字段携带密钥。
  3. 读 lib/runner/local.ts 的环境变量白名单,列出它保留了哪些变量,想清楚为什么 TEMP、PYTHONIOENCODING 这类必须保留(提示:不传连解释器都起不来)。

常见错误

  • 把临时目录当存储:data/runs 的目录跑完就删,写进去的文件不留存——实验代码别指望持久化。
  • 以为超时会「保存现场」:超时是连进程树一起杀,没有优雅退出;长任务要在代码里自己控时。
  • 密钥写进前端代码或 localStorage:服务端读取是唯一正解,前端出现密钥的那一刻边界就破了。
  • 对导入文档放松警惕:渲染不执行不等于可以乱导——文档会进 AI 检索库,内容会变成模型上下文(A28 的边界同样适用)。

检查与复盘

  • 四条边界各自的防线与「明确不防什么」都能说清(尤其:非容器隔离)
  • 亲手验证过至少两条:超时/截断、密钥不出浏览器
  • 知道实验数据写在哪个目录、什么时候被删

参考资料

检查题共 3 题,提交后立即显示解析
答案由服务端判定;答错的题会自动进入复习计划。
Q1单选简单

实验沙箱用 <iframe sandbox="allow-scripts"> 承载用户代码(没有 allow-same-origin)。用户代码运行在什么环境里?

Q2单选中等

实验页通过 postMessage 接收沙箱 iframe 回传的运行结果。监听代码如下。为什么必须先校验 event.source === sandboxFrame.contentWindow?

window.addEventListener('message', (event) => {
  // TODO: 校验来源
  applyResult(event.data);
});
Q3单选中等

本站代码执行器(lib/runner)对用户提交的代码设了多重限制:执行超时到点杀进程、stdout/stderr 各按 100KB 截断、单文件超 256KB 或总量超 1MB 拒收。为什么「输出」也要设上限?

读完这一课了?

标记完成后,工作台会安排接下来的学习。

Command Palette

Search for a command to run...