fix: [paid] 裸标签泄漏——核心渲染器兜底剥离 + 恢复 dev 库插件状态

- 根因:dev 数据库 membership 插件被停用(PluginRecord enabled 为空),其 post.rendered 过滤器未运行,[paid] 标签对所有访客裸露;与缓存无关
- 已恢复 membership 启用并清页面缓存,实测游客看到 teaser
- 防御:PostContentRenderer 渲染后兜底剥离裸 [paid]/[/paid] 标签,即使会员插件被停用也不会泄漏到页面
- 回归测试:渲染结果永不含 [paid]
This commit is contained in:
ak
2026-08-12 17:48:55 +08:00
parent d7a9135456
commit c231f4af96
2 changed files with 21 additions and 1 deletions
+4 -1
View File
@@ -43,7 +43,10 @@ class PostContentRenderer
$html = $this->renderShortcodes($content, $post); $html = $this->renderShortcodes($content, $post);
// 插件钩子:付费内容过滤、AI 处理等 // 插件钩子:付费内容过滤、AI 处理等
return app(\App\Blog\Support\PluginManager::class)->applyFilters('post.rendered', $html, $post); $html = app(\App\Blog\Support\PluginManager::class)->applyFilters('post.rendered', $html, $post);
// 兜底:任何情况下不把裸 [paid] 标签输出到页面(如会员插件被停用时)
return preg_replace('/\[(\/?paid)\]/i', '', $html);
} }
/** /**
+17
View File
@@ -218,6 +218,23 @@ class MembershipFlowTest extends TestCase
->assertSee('隐藏付费块'); ->assertSee('隐藏付费块');
} }
public function test_rendered_paid_content_never_leaks_raw_tags(): void
{
$post = Post::create([
'title' => '渲染防泄漏',
'slug' => 'render-no-leak',
'content' => "公开\n\n[paid]\n隐藏内容\n[/paid]",
'content_format' => 'markdown',
'status' => 'published',
'published_at' => now(),
]);
$html = app(\App\Blog\Services\PostContentRenderer::class)->render($post);
$this->assertStringNotContainsString('[paid]', $html);
$this->assertStringNotContainsString('[/paid]', $html);
}
public function test_members_only_post_shows_paywall_to_guest(): void public function test_members_only_post_shows_paywall_to_guest(): void
{ {
$post = Post::create([ $post = Post::create([