mirror of
https://github.com/BookStackApp/BookStack.git
synced 2026-05-04 18:08:46 +03:00
From testing, don't think this could exploited directly, as the response would error instead of allowing control characters, but this adds an extra layer of sanitization, and switches to encoded disposition filenames for better UTF8 support.
210 lines
7.3 KiB
PHP
210 lines
7.3 KiB
PHP
<?php
|
|
|
|
namespace Tests\Api;
|
|
|
|
use BookStack\Entities\Models\Book;
|
|
use BookStack\Entities\Models\Chapter;
|
|
use Tests\Exports\ZipTestHelper;
|
|
use Tests\TestCase;
|
|
|
|
class ExportsApiTest extends TestCase
|
|
{
|
|
use TestsApi;
|
|
|
|
public function test_book_html_endpoint()
|
|
{
|
|
$this->actingAsApiEditor();
|
|
$book = $this->entities->book();
|
|
|
|
$resp = $this->get("/api/books/{$book->id}/export/html");
|
|
$resp->assertStatus(200);
|
|
$resp->assertSee($book->name);
|
|
$resp->assertHeader('Content-Disposition', 'attachment; filename*=UTF-8\'\'' . $book->slug . '.html');
|
|
}
|
|
|
|
public function test_book_plain_text_endpoint()
|
|
{
|
|
$this->actingAsApiEditor();
|
|
$book = $this->entities->book();
|
|
|
|
$resp = $this->get("/api/books/{$book->id}/export/plaintext");
|
|
$resp->assertStatus(200);
|
|
$resp->assertSee($book->name);
|
|
$resp->assertHeader('Content-Disposition', 'attachment; filename*=UTF-8\'\'' . $book->slug . '.txt');
|
|
}
|
|
|
|
public function test_book_pdf_endpoint()
|
|
{
|
|
$this->actingAsApiEditor();
|
|
$book = $this->entities->book();
|
|
|
|
$resp = $this->get("/api/books/{$book->id}/export/pdf");
|
|
$resp->assertStatus(200);
|
|
$resp->assertHeader('Content-Disposition', 'attachment; filename*=UTF-8\'\'' . $book->slug . '.pdf');
|
|
}
|
|
|
|
public function test_book_markdown_endpoint()
|
|
{
|
|
$this->actingAsApiEditor();
|
|
$book = Book::visible()->has('pages')->has('chapters')->first();
|
|
|
|
$resp = $this->get("/api/books/{$book->id}/export/markdown");
|
|
$resp->assertStatus(200);
|
|
$resp->assertHeader('Content-Disposition', 'attachment; filename*=UTF-8\'\'' . $book->slug . '.md');
|
|
$resp->assertSee('# ' . $book->name);
|
|
$resp->assertSee('# ' . $book->pages()->first()->name);
|
|
$resp->assertSee('# ' . $book->chapters()->first()->name);
|
|
}
|
|
|
|
public function test_book_zip_endpoint()
|
|
{
|
|
$this->actingAsApiEditor();
|
|
$book = Book::visible()->has('pages')->has('chapters')->first();
|
|
|
|
$resp = $this->get("/api/books/{$book->id}/export/zip");
|
|
$resp->assertStatus(200);
|
|
$resp->assertHeader('Content-Disposition', 'attachment; filename*=UTF-8\'\'' . $book->slug . '.zip');
|
|
|
|
$zip = ZipTestHelper::extractFromZipResponse($resp);
|
|
$this->assertArrayHasKey('book', $zip->data);
|
|
}
|
|
|
|
public function test_chapter_html_endpoint()
|
|
{
|
|
$this->actingAsApiEditor();
|
|
$chapter = $this->entities->chapter();
|
|
|
|
$resp = $this->get("/api/chapters/{$chapter->id}/export/html");
|
|
$resp->assertStatus(200);
|
|
$resp->assertSee($chapter->name);
|
|
$resp->assertHeader('Content-Disposition', 'attachment; filename*=UTF-8\'\'' . $chapter->slug . '.html');
|
|
}
|
|
|
|
public function test_chapter_plain_text_endpoint()
|
|
{
|
|
$this->actingAsApiEditor();
|
|
$chapter = $this->entities->chapter();
|
|
|
|
$resp = $this->get("/api/chapters/{$chapter->id}/export/plaintext");
|
|
$resp->assertStatus(200);
|
|
$resp->assertSee($chapter->name);
|
|
$resp->assertHeader('Content-Disposition', 'attachment; filename*=UTF-8\'\'' . $chapter->slug . '.txt');
|
|
}
|
|
|
|
public function test_chapter_pdf_endpoint()
|
|
{
|
|
$this->actingAsApiEditor();
|
|
$chapter = $this->entities->chapter();
|
|
|
|
$resp = $this->get("/api/chapters/{$chapter->id}/export/pdf");
|
|
$resp->assertStatus(200);
|
|
$resp->assertHeader('Content-Disposition', 'attachment; filename*=UTF-8\'\'' . $chapter->slug . '.pdf');
|
|
}
|
|
|
|
public function test_chapter_markdown_endpoint()
|
|
{
|
|
$this->actingAsApiEditor();
|
|
$chapter = Chapter::visible()->has('pages')->first();
|
|
|
|
$resp = $this->get("/api/chapters/{$chapter->id}/export/markdown");
|
|
$resp->assertStatus(200);
|
|
$resp->assertHeader('Content-Disposition', 'attachment; filename*=UTF-8\'\'' . $chapter->slug . '.md');
|
|
$resp->assertSee('# ' . $chapter->name);
|
|
$resp->assertSee('# ' . $chapter->pages()->first()->name);
|
|
}
|
|
|
|
public function test_chapter_zip_endpoint()
|
|
{
|
|
$this->actingAsApiEditor();
|
|
$chapter = Chapter::visible()->has('pages')->first();
|
|
|
|
$resp = $this->get("/api/chapters/{$chapter->id}/export/zip");
|
|
$resp->assertStatus(200);
|
|
$resp->assertHeader('Content-Disposition', 'attachment; filename*=UTF-8\'\'' . $chapter->slug . '.zip');
|
|
|
|
$zip = ZipTestHelper::extractFromZipResponse($resp);
|
|
$this->assertArrayHasKey('chapter', $zip->data);
|
|
}
|
|
|
|
public function test_page_html_endpoint()
|
|
{
|
|
$this->actingAsApiEditor();
|
|
$page = $this->entities->page();
|
|
|
|
$resp = $this->get("/api/pages/{$page->id}/export/html");
|
|
$resp->assertStatus(200);
|
|
$resp->assertSee($page->name);
|
|
$resp->assertHeader('Content-Disposition', 'attachment; filename*=UTF-8\'\'' . $page->slug . '.html');
|
|
}
|
|
|
|
public function test_page_plain_text_endpoint()
|
|
{
|
|
$this->actingAsApiEditor();
|
|
$page = $this->entities->page();
|
|
|
|
$resp = $this->get("/api/pages/{$page->id}/export/plaintext");
|
|
$resp->assertStatus(200);
|
|
$resp->assertSee($page->name);
|
|
$resp->assertHeader('Content-Disposition', 'attachment; filename*=UTF-8\'\'' . $page->slug . '.txt');
|
|
}
|
|
|
|
public function test_page_pdf_endpoint()
|
|
{
|
|
$this->actingAsApiEditor();
|
|
$page = $this->entities->page();
|
|
|
|
$resp = $this->get("/api/pages/{$page->id}/export/pdf");
|
|
$resp->assertStatus(200);
|
|
$resp->assertHeader('Content-Disposition', 'attachment; filename*=UTF-8\'\'' . $page->slug . '.pdf');
|
|
}
|
|
|
|
public function test_page_markdown_endpoint()
|
|
{
|
|
$this->actingAsApiEditor();
|
|
$page = $this->entities->page();
|
|
|
|
$resp = $this->get("/api/pages/{$page->id}/export/markdown");
|
|
$resp->assertStatus(200);
|
|
$resp->assertSee('# ' . $page->name);
|
|
$resp->assertHeader('Content-Disposition', 'attachment; filename*=UTF-8\'\'' . $page->slug . '.md');
|
|
}
|
|
|
|
public function test_page_zip_endpoint()
|
|
{
|
|
$this->actingAsApiEditor();
|
|
$page = $this->entities->page();
|
|
|
|
$resp = $this->get("/api/pages/{$page->id}/export/zip");
|
|
$resp->assertStatus(200);
|
|
$resp->assertHeader('Content-Disposition', 'attachment; filename*=UTF-8\'\'' . $page->slug . '.zip');
|
|
|
|
$zip = ZipTestHelper::extractFromZipResponse($resp);
|
|
$this->assertArrayHasKey('page', $zip->data);
|
|
}
|
|
|
|
public function test_cant_export_when_not_have_permission()
|
|
{
|
|
$types = ['html', 'plaintext', 'pdf', 'markdown', 'zip'];
|
|
$this->actingAsApiEditor();
|
|
$this->permissions->removeUserRolePermissions($this->users->editor(), ['content-export']);
|
|
|
|
$book = $this->entities->book();
|
|
foreach ($types as $type) {
|
|
$resp = $this->get("/api/books/{$book->id}/export/{$type}");
|
|
$this->assertPermissionError($resp);
|
|
}
|
|
|
|
$chapter = Chapter::visible()->has('pages')->first();
|
|
foreach ($types as $type) {
|
|
$resp = $this->get("/api/chapters/{$chapter->id}/export/{$type}");
|
|
$this->assertPermissionError($resp);
|
|
}
|
|
|
|
$page = $this->entities->page();
|
|
foreach ($types as $type) {
|
|
$resp = $this->get("/api/pages/{$page->id}/export/{$type}");
|
|
$this->assertPermissionError($resp);
|
|
}
|
|
}
|
|
}
|