diff --git a/app/Exports/ZipExports/Models/ZipExportAttachment.php b/app/Exports/ZipExports/Models/ZipExportAttachment.php index 97995738f..88c20e4d3 100644 --- a/app/Exports/ZipExports/Models/ZipExportAttachment.php +++ b/app/Exports/ZipExports/Models/ZipExportAttachment.php @@ -45,7 +45,7 @@ final class ZipExportAttachment extends ZipExportModel $rules = [ 'id' => ['nullable', 'int', $context->uniqueIdRule('attachment')], 'name' => ['required', 'string', 'min:1'], - 'link' => ['required_without:file', 'nullable', 'string'], + 'link' => ['required_without:file', 'nullable', 'string', 'safe_url'], 'file' => ['required_without:link', 'nullable', 'string', $context->fileReferenceRule()], ]; diff --git a/tests/Exports/ZipExportValidatorTest.php b/tests/Exports/ZipExportValidatorTest.php index c453ef294..e801705be 100644 --- a/tests/Exports/ZipExportValidatorTest.php +++ b/tests/Exports/ZipExportValidatorTest.php @@ -90,4 +90,29 @@ class ZipExportValidatorTest extends TestCase $this->assertEquals('The file needs to reference a file of type image/png,image/jpeg,image/gif,image/webp, found text/plain.', $results['page.images.0.file']); } + + public function test_page_link_attachments_cant_be_data_or_js() + { + $validateResultCountByLink = [ + 'data:text/html,

hi

' => 1, + 'javascript:alert(\'hi\')' => 1, + 'mailto:email@example.com' => 0, + ]; + + foreach ($validateResultCountByLink as $link => $count) { + $validator = $this->getValidatorForData([ + 'page' => [ + 'id' => 4, + 'name' => 'My page', + 'markdown' => 'hello', + 'attachments' => [ + ['id' => 4, 'name' => 'Attachment A', 'link' => $link], + ], + ] + ]); + + $results = $validator->validate(); + $this->assertCount($count, $results); + } + } }