byrcsc/laravel-comments · 1.x
Rendering and safety.
What the package treats as untrusted, what it deliberately does not do for you, and where each trust boundary sits.
The package stores what it was given and hands it back unchanged. That is a design decision with consequences, and this page is the list of them.
The body is stored verbatim
No sanitization, no markdown, no HTML filtering, no trimming. A body containing
<script>alert(1)</script> is stored as exactly that, and read back as exactly
that.
Sanitizing on the way in would be the package deciding what your application renders, and it would be wrong for at least half of them: a code-review tool wants angle brackets intact, a support desk wants the raw text for an export, and a markdown site wants the source rather than someone else's idea of safe HTML.
So escaping is yours, on output:
{{ $comment->body }}Blade escapes this. {!! !!} does not — and if you render markdown or HTML,
sanitize before you do.
Untrusted input
Treat all four of these as input from a stranger, because that is what they are:
| Value | Why |
|---|---|
$comment->body | Stored verbatim, never inspected |
$comment->guest_name | Whatever the form sent, verified by nothing |
$comment->guest_email | Never verified, never confirmed, never mailed |
$attachment->name | Whatever the caller recorded, defaults to a basename |
The revision bodies carry the same warning as the comment body: they are prior versions of untrusted input.
The shipped mail view escapes everything it interpolates. If you publish and edit it, keep it that way.
The guest email is not a mailbox
The package never sends anything to guest_email. It is recorded because your
application asked for it, and it is not treated as a verified address anywhere —
the reply notification skips guest-authored parents outright, whatever channel is
configured.
Verifying it, if you want to, is your application's job, and so is deciding whether to show it at all.
Attachments: the file is yours
An attachment row is metadata. The package never opens the file, never checks it exists, never validates its type, and never deletes it.
That means every one of these is your application's:
- Validating the upload. MIME type, size, extension, and content checks all
happen before
attach()is called. The row records what you tell it, and a row claimingapplication/pdfproves nothing about the bytes. - Serving the file. The package builds no URLs and defines no routes. A signed URL, a controller with an authorization check, or a public disk — all yours.
- Authorizing the download. An attachment on a pending or rejected comment is still a row with a readable path. Nothing hides it.
- Deleting the bytes. Removing a row fires
AttachmentRemoved; that is where the delete belongs. See attachments.
Guest writes are unthrottled
The package ships no rate limiting. An application that opens create to guests
in its policy owns the throttling, the spam checks, and the
captcha, because those are the checks that actually matter for anonymous writes
and none of them can be answered generically.
The package does give you the seams: guests default to pending so nothing
anonymous is visible until somebody looks at it, and markAsSpam() is where
your own detection records its verdict.
Status is not access control
Approving a comment does not publish it and rejecting one does not hide it. A
rejected comment is a readable row, and Comment::query()->get() will return
it.
Whatever you show a visitor comes from your query:
$post->comments()->approved()->topLevel()->get();Forgetting approved() on a public page shows pending and rejected comments to
everybody. That is the single easiest mistake to make with this package.
The package never authorizes itself
approve(), pin(), react(), edit(), and attach() all work with nobody
logged in and the harshest policy the framework allows registered. They are
engine methods, not endpoints. See authorization.
Enum casting is not validation
status casts to CommentStatus, so a status column holding an unknown string
will fail when the model hydrates it — not when it was written by raw SQL. Use
CommentStatus::values() in your form rules, and prefer the transition methods
over writing the column directly.