mirror of
https://github.com/BookStackApp/BookStack.git
synced 2026-07-15 21:31:36 +03:00
Attachments: Added more extensive URL filtering
Added a central URLFilter class to check & clean URLs used for attachments, which is also used for validation, and by the purifier to standardise protocols (and to make protocol config easier in future). Thanks to mfk25 for reporting.
This commit is contained in:
@@ -315,6 +315,34 @@ class AttachmentTest extends TestCase
|
||||
}
|
||||
}
|
||||
|
||||
public function test_existing_data_and_js_links_do_not_render_link()
|
||||
{
|
||||
$this->asAdmin();
|
||||
$page = $this->entities->page();
|
||||
$attachment = Attachment::factory()->create(['uploaded_to' => $page->id]);
|
||||
|
||||
$links = [
|
||||
'javascript:alert("bunny")',
|
||||
' javascript:alert("bunny")',
|
||||
'JavaScript:alert("bunny")',
|
||||
"\t\n\t\nJavaScript:alert(\"bunny\")",
|
||||
'data:text/html;bunny<a></a>',
|
||||
'Data:text/html;bunny<a></a>',
|
||||
'Data:text/html;bunny<a></a>',
|
||||
'donk\tscript:alert("bunny")',
|
||||
"donk\tscript:alert('bunny')",
|
||||
];
|
||||
|
||||
foreach ($links as $link) {
|
||||
$attachment->path = $link;
|
||||
$attachment->save();
|
||||
|
||||
$resp = $this->get($page->getUrl());
|
||||
$resp->assertDontSee('bunny', false);
|
||||
$resp->assertSee('#badlink', false);
|
||||
}
|
||||
}
|
||||
|
||||
public function test_attachment_delete_only_shows_with_permission()
|
||||
{
|
||||
$this->asAdmin();
|
||||
|
||||
71
tests/Util/UrlFilterTest.php
Normal file
71
tests/Util/UrlFilterTest.php
Normal file
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Util;
|
||||
|
||||
use BookStack\Util\UrlFilter;
|
||||
use Tests\TestCase;
|
||||
|
||||
class UrlFilterTest extends TestCase
|
||||
{
|
||||
public function test_it_finds_invalid_urls()
|
||||
{
|
||||
$urls = [
|
||||
'javascript:alert("bunny")',
|
||||
' javascript:alert("bunny")',
|
||||
'JavaScript:alert("bunny")',
|
||||
"\t\n\t\nJavaScript:alert(\"bunny\")",
|
||||
'data:text/html;bunny<a></a>',
|
||||
'Data:text/html;bunny<a></a>',
|
||||
'Data:text/html;bunny<a></a>',
|
||||
"http://example.com\0javascript:alert(1)",
|
||||
];
|
||||
|
||||
foreach ($urls as $url) {
|
||||
$filter = new UrlFilter($url);
|
||||
$this->assertFalse($filter->isAllowed(), "Failed to detect invalid url: {$url}");
|
||||
}
|
||||
}
|
||||
|
||||
public function test_clean()
|
||||
{
|
||||
$expectedOutputByInput = [
|
||||
'javascript:alert("bunny")' => '#badlink',
|
||||
' javascript:alert("bunny")' => '#badlink',
|
||||
'JavaScript:alert("bunny")' => '#badlink',
|
||||
"\t\n\t\nJavaScript:alert(\"bunny\")" => '#badlink',
|
||||
'data:text/html;bunny<a></a>' => '#badlink',
|
||||
'Data:text/html;bunny<a></a>' => '#badlink',
|
||||
'Data:text/html;bunny<a></a>' => '#badlink',
|
||||
"http://example.com\0javascript:alert(1)" => '#badlink',
|
||||
"Java\tScript:alert(\"bunny\")" => '#badlink',
|
||||
|
||||
'https://example.com' => 'https://example.com',
|
||||
'https://example.com/a/b' => 'https://example.com/a/b',
|
||||
'https://example.com/a/b?a=b#ab' => 'https://example.com/a/b?a=b#ab',
|
||||
'https://example.com/a/b?a=b#ab&c=d' => 'https://example.com/a/b?a=b#ab&c=d',
|
||||
'https://example.com:5050' => 'https://example.com:5050',
|
||||
'https://example.com:5050/a/b' => 'https://example.com:5050/a/b',
|
||||
'https://example.com:5050/a/b?a=b#ab' => 'https://example.com:5050/a/b?a=b#ab',
|
||||
'https://user@example.com:5011/a/b?a=b' => 'https://user@example.com:5011/a/b?a=b',
|
||||
'https://user:pass@example.com:5011/a/b?a=b' => 'https://user:pass@example.com:5011/a/b?a=b',
|
||||
|
||||
'//example.com' => 'https://example.com',
|
||||
'a/b/c' => 'a/b/c',
|
||||
'/a/b/c' => '/a/b/c',
|
||||
|
||||
'tel:123456789' => 'tel:123456789',
|
||||
'TEL:123456789' => 'tel:123456789',
|
||||
'maiLto:a@b.c' => 'mailto:a@b.c',
|
||||
'file://a/b/c' => 'file://a/b/c',
|
||||
'ftp://a/b/c' => 'ftp://a/b/c',
|
||||
'nntp://a/b/c' => 'nntp://a/b/c',
|
||||
'news:a/b/c' => 'news:a/b/c',
|
||||
];
|
||||
|
||||
foreach ($expectedOutputByInput as $input => $expected) {
|
||||
$filter = new UrlFilter($input);
|
||||
$output = $filter->clean();
|
||||
$this->assertEquals($expected, $output, "Failed to clean url: {$input}");
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user