PluginProbe
Fluent Support – Helpdesk & Customer Support Ticket System / 2.4.0
Fluent Support – Helpdesk & Customer Support Ticket System v2.4.0
2.4.0 2.3.2 2.3.1 2.3.0 2.2.1 2.2.0 trunk 1.10.0 1.10.1 1.10.2 1.10.3 1.10.4 1.10.5 1.4.0 1.4.1 1.4.2 1.4.5 1.4.6 1.4.7 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 1.5.5 All 68 releases
← All changes | app/Http/Controllers/TicketController.php +16 -6 2.3.12.4.0 View file →
@@ -66,9 +66,9 @@
66 66 $businessSettings = (new \FluentSupport\App\Services\EmailNotification\Settings())->globalBusinessSettings();
67 67 $maxFileSize = absint($businessSettings['max_file_size']);
68 68
69 69 $portalSettings = [
70 - 'support_products' => \FluentSupport\App\Models\Product::select(['id', 'title'])->get(),
70 + 'support_products' => \FluentSupport\App\Models\Product::select(['id', 'title'])->orderedByTitle()->get(),
71 71 'customer_ticket_priorities' => Helper::customerTicketPriorities(),
72 72 'has_file_upload' => !!Helper::ticketAcceptedFileMiles(),
73 73 'has_rich_text_editor' => true,
74 74 'max_file_size' => $maxFileSize,
@@ -883,9 +883,9 @@
883 883
884 884 $propName = $request->getSafe('prop_name', 'sanitize_text_field');
885 885 $propValue = $request->getSafe('prop_value', 'sanitize_text_field');
886 886
887 - // FS-SEC-007: this generic endpoint may only touch a fixed set of
887 + // This generic endpoint may only touch a fixed set of
888 888 // ticket columns. Previously prop_name was assigned straight onto the
889 889 // model ($ticket->{$propName} = $propValue), letting a caller rewrite
890 890 // ownership, mailbox, privacy, hash, serial_number, created_by and
891 891 // other sensitive columns and bypass $fillable entirely. Every
@@ -962,9 +962,9 @@
962 962 * The only ticket columns that may be changed through updateTicketProperty.
963 963 * This mirrors exactly what the admin UI edits (agent, title, mailbox,
964 964 * product, status and the two priority fields). Ownership, audit,
965 965 * public-identifier and other sensitive columns are intentionally absent
966 - * and must go through their dedicated workflows (FS-SEC-007).
966 + * and must go through their dedicated workflows.
967 967 *
968 968 * @return array
969 969 */
970 970 protected function updatableTicketProperties()
@@ -983,9 +983,9 @@
983 983 /**
984 984 * Validate and normalize a single ticket-property update. Each allowlisted
985 985 * property is checked against its own value domain and capability, so a
986 986 * caller can neither set an out-of-range value nor perform a change the UI
987 - * gates behind a stronger permission (FS-SEC-007).
987 + * gates behind a stronger permission.
988 988 *
989 989 * @param Ticket $ticket
990 990 * @param string $propName Already confirmed to be in the allowlist.
991 991 * @param string $propValue Raw (text-sanitized) value from the request.
@@ -1019,8 +1019,16 @@
1019 1019
1020 1020 if (!in_array($propValue, $allowedStatuses, true)) {
1021 1021 throw new \Exception(esc_html__('Invalid ticket status.', 'fluent-support'), 422);
1022 1022 }
1023 +
1024 + // This route only assigns the column and saves, so closing or
1025 + // reopening here would skip TicketService's closure fields, hooks and cleanup.
1026 + // Use closeTicket() / reOpenTicket(); the status dropdown already does.
1027 + if ($propValue === 'closed' || $ticket->status === 'closed') {
1028 + throw new \Exception(esc_html__('Closing or reopening a ticket must use the dedicated close and re-open actions.', 'fluent-support'), 422);
1029 + }
1030 +
1023 1031 return $propValue;
1024 1032
1025 1033 case 'priority':
1026 1034 if (!array_key_exists($propValue, Helper::adminTicketPriorities())) {
@@ -1549,9 +1557,11 @@
1549 1557 esc_html__('Sorry, you do not have permission to update this response.', 'fluent-support')
1550 1558 );
1551 1559 }
1552 1560
1553 - $content = wp_unslash(wp_kses_post($request->getSafe('content', 'wp_kses_post')));
1561 + // Request input is already unslashed at the boundary; unslashing again
1562 + // would strip literal backslashes out of the edited reply.
1563 + $content = wp_kses_post($request->getSafe('content', 'wp_kses_post'));
1554 1564 $response->content = $content;
1555 1565
1556 1566 if ($isDraft && !$isAuthor && $canApproveDraft) {
1557 1567 $response = $this->approveDraftConversation($ticket, $response, $agent, $content);
@@ -1593,9 +1603,9 @@
1593 1603 $response = $this->approveDraftConversation(
1594 1604 $ticket,
1595 1605 $response,
1596 1606 $person,
1597 - wp_unslash(wp_kses_post($request->getSafe('content', 'wp_kses_post')))
1607 + wp_kses_post($request->getSafe('content', 'wp_kses_post'))
1598 1608 );
1599 1609
1600 1610 return [
1601 1611 'message' => __('Draft response has been successfully approved.', 'fluent-support'),