pi 的三条增量,前两条落地: 1. **错误信息自带修法**:受众不只是读过规范的人 —— 并发写 WebUI 的 agent 新加判据时不会打开 CRITERIA.md,看到红的第一反应可能是"套件坏了"。所以把可照抄的修法写进那条错误本身 (共享 helper 的用法 + 样板文件路径),并说明 node:test 的判据不用管。 **red 是 ta 一定会看到的,文档不一定被打开。** 2. **marker 由共享 helper 打印**:新增 test/lib/checks.mjs(导出 check/finish), 计数只可能在该模块内发生 → "漏打 marker"与"计数写错位置"这两类在新文件上不可能发生。 为避免"写了没人用"(本仓踩过的坑),同时把两个手工计数的判据改用它: narrow-layout(原来只有 failed 计数)与 markdown-xss(原来根本没有计数器)—— 条数不变(52 / 9),套件仍全绿。 未回改其余 10 个文件:run-all 的 marker 检查已经覆盖它们。
42 lines
1.9 KiB
JavaScript
42 lines
1.9 KiB
JavaScript
// 回归测试:确认邮件正文的 Markdown 渲染不会执行注入的脚本。
|
||
// react-markdown 默认不解析 raw HTML(无 rehype-raw),且用 defaultUrlTransform
|
||
// 清空非 http(s)/mailto 协议的 URL —— 本测试守住这两个前提,防止日后有人
|
||
// 为了「支持 HTML 邮件」顺手加上 rehype-raw 而不自觉地开了 XSS 口子。
|
||
//
|
||
// 运行:node test/markdown-xss.test.mjs
|
||
|
||
import { renderToStaticMarkup } from 'react-dom/server';
|
||
import React from 'react';
|
||
import Markdown from 'react-markdown';
|
||
import remarkGfm from 'remark-gfm';
|
||
import { check, finish } from './lib/checks.mjs';
|
||
|
||
// 只有【真实标签】里的危险内容才算漏。
|
||
// 注意不能直接搜 onerror=:raw HTML 被转义成 <img … onerror=" 后,
|
||
// 文本里仍含该字样但已无执行能力,按标签边界匹配才不会误报。
|
||
const dangerous = /<(script|iframe|object|embed)\b|<[a-z][^>]*\son[a-z]+\s*=|<[a-z][^>]*(href|src)\s*=\s*"javascript:/i;
|
||
|
||
const payloads = [
|
||
'<script>alert(1)</script>',
|
||
'<img src=x onerror="alert(1)">',
|
||
'[click](javascript:alert(1))',
|
||
'<a href="javascript:alert(1)">x</a>',
|
||
'<iframe src="https://evil.com"></iframe>',
|
||
')',
|
||
'<div onmouseover="alert(1)">hover</div>',
|
||
'[ok](https://example.com)',
|
||
'**bold** `code`',
|
||
];
|
||
|
||
// 每条 payload 就是一条判据(换来的是"跑了几条"可核对,而不是只报一句总括)
|
||
console.log('Markdown 注入回归:');
|
||
for (const p of payloads) {
|
||
const html = renderToStaticMarkup(
|
||
React.createElement(Markdown, { remarkPlugins: [remarkGfm] }, p)
|
||
);
|
||
const bad = dangerous.test(html);
|
||
console.log((bad ? 'LEAK ' : 'safe '), JSON.stringify(p), '->', html.slice(0, 80));
|
||
check(`危险内容被清除:${JSON.stringify(p)}`, !bad, `渲染结果里出现了真实可执行的危险标记:${html.slice(0, 120)}`);
|
||
}
|
||
finish('Markdown 注入');
|