use BookStack\App\Model;
use BookStack\Users\Models\HasCreatorAndUpdater;
+use BookStack\Util\HtmlContentFilter;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\MorphTo;
{
return "Comment #{$this->local_id} (ID: {$this->id}) for {$this->entity_type} (ID: {$this->entity_id})";
}
+
+ public function safeHtml(): string
+ {
+ return HtmlContentFilter::removeScriptsFromHtmlString($this->html ?? '');
+ }
}
toolbar: 'bold italic link bullist numlist',
content_style: getContentStyle(options),
file_picker_types: 'file',
+ valid_elements: 'p,a[href|title],ol,ul,li,strong,em,br',
file_picker_callback: filePickerCallback,
init_instance_callback(editor) {
addCustomHeadContent(editor.getDoc());
+@php
+ $commentHtml = $comment->safeHtml();
+@endphp
<div component="{{ $readOnly ? '' : 'page-comment' }}"
option:page-comment:comment-id="{{ $comment->id }}"
option:page-comment:comment-local-id="{{ $comment->local_id }}"
<a class="text-muted text-small" href="#comment{{ $comment->parent_id }}">@icon('reply'){{ trans('entities.comment_in_reply_to', ['commentId' => '#' . $comment->parent_id]) }}</a>
</p>
@endif
- {!! $comment->html !!}
+ {!! $commentHtml !!}
</div>
@if(!$readOnly && userCan('comment-update', $comment))
<form novalidate refs="page-comment@form" hidden class="content pt-s px-s block">
<div class="form-group description-input">
- <textarea refs="page-comment@input" name="html" rows="3" placeholder="{{ trans('entities.comment_placeholder') }}">{{ $comment->html }}</textarea>
+ <textarea refs="page-comment@input" name="html" rows="3" placeholder="{{ trans('entities.comment_placeholder') }}">{{ $commentHtml }}</textarea>
</div>
<div class="form-group text-right">
<button type="button" class="button outline" refs="page-comment@form-cancel">{{ trans('common.cancel') }}</button>
public function test_scripts_cannot_be_injected_via_comment_html()
{
- $this->asAdmin();
$page = $this->entities->page();
$script = '<script>const a = "script";</script><p onclick="1">My lovely comment</p>';
- $this->postJson("/comment/$page->id", [
+ $this->asAdmin()->postJson("/comment/$page->id", [
'html' => $script,
]);
$pageView->assertSee('<p>My lovely comment</p><p>updated</p>');
}
+ public function test_scripts_are_removed_even_if_already_in_db()
+ {
+ $page = $this->entities->page();
+ Comment::factory()->create([
+ 'html' => '<script>superbadscript</script><p onclick="superbadonclick">scriptincommentest</p>',
+ 'entity_type' => 'page', 'entity_id' => $page
+ ]);
+
+ $resp = $this->asAdmin()->get($page->getUrl());
+ $resp->assertSee('scriptincommentest', false);
+ $resp->assertDontSee('superbadscript', false);
+ $resp->assertDontSee('superbadonclick', false);
+ }
+
public function test_reply_comments_are_nested()
{
$this->asAdmin();