validate([ 'commentBody' => ['nullable', 'string'], 'internal' => ['nullable', 'boolean'], 'parent_id' => [ 'nullable', 'integer', Rule::exists('helpdesk_comments', 'id')->where('ticket_id', $ticket->id), ], 'canned_response_id' => ['nullable', 'integer', Rule::exists('helpdesk_canned_responses', 'id')], 'attachments' => ['nullable', 'array'], 'attachments.*' => ['file', 'max:'.$maxUploadKilobytes, 'mimes:'.implode(',', $allowedMimes)], ]); $user = Auth::user(); abort_if($user === null, 403); $body = trim((string) ($validated['commentBody'] ?? '')); if ($body === '' && filled($validated['canned_response_id'] ?? null)) { $response = CannedResponse::query() ->where('is_active', true) ->findOrFail((int) $validated['canned_response_id']); $body = trim($response->body); } $body = $this->normaliseHtmlBody($renderer->render($body, $ticket, $user)); validator(['commentBody' => $body], [ 'commentBody' => ['required', 'string', 'min:2'], ])->validate(); $uploadedFiles = $request->file('attachments', []); $uploadedFiles = is_array($uploadedFiles) ? $uploadedFiles : [$uploadedFiles]; $tickets->comment( ticket: $ticket, user: $user, body: $body, internal: (bool) ($validated['internal'] ?? false) && $this->userCanCreateInternalNote($user), files: $uploadedFiles, parentId: filled($validated['parent_id'] ?? null) ? (int) $validated['parent_id'] : null, ); return redirect() ->route('helpdesk.tickets.show', $ticket) ->with('ticket-comment-added', filled($validated['parent_id'] ?? null) ? 'Reply posted.' : 'Comment posted.'); } /** * Keep supported formatting tags but strip scripts and unsafe inline handlers. */ private function normaliseHtmlBody(string $body): string { $body = preg_replace('/<\s*(script|style)[^>]*>.*?<\s*\/\s*\1\s*>/is', '', $body) ?: ''; $body = preg_replace('/\s+on[a-z]+\s*=\s*("[^"]*"|\'[^\']*\'|[^\s>]+)/i', '', $body) ?: ''; $body = preg_replace('/javascript\s*:/i', '', $body) ?: ''; return trim(strip_tags($body, '