21 Commits

Author SHA1 Message Date
xfy
db74a96de1 style: 格式化 Rust 与 TS 代码 2026-07-23 13:34:27 +08:00
xfy
6276bab6c6 fix(sanitizer): 允许 KaTeX 渲染所需的 svg 与 path 标签及绘图属性
Some checks failed
CI / check (push) Has been cancelled
CI / build (push) Has been cancelled
KaTeX 在 SSR 渲染根号(\sqrt)、矩阵竖线/括号(\begin{pmatrix})、大括号、上/下括号、箭头等复杂公式时,通过 <svg> 和 <path> 绘制线条与图标。

原先 HTML 消毒器 (sanitizer) 的标签白名单中未包含 svg / path,导致 lol_html 过滤正文与评论 HTML 时将 <svg> 和 <path> 剥离,致使根号顶线与矩阵括号丢失。

改动:
1. DEFAULT_ALLOWED_TAGS 增加 svg / path 标签放行
2. Sanitizer 属性白名单放行 svg (xmlns, width, height, viewbox, preserveaspectratio, style) 与 path (d) 绘图属性
3. 补充 sanitizer 与 markdown 单元测试防御回归
2026-07-23 13:27:13 +08:00
xfy
6fee07df1f feat(markdown): 文章页脚注完整支持(语义化 + back-link + 样式)
切换到 pulldown-cmark 的 GFM 脚注模式(与 GitHub 行为一致),并用自定义
事件渲染器替换默认输出,补齐语义化结构与无障碍属性:

- 解析:Options::all() 同时置 ENABLE_OLD_FOOTNOTES(bit 9|2),使
  has_gfm_footnotes() 返回 false 走 OLD 模式。remove(OLD) 会连
  ENABLE_FOOTNOTES 一起清掉(OLD 位掩码含 bit 2),故 remove 后须
  insert(ENABLE_FOOTNOTES) 单独加回。
- 渲染:事件循环新增 FootnoteReference / Start/End FootnoteDefinition
  三个分支。引用处 <sup class="fn-ref" id="fnref:{label}-{n}"> 带
  role="doc-noteref";定义处 <aside role="doc-footnote"
  aria-labelledby="..."> 取代默认 <div>;End 处按引用次数输出 N 个
  back-link(↩、↩²、↩³…)指向各自引用位置,role="doc-backlink"。
- 编号:按 label 首次出现顺序分配(1,2,3…),与定义位置无关。第一遍
  遍历顺带统计(复用现有两遍遍历模式,零额外扫描成本)。
- id 安全:footnote_id() 保留 label 原文(不转拼音),仅把 ASCII 标点
  /空格转 -,确保 ref↔def 双向一致且不破坏 HTML 属性。

