db74a96de1
style: 格式化 Rust 与 TS 代码
2026-07-23 13:34:27 +08:00
22d226d4e8
feat(code-runner): 添加语言别名归一化与 bun 运行器注册
...
新增 normalize_lang 别名表,把读者/作者习惯的语言简写归一到 canonical key:
- js / javascript → node
- rs → rust
- ts / typescript → bun
归一化在 markdown 渲染期(parse_fence_info)即完成,使 data-lang、
ExecRequest.language、ExecResult.language、LANGUAGES.get 全程只见到
canonical 名(python/node/go/rust/bun),避免前端用别名调 StartExec 时
查表落空。execute.rs 的 start_exec / start_exec_stream 在 spawn 前显式
normalize_lang,保证 ExecResult.language 回显 canonical。
同时注册 bun 语言(canonical):镜像 yggdrasil-runner-bun,run_cmd
'bun run /code/main.ts',默认资源与 node 对齐(256MB/5s)。Dockerfile
与 build-runners.sh 跟进。
测试覆盖三处协同契约:languages.rs 的 normalize_lang 别名表、
execute.rs 的 validate 接受别名、markdown.rs 的 data-lang 归一化。
2026-07-21 15:21:58 +08:00
fa8b1d9968
test: 补齐安全关键路径的单元测试盲区
...
针对前期测试评估发现的高性价比纯函数与安全不变量盲区补测,
共新增 35 个回归测试(全部通过),分布如下:
- infra/docker.rs (+5): 扩展 build_host_config 的隔离断言——cap_drop=ALL、
security_opt=no-new-privileges、pids_limit=64、memory_swap==memory(禁 swap)、
tmpfs 的 exec 仅落在 /tmp、ulimits 只保留 nofile(无 nproc)。这些配置若被
误删会让容器隔离悄悄失效,此前 test_host_config_generation 仅断言 cpu/memory。
- api/code_runner/execute.rs (+8): validate_exec_request 此前零测试。覆盖
语言白名单(大小写/空白容忍、多 token 拒绝、未注册拒绝)、源码大小边界
(恰好 max 放行 / max+1 拒绝)、校验顺序(语言优先于大小)。
- api/database/export.rs (+12): is_simple_ident / parse_source 此前零测试。
覆盖表名白名单(SQL 注入 / 路径穿越 / Unicode 拒绝)、query: 来源的只读
AST 校验(INSERT/UPDATE/DELETE/DROP/TRUNCATE/CREATE/ALTER/GRANT 全拦截)。
- api/sanitizer.rs (+10): 补 XSS 攻击向量回归——事件处理器属性(onerror/onload/
onclick + 大小写混淆)、script/style 连内容移除、iframe/object/svg/form 等
危险标签整体移除、javascript:/vbscript: scheme(含 HTML 实体编码绕过)、
data URI 在文章正文路径(allow_data_uri=false)的拒绝、svg data URI 风险点锁定。
顺带记录一个既有可维护性问题:docker.rs 的 4 个 test_run_in_container_*
依赖真实 Docker daemon,无 daemon 环境会失败而非跳过,建议后续加 #[ignore]
或 daemon 探测守卫。本次未改动它们。
2026-07-16 11:55:58 +08:00
65a7b1226f
style: 全项目格式规范化(cargo fmt)+ Docker daemon 断连容错
...
- 全项目 .rs 统一 cargo fmt 排版(换行/缩进/参数分行/导入排序)
- infra/docker: DOCKER_CLIENT 连接失败不 panic,降级返回 None
(博客不依赖 Docker,panic=abort 下会导致整个进程崩溃)
- infra/mod: 去除多余空行
- database/mod: 模块声明按字母序重新排序
2026-07-15 17:22:06 +08:00
e50941ef24
fix(runner): propagate duration_ms through SSE done event
...
The SSE streaming path hardcoded '耗时: -' because duration_ms was
never threaded through:
- docker.rs: OutputChunk::Done now carries duration_ms, computed from
start_container to wait completion via Instant::elapsed
- sse.rs: DonePayload gains duration_ms, serialized into the done event
- runner.rs: WASM done callback formats the real duration instead of '-'
The polling fallback path already had correct duration via ExecResult;
only the SSE path was missing it.
2026-07-13 10:09:15 +08:00
aa2d7f3f2d
fix(runner): use python -u for unbuffered stdout streaming
...
Python defaults to 4KB block buffering when stdout is a pipe (not a
TTY). Docker attach uses pipes, so print() output accumulated in the
buffer and only flushed when the process exited — the 3-second
sleep(1) loop appeared as a single dump at the end.
python -u (equivalent to PYTHONUNBUFFERED=1) forces line-level
flushing, so each print() is immediately written to the pipe and
picked up by the concurrent log reader → SSE → xterm.js.
Node/Go/Rust already line-buffer to pipes, so only Python needed the
flag. stdout/stderr separation is preserved (no TTY merge).
2026-07-13 10:03:58 +08:00
60138ed6e1
feat(api): add SSE endpoint /api/exec/stream
...
GET /api/exec/stream?task_id=X — pops the mpsc Receiver from
EXEC_STREAMS (one-shot, prevents duplicate consumers), wraps it in
ReceiverStream, maps OutputChunk → SSE Event (stdout/stderr/done).
keep_alive every 15s prevents reverse-proxy idle timeouts.
Route registration: dedicated sse_route with csrf_middleware but NO
TimeoutLayer (SSE is a long-lived connection; the 30s app-route timeout
would kill it). Auth + rate limit already enforced in start_exec_stream.
main.rs merge order: upload(300s) → export(120s) → sse(no timeout) →
app(30s) → static(none).
2026-07-10 11:48:26 +08:00
1e556aa125
feat(runner): add SSE task registry and streaming executor
...
progress.rs:
- EXEC_STREAMS DashMap: SSE handler pops rx by task_id (one-shot)
- StreamEntry wraps rx + created_at for TTL-based GC
- gc_old_tasks now cleans both EXEC_TASKS and EXEC_STREAMS
execute.rs:
- validate_exec_request: extracted shared whitelist + size check
- check_rate_limit_for_user: extracted shared admin-aware rate limit
- start_exec refactored to use both helpers (behavior unchanged)
- start_exec_stream: new server fn, same validation chain, spawns
run_in_container_stream(tx) + writes EXEC_TASKS for polling fallback
2026-07-10 11:45:20 +08:00
2f6f8e549d
refactor(code-runner): default CODE_RUNNER_LANGUAGES to all-open
...
CODE_RUNNER_LANGUAGES 未设置时,白名单不再默认为 python,node,
而是放行 LANGUAGES 注册表里的全部语言。该环境变量变为「可选的收窄开关」:
新增语言到注册表即可默认启用,无需再同步运维白名单(避免两边脱节,
如本次 go/rust 就因 .env 未同步白名单导致 is_supported_lang 拒绝)。
- RunnerConfig.languages: Vec<String> → Option<Vec<String>>,
None 表示不限制
- is_supported_lang: Option 为 None 时直接放行注册表语言
- 测试 is_supported_lang_default_whitelist → is_supported_lang_default_all_open,
断言 go/rust 在默认(未设白名单)下可用
2026-07-09 11:02:04 +08:00
c624997c04
feat(code-runner): add Go and Rust language support
...
为 Code Runner 新增 Go 和 Rust 两种编译型语言,沿用现有 base 镜像分层结构。
Go:
- run_cmd 直接用 go run(单条命令,内部编译+运行)
- 镜像把 GOCACHE/GOTMPDIR/GOPATH 重定向到 /tmp tmpfs
(只读根文件系统下 /Users/issuser/.cache 不可写)
- default_limits: 1 核 / 256MB / 10s(编译冷启动比解释型慢)
Rust:
- rustc 编译 + 运行是两步,但 docker.rs 注入脚本用 exec 执行 run_cmd,
exec 替换 shell 进程后 'A && B' 后半段不执行,
因此镜像内置 /usr/local/bin/run-rust.sh wrapper 封装两步
- default_limits: 1 核 / 512MB / 15s(rustc 内存大、编译慢)
新增镜像与白名单后,默认 CODE_RUNNER_LANGUAGES=python,node,go,rust。
2026-07-09 09:40:32 +08:00
9231e993e4
fix(clippy): 修复两处预存 clippy 错误
...
- execute.rs: ExecResult/ExecStatus 仅 server 端使用,加 cfg gate
消除 WASM 目标的 unused_imports
- system.rs: in_scope 外层闭包保留 || execute_for_editor() 包装
(Closure::new 要求 Fn 可重复调用,直接传闭包会 use-after-move),
加 #[allow(clippy::redundant_closure)] 并注释原因
cargo clippy 双目标 -- -D warnings 均通过。
2026-07-06 11:42:03 +08:00
c83b834c8b
feat(code-runner): admin 跳过代码运行速率限制
...
作者在 /admin/runner 沙箱调试代码块时,1 次/秒 + 50 次/天的读者限流
(RATE_LIMIT_CODE_EXEC_*)会频繁打断试运行。已登录 admin 改为跳过
check_code_exec_limit,仍受并发槽、资源钳制与源码大小校验约束。
2026-07-05 14:32:56 +08:00
f3eb93f320
docs(agents): document code runner and fix clippy/biome lint
...
文档:
- AGENTS.md 新增「Code Runner」架构章节(三层架构 + WASM 可见性规则 +
governor 0.8 无 per_day 的处理 + runner 镜像契约)
- Environment 段补齐 CODE_RUNNER_* 与 RATE_LIMIT_CODE_EXEC_* 环境变量
Lint 修复(clippy --all-targets --all-features -D warnings 全绿):
- runner.rs/runner.rs: use_signal 冗余闭包 → 直接传 fn
- runner_config.rs: &*RUNNER_CONFIG 去 deref;assert_eq!(...false/true) → assert!
- markdown.rs: 局部变量 /// 改 //;Option.map().flatten() → and_then
- languages.rs: 删除从未读取的 LanguageDef.name 字段(语言名即 map key)
- docker.rs: start_container if let Err → ?;timeout match → is_err();
嵌套 if 合并(修 task 2 遗留 + clippy 1.96 新 lint)
- pages/admin/posts.rs: use_signal 冗余闭包(预存 lint,顺带修绿)
- codemirror editor.ts: biome format 重排 setSchema 单行
2026-07-03 18:57:25 +08:00
6b875e6ba5
feat(ui): create CodeRunner Dioxus UI component with async polling
...
- runner.rs: CodeRunner 组件(源码展示 + Run 按钮 + 阶段/输出/错误区)
spawn 提交 StartExec 后用 sleep_ms(WASM 友好) 轮询 GetExecResult,MAX_POLLS 兜底
mut 信号加 cfg_attr 放行 server 的 unused_mut(AGENTS.md 约定)
提前克隆 source/language 给 move 闭包,避免 rsx! 二次借用 (E0382)
- execute.rs: 修正双目标可见性——server fn 模块不能 cfg-gate
共享类型 use 双目标可见,server-only 辅助 use 单独 gate(对齐 posts 模块约定)
- mod.rs: execute 不再 gate,languages/progress 仍 gate
2026-07-03 18:28:52 +08:00
9fe128d44d
feat(api): implement StartExec and GetExecResult server functions
...
- rate_limit.rs: 新增 CODE_EXEC 双层限流(per-second burst + 每日总额)
governor 0.8 无 per_day,用 with_period(24h)+allow_burst 模拟
- execute.rs: StartExec(IP 提取/语言白名单/源码大小/入队/并发信号量/clamp/容器执行)
+ GetExecResult(轮询读取 DashMap);系统异常脱敏,日志记详情
- .env.example: RATE_LIMIT_CODE_EXEC_PER_SEC 改为整数(governor 不支持小数)
2026-07-03 18:15:59 +08:00
3f3bbf284c
feat(api): add language registry and markdown fence info parser
2026-07-03 18:11:43 +08:00
9baa9da62f
feat(api): implement in-memory task registry with gc
2026-07-03 18:10:20 +08:00
60fd6378b6
feat(api): define shared serialized structs for code runner
...
Co-authored-by: plan docs/superpowers/plans/2026-07-03-code-runner.md Task 3
2026-07-03 18:09:27 +08:00