83 lines
2.9 KiB
PHP
83 lines
2.9 KiB
PHP
|
|
<?php
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 文件功能:后台更新日志安全测试
|
||
|
|
* 验证广播 payload 与系统消息在发布时会对危险标题进行转义。
|
||
|
|
*/
|
||
|
|
|
||
|
|
namespace Tests\Feature\Feature;
|
||
|
|
|
||
|
|
use App\Events\ChangelogPublished;
|
||
|
|
use App\Jobs\SaveMessageJob;
|
||
|
|
use App\Models\User;
|
||
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||
|
|
use Illuminate\Support\Facades\Event;
|
||
|
|
use Illuminate\Support\Facades\Queue;
|
||
|
|
use Tests\TestCase;
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 后台更新日志安全测试
|
||
|
|
* 负责回归标题进入广播和系统公告前会被安全处理。
|
||
|
|
*/
|
||
|
|
class AdminChangelogControllerSecurityTest extends TestCase
|
||
|
|
{
|
||
|
|
use RefreshDatabase;
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 测试发布更新日志时会对广播字段与系统消息正文做 XSS 防护。
|
||
|
|
*/
|
||
|
|
public function test_published_changelog_escapes_title_for_broadcast_and_system_message(): void
|
||
|
|
{
|
||
|
|
Event::fake([ChangelogPublished::class]);
|
||
|
|
Queue::fake();
|
||
|
|
|
||
|
|
$admin = User::factory()->create([
|
||
|
|
'id' => 1,
|
||
|
|
'user_level' => 100,
|
||
|
|
]);
|
||
|
|
|
||
|
|
$dangerousTitle = '<img src=x onerror=alert(1)>';
|
||
|
|
$dangerousVersion = '2026-04-" onclick="alert(2)';
|
||
|
|
|
||
|
|
$response = $this->actingAs($admin)->post(route('admin.changelogs.store'), [
|
||
|
|
'version' => $dangerousVersion,
|
||
|
|
'title' => $dangerousTitle,
|
||
|
|
'type' => 'fix',
|
||
|
|
'content' => '修复说明',
|
||
|
|
'is_published' => '1',
|
||
|
|
'notify_chat' => '1',
|
||
|
|
]);
|
||
|
|
|
||
|
|
$response->assertRedirect(route('admin.changelogs.index'));
|
||
|
|
$this->assertDatabaseHas('dev_changelogs', [
|
||
|
|
'title' => $dangerousTitle,
|
||
|
|
'version' => $dangerousVersion,
|
||
|
|
'is_published' => true,
|
||
|
|
]);
|
||
|
|
|
||
|
|
Event::assertDispatched(ChangelogPublished::class, function (ChangelogPublished $event) use ($dangerousTitle, $dangerousVersion) {
|
||
|
|
$payload = $event->broadcastWith();
|
||
|
|
|
||
|
|
$this->assertSame($dangerousTitle, $payload['title']);
|
||
|
|
$this->assertSame(e($dangerousTitle), $payload['safe_title']);
|
||
|
|
$this->assertSame(e($dangerousVersion), $payload['safe_version']);
|
||
|
|
$this->assertStringContainsString(rawurlencode($dangerousVersion), $payload['url']);
|
||
|
|
$this->assertStringNotContainsString('" onclick="', $payload['url']);
|
||
|
|
|
||
|
|
return true;
|
||
|
|
});
|
||
|
|
|
||
|
|
Queue::assertPushed(SaveMessageJob::class, function (SaveMessageJob $job) use ($dangerousTitle, $dangerousVersion) {
|
||
|
|
$content = $job->messageData['content'] ?? '';
|
||
|
|
|
||
|
|
$this->assertStringContainsString(e($dangerousTitle), $content);
|
||
|
|
$this->assertStringContainsString(e($dangerousVersion), $content);
|
||
|
|
$this->assertStringContainsString('rel="noopener"', $content);
|
||
|
|
$this->assertStringContainsString(rawurlencode($dangerousVersion), $content);
|
||
|
|
$this->assertStringNotContainsString($dangerousTitle, $content);
|
||
|
|
|
||
|
|
return true;
|
||
|
|
});
|
||
|
|
}
|
||
|
|
}
|