sanitizer:放行 <section> 与 aria-labelledby;修复 is_safe_url 对
含冒号锚点(如 #fn:label)的误判——fragment 内冒号不是 scheme 分隔符,
# 开头的 URL 优先按锚点放行。

input.css:仿 blockquote 视觉语言(左侧边框 + 次级文本色)补脚注样式,
复用 paper 色彩变量,暗色模式自动适配。
2026-07-17 10:57:40 +08:00
xfy
84f6223c68 feat(markdown): 评论支持数学公式渲染
- comments/markdown.rs: 开启 ENABLE_MATH,InlineMath/DisplayMath 事件
  渲染成 katex HTML 注入事件流(评论不用块级 <p> 包裹,保持紧凑)
- sanitizer.rs: 评论路径 span 白名单加 style(KaTeX 内联定位 style 需保留,
  与文章正文 sanitizer.rs:382 对齐)
- 新增 3 个评论公式测试(内联/块级/style 保留)

双 target 编译通过,24 个评论测试 + 36 个 sanitizer 测试全过。
2026-07-16 14:12:20 +08:00
xfy
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
xfy
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
xfy
1584e01425 chore(deps): update all cargo and pnpm dependencies to latest versions 2026-07-09 18:12:54 +08:00
xfy
0e8b5fd7b7 feat(ui): scan and mount code-runner component client-side in reader
采用 Dioxus vdom 友好的片段拆分(避免篡改 dangerous_inner_html 产物导致 hydration 冲突):
- post_content.rs: split_content_fragments 把 content_html 拆成 Html/Runnable 片段,
  Runnable 作为 <CodeRunner> 组件穿插渲染;HTML 实体解码还原源码;6 个单测
- markdown.rs: 可运行 pre 增加 data-source(转义后的原始源码), 供阅读器无损提取
- sanitizer.rs: 放行 pre 的 data-source 属性

(任务 7 的 data-source 增量一并在此提交)
2026-07-03 18:35:57 +08:00
xfy
a77a2e3502 feat(markdown): support rendering and sanitizing runnable pre code-blocks
- markdown.rs: CodeBlock 处理识别 `runnable` 标记 + 受支持语言,在 <pre> 上
  挂 data-runnable/data-lang/data-overrides;overrides JSON 经 escape_html 转义
  防属性注入;非可运行围栏的 language-xxx 改取纯语言 token(首个空白前)
- sanitizer.rs: clean_html 放行 <pre> 的 data-runnable/data-lang/data-overrides
- 新增 4 个 markdown 测试 + 2 个 sanitizer 测试覆盖转义/白名单/字段顺序
2026-07-03 18:19:26 +08:00
xfy
291189fb77 fix(sanitizer): 折叠嵌套 if 修复 clippy::collapsible_if
865871f 引入的 checkbox type 白名单逻辑用了嵌套 if,触发 rust 1.96
的 clippy::collapsible_if,导致 make clippy 直接失败。按 clippy 建议
合并为单个 if 链,行为不变。
2026-07-02 16:36:05 +08:00
xfy
865871f45c fix(markdown): 修复任务列表 checkbox 在前台被 sanitizer 剥离
编辑器端已挂 TaskList/TaskItem,但前台渲染走 pulldown-cmark,其输出的
<input type="checkbox"> 不在 sanitizer 白名单内,被 remove_and_keep_content
剥离,导致任务列表在前台只剩裸 <li> 文本。

- sanitizer: 白名单放开 input,但强制 type="checkbox" 属性值校验
  - attrs_to_remove 增加 input.type 值校验,非 checkbox 一律删除属性
  - element_handler 末尾兜底:缺 type 或非 checkbox 的 input 整标签移除
    (void 元素无内容,remove() 不丢正文,封堵 type=image/text 等 XSS 滥用)
- 仅 clean_html 放开,clean_comment_html 保持不放开(评论无需任务列表)
- input.css 补 .md-content 任务列表样式(pulldown-cmark 裸 li>input 结构,
  用 :has() 识别,不支持 :has() 的浏览器降级为普通列表仍可读)
- 新增 sanitizer 5 个测试(白名单/XSS 边界)+ markdown 1 个端到端测试
2026-07-02 13:22:37 +08:00
xfy
373498870a style: apply cargo fmt to workspace
Some checks failed
CI / check (push) Failing after 5m35s
CI / build (push) Has been skipped
2026-06-29 13:47:40 +08:00
xfy
4690b64a99 feat(sanitizer): allow data-src/class/style on img for blur-up 2026-06-22 17:57:05 +08:00
xfy
3d187382cc perf(sanitizer): staticize allowlists with LazyLock to avoid per-call allocation
default_allowed_tags / clean_content_tags / default_allowed_schemes
原本每次调用都新建 HashSet 并逐个 insert;sanitize() 还 clone 一份。
改为 LazyLock 静态集合,SanitizerConfig 直接持有 &'static 引用,
评论白名单 COMMENT_ALLOWED_TAGS 在默认集合上派生。18 个 sanitizer
测试全部通过,XSS/URL 过滤行为不变。
2026-06-18 11:23:30 +08:00
xfy
449a545886 security: fix critical issues from repository review
Some checks failed
CI / build (push) Has been cancelled
CI / check (push) Has been cancelled
P0 blockers:
- Fix migration numbering conflict and duplicate indexes
- Change comments.post_id FK to ON DELETE CASCADE
- Restrict public post detail endpoint to published posts only
- Fix rate-limiting IP extraction and fallback to ConnectInfo
- Harden HTML sanitizer: deny unknown URL schemes, restrict data URIs
- Remove session token from login response body
- Enforce image pixel/dimension limits on upload and serving

P1 high-risk:
- Validate uploads by magic bytes and decode GIF/WebP
- Add pagination/rate-limiting to search, tag posts, and comments
- Make first-admin registration and slug uniqueness check atomic
- HTML-escape comment author fields
- Improve HTML minify cache key and skip admin/error responses
- Add mobile navigation menu

P2 accessibility/quality:
- Associate form labels with inputs
- Key PostDetail article by slug to re-init scripts on navigation
- Improve image viewer keyboard accessibility
- Make theme toggle SSR-friendly and add aria-label
- Invalidate slug 404 cache on create and pending count on new comment
- Deduplicate tags case-insensitively

P3 cleanup:
- Remove unused tower-http dependency, expand make clean
- Configure DB pool timeouts and verified recycling
- Run background cleanup tasks immediately on startup
- Use SHA-256 for stable disk cache keys
- Log DB errors with Display instead of Debug
- Update README migration instructions

All tests pass (321), clippy clean, dx check clean.
2026-06-17 10:34:14 +08:00
xfy
28f0117f06 test(sanitizer): 补充 is_safe_url 分支测试
is_safe_url 是 HTML 消毒器的安全核心,原仅通过 clean_html 间接覆盖。
新增 9 个直接单元测试,锁定各分支契约:

- https/http 白名单通过
- javascript / vbscript scheme 拒绝(含大小写混淆)
- data URI 按 allow_data_uri 标志决定(文章正文 vs 评论)
- 相对路径与锚点片段通过
- 空字符串/纯空白视为安全
- mailto / tel / ftp 等其它白名单 scheme 通过
- 含空白的 scheme 名(混淆手法)被拒绝

共 17 个测试,全部通过。
2026-06-15 11:19:46 +08:00
xfy
26b012c40c docs(api, auth): 补充中文注释 2026-06-12 18:27:24 +08:00
xfy
4fe26f7eb3 test: improve unit test coverage and assertions
Some checks failed
CI / check (push) Failing after 15m51s
CI / build (push) Has been skipped
- Add tests for sanitizer, mime_to_ext, clean_tags, Theme::toggle
- Tighten assertions in highlight, markdown, post status classes
- Add boundary and XSS cases for comments, slug, rate_limit, webp
- Update Cargo.lock for serial_test dev-dependency
2026-06-12 18:13:51 +08:00
xfy
942ac853fe refactor: tighten module-level allow attributes 2026-06-12 17:26:46 +08:00
xfy
294d60afab style: format rust code
Some checks failed
CI / build (push) Has been cancelled
CI / check (push) Has been cancelled
2026-06-12 17:14:31 +08:00
xfy
a10bf8737c refactor(sanitizer): extract shared sanitizer module and migrate from ammonia to lol_html 2026-06-12 11:15:42 +08:00