Commit 9965af0d by luoqi

merge: fix/strip-html-double-encoded → test(双重编码剥不干净)

parents 7b0124bb 1fe0a5a9
Pipeline #3662 failed in 0 seconds
......@@ -136,7 +136,13 @@ function evalExpr(expr: DeriveExpr, row: Row): unknown {
* - 收尾清理连续空行/首尾空白;全部剥完只剩空白 → 返回 null(空壳不入库)
*/
export function stripHtml(input: string): string | null {
const text = input
// 单轮:剥标签 → 还原实体。实体还原可能**再露出字面标签** —— 宿主实测存在双重编码:
// '<p><span…>&lt;p&gt;阿斯蒂芬撒的发生&lt;/p&gt;</span></p>'
// 外层是真标签、内层是被转义的标签,剥一轮后 &lt;p&gt; 还原成 <p> 留在结果里。
// 故做**有界多轮**:还原后若又出现标签则再剥一轮,最多 2 轮 —— 不做无界循环,
// 避免构造出的畸形输入(每轮都产生新标签)把摄入卡死。
const once = (v: string): string =>
v
// 块级结束/换行标签先转成换行,保住段落边界
.replace(/<\s*br\s*\/?\s*>/gi, '\n')
.replace(/<\/\s*(p|div|li|tr|h[1-6]|blockquote)\s*>/gi, '\n')
......@@ -148,7 +154,13 @@ export function stripHtml(input: string): string | null {
.replace(/&gt;/gi, '>')
.replace(/&quot;/gi, '"')
.replace(/&#39;/g, "'")
.replace(/&amp;/gi, '&')
.replace(/&amp;/gi, '&');
let text = once(input);
// 第 2 轮只在确实又露出标签时才跑(绝大多数行一轮即净,不做无谓开销)
if (/<[^>]+>/.test(text)) text = once(text);
text = text
// 连续空行压成一个,首尾清干净
.replace(/[ \t]+\n/g, '\n')
.replace(/\n{2,}/g, '\n')
......
......@@ -168,5 +168,20 @@ describe('FRIDAY 回访摄入', () => {
test('&amp;lt; 不被二次解码成 <(实体还原顺序)', () => {
expect(stripHtml('&amp;lt;')).toBe('&lt;');
});
test('⭐ 双重编码:剥完外层标签、还原实体后又露出字面标签 → 再剥一轮', () => {
// 线上真实数据(customer_return_visit id=266151):外层是真标签,内层是被转义的标签。
// 首轮导入时这条**没被剥干净**,落库后仍是 "<p>阿斯蒂芬撒的发生</p>" —— 由此加的有界第二轮。
const real =
'<p><span style="color:#000000"><span style="font-size:13px">' +
'<span style="background-color:#ffffff">&lt;p&gt;阿斯蒂芬撒的发生&lt;/p&gt;</span></span></span></p>';
expect(stripHtml(real)).toBe('阿斯蒂芬撒的发生');
});
test('只剥两轮,不做无界循环(畸形输入不能卡死摄入)', () => {
// 每轮都能再生出标签的构造输入:两轮后仍有残留是可接受的,关键是**必然终止**
const nested = '&amp;amp;lt;p&amp;amp;gt;x';
expect(typeof stripHtml(nested)).toBe('string');
});
});
});
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment