Overleaf checkSanitize 开发脚本解析:用 MediaWiki parse API 校验 Learn 页面 HTML 经 sanitize-html 净化后的一致性

发布时间:2026/9/13 7:59:57
Overleaf checkSanitize 开发脚本解析:用 MediaWiki parse API 校验 Learn 页面 HTML 经 sanitize-html 净化后的一致性 Overleaf checkSanitize 开发脚本解析用 MediaWiki parse API 校验 Learn 页面 HTML 经 sanitize-html 净化后的一致性【免费下载链接】overleafA web-based collaborative LaTeX editor项目地址: https://gitcode.com/GitHub_Trending/ov/overleaf本文聚焦 Overleaf 仓库中的开发辅助脚本checkSanitize它从 Overleaf Learn 支持 WikiMediaWiki拉取全部页面模拟 Web 服务对页面 HTML 执行的 sanitize-html 净化流程并逐页比净化前后差异帮助开发者在调整净化配置时及时发现会被“误伤”的合法标记。读完本文你将掌握该脚本的运行方式、数据抓取机制、诊断输出字段的含义以及为什么它选择 MediaWiki parse API 而非批量导出bulk export作为数据源。脚本要解决的问题Overleaf 的 Learn支持文档/Wiki页面内容托管在 MediaWiki 上Web 服务渲染前会把这些页面 HTML 交给sanitize-html做净化防止不受信任的标记进入页面。净化配置allowlist过严就可能把 Wiki 中合法使用的元素“洗掉”或改写导致线上页面与 Wiki 上看到的预览不一致。checkSanitize就是这个差异检测器。从 checkSanitizeOptions.mjs 源码中的注释可以看到checkSanitizeOptions is only used in dev env——它是一个仅面向开发环境的校验工具而非线上运行时组件。其核心判断逻辑在 checkSanitizeOptions.mjstext normalize(text, title) const sanitized normalize(sanitizeHtml(text, sanitizeOptions)) if (text sanitized) return // 净化前后一致静默通过页面净化前后字符串完全一致即通过、不输出任何内容一旦不一致就打印一段诊断块见后文“诊断输出”一节。脚本引用的共享净化配置路径为modules/learn/app/src/sanitizeOptions.mjs相对 checkSanitizeOptions.mjs 的导入。需要说明从当前仓库快照的源码结构看services/web/modules/下仅包含 full-project-search、history-v1、launchpad、server-ce-scripts、user-activate 等目录未见该文件实体可推断此脚本依赖的 Learn 模块配置随完整开发环境提供本文仅按脚本代码中的引用路径描述其作用。运行方式按 README 的说明脚本的调用方式为在services/web目录下node scripts/learn/checkSanitize/index.mjs https://LEARN_WIKI其中https://LEARN_WIKI是 Learn Wiki 站点的 Base URL需要替换为实际地址。index.mjs 对参数做了严格校验——最后一个命令行参数必须以http开头否则抛出带用法提示的错误const BASE_URL process.argv.pop() if (!BASE_URL.startsWith(http)) { throw new Error( Usage: node scripts/learn/checkSanitize/index.mjs https://LEARN_WIKI ) }主流程非常直接index.mjsgetAllPagesAndCache(BASE_URL)获取全站页面列表带本地缓存对每个页面scrapeAndCachePage(BASE_URL, page)拉取 parse API 结果取出parsed.title与parsed.text[*]MediaWiki parse 结果中*键对应正文 HTML调用checkSanitizeOptions(page, title, text)执行校验单页出错时先打印---分隔线与出错页名再向上抛出终止整个巡检。此外脚本通过 scriptRunner来自scripts/lib/ScriptRunner.mjs包裹执行它会把脚本运行信息OL_POD_NAME、OL_USERNAME、OL_IMAGE_VERSION等环境变量写入 MongoDB 的 ScriptLog 集合若设置了OL_USERNAME还会在控制台打印 admin 后台的 script-log 跟踪链接。也就是说这个巡检脚本的运行记录同样可被管理员追溯。数据抓取层parse API、分页与本地缓存抓取逻辑集中在 scrape.mjs它基于仓库内的overleaf/fetch-utilslibraries/fetch-utils/index.js完成 HTTP 请求并设计了本地缓存以避免重复请求 Wiki。页面正文MediaWiki parse APIscrape() 请求 Wiki 的api.phpconst uri new URL(baseUrl /learn-scripts/api.php) uri.search new URLSearchParams({ page, action: parse, format: json, redirects: true, }).toString()即actionparse解析单页并返回 HTML、formatjson、redirectstrue跟随重定向。scrapeAndCachePage() 先尝试读缓存文件data/learnPages/页名.json未命中才请求 API取响应中的parse对象若缺失则打印原始响应并抛出bad contents命中后把结果格式化写入缓存。页名转文件名时有一个实用的防御getName()MediaWiki 存在极长的页面标题直接 percent-encode 后可能超过文件系统文件名长度上限因此超过 100 字符时截断并追加页名的 SHA-1 哈希保证唯一且可落盘。页面列表generatorallpages 游标翻页getAllPagesFrom() 使用查询接口枚举全部页面actionquery、generatorallpages生成器模式列出所有页面gapfilterredirnonredirects过滤掉重定向页源码注释解释这是为了避免把同一内容的重定向页校验两遍gaplimit100把默认每页 10 条提升到 100 条减少往返...continueFrom透传游标。getAllPages() 依据响应中的continue字段循环翻页直到游标耗尽最后对页面列表做sort()使巡检顺序稳定可复现。getAllPagesAndCache() 进一步把整个列表缓存到data/learnPages/allPages.txt下次运行直接复用。所有缓存文件都落在services/web/data/learnPages/下。核心校验逻辑normalize、快速 diff 与诊断输出normalize消除无关差异sanitizeOptions 校验前 的 raw 文本会先经过 normalize() 规整目的是剔除那些“Web 端本来就会丢掉、但与净化配置无关”的差异让比对聚焦于真正的净化行为style块处理。Wiki 页面为预览保留style而 Web 端渲染时会丢弃。默认行为OMIT_STYLE未设为false时是把style整块删掉若设置环境变量EXTRACT_STYLEtrue会先用 prettierparser: css格式化该 CSS再以sha1(css)-encodeURIComponent(title).css的文件名写入data/dumpFolder/供人工检查checkSanitizeOptions.mjs。相关环境变量EXTRACT_STYLE取值true时提取并 dump 各页 CSSOMIT_STYLE非false默认时丢弃 style 块设为false则保留参与比对。注释剔除删除每页底部的!-- \nNewPP limit report...注释MediaWiki 的渲染统计以及数学字符标注产生的!-- . --空注释。一致包裹若输出不以htmlhead开头则包一层htmlhead…/head/html保证 cheerio 解析渲染一致。内联 style 归一去掉style…;的尾分号并把:、;后的空白规范化如margin: 1px→margin:1px。cheerio 重新序列化最后用cheerio.load(blob).html()再走一遍解析与序列化作为最终的 canonical 形式。净化结果sanitizeHtml(text, sanitizeOptions)之后也会再跑一遍normalize确保双方处于同一规范化口径再比较。快速定位首个不一致点不一致时findFirstMismatch() 以chunkSize 100为步长做整块前缀比较快速跳过相同前缀定位第一处分歧的偏移量。peak() 再围绕该偏移前后各取zoomOut 50字符的上下文并用JSON.stringify包装这样换行等控制字符会以\n等转义形式可见便于在终端对照。诊断输出字段一旦某页净化后发生变化脚本向 stderr 打印如下八行与 README 示例一致字段含义page/titleMediaWiki 页面名与标题便于直接定位回 WikimatchHTML 规范化后是否完全一致text sanitizedtoText去掉全部标签后的纯文本是否一致——即净化是否改变了用户可见文字text不一致点前后的原始 HTML 片段JSON 转义sanitized不一致点前后的净化后 HTML 片段textToText原始 HTML 对应的纯文本片段sanitizedToText净化后 HTML 对应的纯文本片段toText与 HTML 比对是双层防线即使标签被替换只要最终可见文字不变toText: true影响通常只是样式层面的反之若纯文本也变了说明净化直接吞掉了内容优先级更高。示例输出解读README 给出了一条真实诊断样例某支持页面里Wiki 作者用nowiki包裹了一段 URL 字面量。净化后nowiki标签被转义成lt;nowikigt;...lt;/nowikigt;于是match: false、toText: false——HTML 与可见文本都发生了变化对照text与sanitized两行能看到分歧恰好落在lt;nowikigt;处对照textToText与sanitizedToText两行还能直观看到转义导致纯文本在/nowiki位置提前“截断”成nowiki...的样子。README 特别提示Note the hidden/escaped nowiki element.——这个被隐藏的转义标签正是问题根源。整体体验上你看到的是“HTML 并排比对 纯文本 diff”两层信息原文you will see a plain-text diff可以快速判断是配置漏放行了某个标签还是 Wiki 标记本身依赖了不应出现的元素。为什么不用 MediaWiki 的 bulk exportREADME 专门说明了数据源选择的原因原文MediaWiki 有批量导出bulk export功能但它的 HTML 转义行为与 Web 服务实际使用的 parse API 不一致——bulk export 不会转义所有占位符形式的 HTML 样元素例如project-id或document goes here这类未闭合的伪标签。若用 bulk export 的数据做校验会产生与线上真实数据源parse API即actionparse不同的伪差异因此脚本统一走与 Web 渲染链路同源的 parse API保证“测的就是线上吃的数据”。小结与相关文件checkSanitize是一个典型的“配置回归检查”工具以 Wiki parse API 为唯一数据源用与线上一致的sanitizeOptions做净化通过 normalize 双层 diff 精确定位净化副作用并借助本地缓存与 ScriptLog 让巡检可重复、可追溯。适合在修改 sanitize 配置、Wiki 模板结构变化时运行一遍全站巡检。关键文件索引文件作用README.md用法、bulk export 局限与示例输出index.mjs入口参数校验、逐页巡检主循环checkSanitizeOptions.mjsnormalize、sanitizeHtml 比对、诊断打印scrape.mjsparse API / allpages 查询、缓存与翻页ScriptRunner.mjs脚本运行日志ScriptLog基础设施libraries/fetch-utils/index.jsfetchString/fetchJson/RequestFailedError适用前提该脚本面向开发环境源码注释明确标注 only used in dev env需要一个可达的 MediaWiki Learn 站点地址缓存目录services/web/data/learnPages、services/web/data/dumpFolder为运行产物首次运行会自动创建。【免费下载链接】overleafA web-based collaborative LaTeX editor项目地址: https://gitcode.com/GitHub_Trending/ov/overleaf创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考