em->clear(); return self::getContainer()->get(BlogRepository::class)->findByUuid($uuid)->getBody(); } public function testScriptTagIsStrippedOnCreate(): void { $admin = $this->createUser(['ROLE_ADMIN']); $res = $this->authJson('POST', '/api/v1/blog', $admin, [ 'title' => 'مقالهٔ تست', 'body' => '

سلام

', ]); $this->assertSame(201, $this->responseCode()); $body = $this->storedBody($res['data']['data']['uuid'] ?? $res['data']['uuid']); $this->assertStringNotContainsString('assertStringNotContainsString('alert(1)', $body); $this->assertStringContainsString('سلام', $body, 'متن سالم نباید حذف شود'); } public function testInlineHandlerAndJavascriptUrlAreStripped(): void { $admin = $this->createUser(['ROLE_ADMIN']); $res = $this->authJson('POST', '/api/v1/blog', $admin, [ 'title' => 'مقالهٔ تست', 'body' => '

متن

لینک' . '', ]); $this->assertSame(201, $this->responseCode()); $body = $this->storedBody($res['data']['data']['uuid'] ?? $res['data']['uuid']); $this->assertStringNotContainsString('onclick', $body); $this->assertStringNotContainsString('onerror', $body); $this->assertStringNotContainsString('javascript:', $body); $this->assertStringContainsString('متن', $body); } public function testSafeRichTextSurvives(): void { $admin = $this->createUser(['ROLE_ADMIN']); $html = '

عنوان

پررنگ و کج

' . '' . 'پیوند' . '
خانه
'; $res = $this->authJson('POST', '/api/v1/blog', $admin, [ 'title' => 'مقالهٔ تست', 'body' => $html, ]); $this->assertSame(201, $this->responseCode()); $body = $this->storedBody($res['data']['data']['uuid'] ?? $res['data']['uuid']); foreach (['assertStringContainsString($tag, $body, "عنصر مجاز {$tag} نباید حذف شود"); } $this->assertStringContainsString('https://example.com', $body); // لینک باید rel امن بگیرد، وگرنه tabnabbing باز می‌ماند. $this->assertStringContainsString('noopener', $body); } public function testUpdatePathIsSanitizedToo(): void { $admin = $this->createUser(['ROLE_ADMIN']); $res = $this->authJson('POST', '/api/v1/blog', $admin, [ 'title' => 'مقالهٔ تست', 'body' => '

اولیه

', ]); $uuid = $res['data']['data']['uuid'] ?? $res['data']['uuid']; $this->authJson('PATCH', "/api/v1/blog/{$uuid}", $admin, [ 'body' => '

ویرایش

', ]); $this->assertSame(200, $this->responseCode()); $body = $this->storedBody($uuid); $this->assertStringNotContainsString('assertStringContainsString('ویرایش', $body); } /** * جدول‌های مقاله‌های موجود ظاهرشان را از attributeهای قدیمیِ HTML می‌گیرند. * این سه غیرقابل‌اجرا هستند و از ۲۰۲۶-۰۸-۰۸ مجازند؛ `style` همچنان می‌رود چون * تنها attributeِ ظاهریِ جدول است که می‌تواند بارِ اجرایی حمل کند. */ public function testTableKeepsInertLayoutAttributesButLosesStyle(): void { $admin = $this->createUser(['ROLE_ADMIN']); $res = $this->authJson('POST', '/api/v1/blog', $admin, [ 'title' => 'مقالهٔ جدول', 'body' => '' . '
سلول
', ]); $uuid = $res['data']['data']['uuid'] ?? $res['data']['uuid']; $body = $this->storedBody($uuid); $this->assertStringContainsString('border="1"', $body); $this->assertStringContainsString('cellpadding="7"', $body); $this->assertStringContainsString('cellspacing="0"', $body); $this->assertStringNotContainsString('style=', $body); $this->assertStringContainsString('سلول', $body); } /** بدنه‌ای که چیزی جز markup ناامن ندارد، بعد از پاک‌سازی خالی است → ۴۲۲، نه ذخیره. */ public function testBodyThatIsOnlyUnsafeMarkupIsRejected(): void { $admin = $this->createUser(['ROLE_ADMIN']); $this->authJson('POST', '/api/v1/blog', $admin, [ 'title' => 'مقالهٔ تست', 'body' => '', ]); $this->assertSame(422, $this->responseCode()); } }