fix(vip): preserve safe HTML in VIP copy

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
kate522andClaude Opus 5 committed 2026-10-19 12:52:23 +08:00
1 parent 7a0445fc4a
commit 573040381d
15 files changed
+155 -118

No files matched your search

@@ -52,7 +52,7 @@ test('弹窗文案字段更名为会员介绍/开通会员更多内容并改为
// 会员介绍为单行 input(zibll text「一句话简介」);开通会员更多内容为 rows 4 的 HTML textarea。
assert.match(
vipSchemaJs,
/key: 'pay_user_vip_desc',\s*\n\s*label: '会员介绍',\s*\n\s*type: 'input',\s*\n\s*default: '开通VIP会员,享受会员专属折扣以及多项特权'/
/key: 'pay_user_vip_desc',\s*\n\s*label: '会员介绍',\s*\n\s*type: 'input',\s*\n\s*sanitize_type: 'html',\s*\n\s*default: '开通VIP会员,享受会员专属折扣以及多项特权'/
);
assert.match(
vipSchemaJs,
@@ -60,7 +60,7 @@ test('弹窗文案字段更名为会员介绍/开通会员更多内容并改为
);
assert.match(
schemaPhp,
/'key' => 'pay_user_vip_desc', 'label' => '会员介绍', 'type' => 'input', 'default' => '开通VIP会员,享受会员专属折扣以及多项特权'/
/'key' => 'pay_user_vip_desc', 'label' => '会员介绍', 'type' => 'input', 'sanitize_type' => 'html', 'default' => '开通VIP会员,享受会员专属折扣以及多项特权'/
);
assert.match(
schemaPhp,
@@ -141,7 +141,7 @@ test('文案默认值三处一致并对齐 zibll', () => {
assert.ok(schemaPhp.includes(`'default' => '${DESC_DEFAULT}'`));
assert.ok(schemaPhp.includes(`'default' => '${MORE_DEFAULT}'`));
// PaymentRuntime 运行时回退(get_all 不合并 schema 默认值,回退即实际默认值)。
assert.ok(paymentRuntime.includes(`$desc = sanitize_text_field((string) ($settings['pay_user_vip_desc'] ?? __('${DESC_DEFAULT}', 'aksrt')))`));
assert.ok(paymentRuntime.includes(`$desc = wp_kses_post((string) ($settings['pay_user_vip_desc'] ?? __('${DESC_DEFAULT}', 'aksrt')))`));
assert.ok(paymentRuntime.includes(`$more = (string) ($settings['pay_user_vip_more'] ?? '${MORE_DEFAULT}')`));
assert.ok(paymentRuntime.includes("$name1 = __('黄金会员', 'aksrt');"));
assert.ok(paymentRuntime.includes("$name2 = __('钻石会员', 'aksrt');"));
@@ -154,19 +154,41 @@ test('文案默认值三处一致并对齐 zibll', () => {
}
});
test('权益与卡密说明字段保存时保留 HTML', () => {
// 权益字段已并入「会员购买选项」的等级 Tab,标签统一为「会员权益 HTML」。
assert.ok(vipSchemaJs.includes("key: `pay_user_vip_${level}_equity`, label: '会员权益 HTML', type: 'textarea', sanitize_type: 'html'"));
assert.ok(schemaPhp.includes("['key' => 'pay_user_vip_' . $level . '_equity', 'label' => '会员权益 HTML', 'type' => 'textarea', 'sanitize_type' => 'html'"));
assert.ok(rechargeJs.includes("key: 'pay_vip_pass_charge_desc', label: '卡密兑换会员说明', type: 'textarea', sanitize_type: 'html'"));
assert.ok(schemaPhp.includes("'key' => 'pay_vip_pass_charge_desc', 'label' => '卡密兑换会员说明', 'type' => 'textarea', 'sanitize_type' => 'html'"));
test('会员等级名称、简介、续费和升级介绍都声明 HTML 净化', () => {
const levelFieldsJs = slice(vipSchemaJs, 'function vipLevelTabFields', 'export const vipSchema');
const levelFieldsPhp = slice(schemaPhp, 'private static function vip_level_tab_fields', 'private static function vip_product_defaults');
assert.match(levelFieldsJs, /pay_user_vip_\$\{level\}_name[^\n]*sanitize_type: 'html'/);
assert.match(levelFieldsJs, /pay_user_vip_\$\{level\}_desc[^\n]*sanitize_type: 'html'/);
assert.match(levelFieldsPhp, /pay_user_vip_' \. \$level \. '_name'[^\n]*'sanitize_type' => 'html'/);
assert.match(levelFieldsPhp, /pay_user_vip_' \. \$level \. '_desc'[^\n]*'sanitize_type' => 'html'/);
assert.match(vipSchemaJs, /key: 'vip_renew_desc',[\s\S]*?sanitize_type: 'html'/);
assert.match(schemaPhp, /'key' => 'vip_renew_desc'[^\n]*'sanitize_type' => 'html'/);
assert.match(vipSchemaJs, /key: 'vip_upgrade_desc',[^\n]*sanitize_type: 'html'/);
assert.match(schemaPhp, /'key' => 'vip_upgrade_desc'[^\n]*'sanitize_type' => 'html'/);
assert.match(vipSchemaJs, /key: 'pay_user_vip_desc',[\s\S]*?sanitize_type: 'html'/);
assert.match(schemaPhp, /'key' => 'pay_user_vip_desc'[^\n]*'sanitize_type' => 'html'/);
});
test('会员介绍运行时按单行文本净化', () => {
// 只收敛 VIP 设置函数:充值等其它设置的 $desc 与本文无关。
test('权益和促销标签 HTML 通过安全净化及 HTML 渲染保留', () => {
const vipSettings = slice(paymentRuntime, 'public static function get_payment_vip_settings', 'public static function get_payment_revenue_settings');
assert.ok(vipSettings.includes("$desc = sanitize_text_field((string) ($settings['pay_user_vip_desc']"));
assert.ok(!vipSettings.includes('$desc = sanitize_textarea_field'));
assert.match(vipSettings, /'equity'\s*=> wp_kses_post\(/);
assert.doesNotMatch(vipSettings, /\$allowed_equity/);
const equityHelper = read('inc', 'helpers', 'aksrt-vip.php');
assert.match(equityHelper, /\$clean = trim\(wp_kses_post\(\$raw\)\)/);
assert.match(payVipJs, /if \(item\.discount_tag\) append\(product, htmlNode\(/);
assert.match(payVipJs, /append\(info, htmlNode\("div", "", levelInfo\.equity\)\)/);
assert.match(payVipJs, /htmlNode\("div", "v-name", levelInfo\.name \|\| group\.name/);
assert.match(payVipJs, /htmlNode\("div", "v-desc em09", levelInfo\.desc/);
assert.match(vipTabPhp, /wp_kses_post\(\$card_desc\)/);
});
test('会员介绍按 post 级 HTML 安全净化并在弹窗与用户中心渲染', () => {
const vipSettings = slice(paymentRuntime, 'public static function get_payment_vip_settings', 'public static function get_payment_revenue_settings');
assert.match(vipSettings, /\$desc = wp_kses_post\(\(string\) \(\$settings\['pay_user_vip_desc'\]/);
assert.match(vipSettings, /'desc'\s+=> wp_kses_post\(\(string\) \(\$settings\['pay_user_vip_1_desc'\]/);
assert.match(vipSettings, /'desc'\s+=> wp_kses_post\(\(string\) \(\$settings\['pay_user_vip_2_desc'\]/);
assert.match(vipTabPhp, /wp_kses_post\(\$vip_description\)/);
assert.match(payVipJs, /htmlNode\("div", "muted-2-color em09", catalog\.desc\)/);
});
test('开通会员更多内容只在弹窗内渲染,不裸露在用户中心页面', () => {