PluginProbe
Fluent Forms – Customizable Contact Forms, Survey, Quiz, & Conversational Form Builder / 6.2.14
Fluent Forms – Customizable Contact Forms, Survey, Quiz, & Conversational Form Builder v6.2.14
6.2.14 6.2.13 6.2.12 6.2.10 6.2.11 6.2.9 6.2.8 6.2.7 6.2.6 6.2.5 6.2.4 6.2.3 6.2.2 3.6.22 3.6.31 3.6.40 3.6.41 3.6.42 3.6.50 3.6.51 3.6.60 3.6.61 3.6.62 3.6.64 3.6.65 All 196 releases
← All changes | app/Http/Controllers/SubmissionController.php +63 -23 6.2.66.2.14 View file →
@@ -3,8 +3,10 @@
3 3 namespace FluentForm\App\Http\Controllers;
4 4
5 5 use Exception;
6 6 use FluentForm\App\Models\Submission;
7 +use FluentForm\App\Modules\Acl\Acl;
8 +use FluentForm\App\Services\Manager\FormManagerService;
7 9 use FluentForm\App\Services\Submission\SubmissionService;
8 10 use FluentForm\Framework\Support\Arr;
9 11
10 12 class SubmissionController extends Controller
@@ -40,14 +42,26 @@
40 42 public function resources(SubmissionService $submissionService)
41 43 {
42 44 try {
43 45 $attributes = $this->request->all();
44 -
46 +
45 47 $sanitizeMap = [
46 48 'form_id' => 'intval',
47 49 ];
48 50 $attributes = fluentform_backend_sanitizer($attributes, $sanitizeMap);
49 -
51 +
52 + // SECURITY (FINDING-02): this route has no {entry_id} placeholder, so SubmissionPolicy
53 + // authorizes the form owning the *request* entry_id, while resources() then reads a
54 + // separate form_id — letting a form-scoped user read another form's counts/labels/
55 + // fields and (via next/previous) submission rows. Re-verify the caller may view
56 + // entries of the form actually being queried.
57 + $formId = (int) Arr::get($attributes, 'form_id');
58 + if (!$formId || !Acl::hasPermission('fluentform_entries_viewer', $formId)) {
59 + return $this->sendError([
60 + 'message' => __('You do not have permission to view this form\'s entries.', 'fluentform'),
61 + ], 403);
62 + }
63 +
50 64 return $this->sendSuccess(
51 65 $submissionService->resources($attributes)
52 66 );
53 67 } catch (Exception $e) {
@@ -56,12 +70,14 @@
56 70 ]);
57 71 }
58 72 }
59 73
60 - public function updateStatus(SubmissionService $submissionService)
74 + public function updateStatus(SubmissionService $submissionService, $submissionId)
61 75 {
62 76 try {
63 - $status = $submissionService->updateStatus($this->request->all());
77 + $attributes = $this->request->all();
78 + $attributes['entry_id'] = intval($submissionId);
79 + $status = $submissionService->updateStatus($attributes);
64 80
65 81 /* translators: %s is the submission status */
66 82 $message = sprintf(__('The submission has been marked as %s', 'fluentform'), $status);
67 83
@@ -75,13 +91,13 @@
75 91 ]);
76 92 }
77 93 }
78 94
79 - public function toggleIsFavorite(SubmissionService $submissionService)
95 + public function toggleIsFavorite(SubmissionService $submissionService, $submissionId)
80 96 {
81 97 try {
82 98 [$message, $isFavourite] = $submissionService->toggleIsFavorite(
83 - $this->request->get('entry_id')
99 + intval($submissionId)
84 100 );
85 101
86 102 return $this->sendSuccess([
87 103 'message' => $message,
@@ -105,9 +121,9 @@
105 121 'message' => $e->getMessage(),
106 122 ]);
107 123 }
108 124 }
109 -
125 +
110 126 public function remove(SubmissionService $submissionService, $submissionId)
111 127 {
112 128 try {
113 129 $submission = Submission::findOrFail($submissionId);
@@ -123,21 +139,36 @@
123 139 'message' => $e->getMessage(),
124 140 ]);
125 141 }
126 142 }
127 -
143 +
128 144 /**
129 145 * Get user list for submission page
146 + *
130 147 * @return \WP_REST_Response
131 148 */
132 149 public function submissionUsers()
133 150 {
151 + // SECURITY (FINDING-21): don't let a lower-tier user enumerate the whole WP roster here.
152 + // Require WP's list_users OR the FF entries-manager permission this feature is built for —
153 + // a delegated non-admin manager holds fluentform_manage_entries (and the assign-user UI is
154 + // shown only to them) but NOT core list_users, so gating on list_users alone broke them.
155 + if (!current_user_can('list_users') && !current_user_can('fluentform_manage_entries')) {
156 + return $this->sendError(['message' => __('You do not have permission to list users.', 'fluentform')], 403);
157 + }
134 158 $search = sanitize_text_field($this->request->get('search'));
135 - $users = get_users([
136 - 'search' => "*{$search}*",
137 - 'number' => 50,
138 - ]);
139 -
159 + if (current_user_can('list_users')) {
160 + $users = get_users([
161 + 'search' => "*{$search}*",
162 + 'number' => 50,
163 + ]);
164 + } else {
165 + // A delegated entries manager may confirm an address they already know,
166 + // but must not browse the site's user roster (FF-SEC-45).
167 + $user = is_email($search) ? get_user_by('email', $search) : false;
168 + $users = $user ? [$user] : [];
169 + }
170 +
140 171 $formattedUsers = [];
141 172 foreach ($users as $user) {
142 173 $formattedUsers[] = [
143 174 'ID' => $user->ID,
@@ -143,9 +174,9 @@
143 174 'ID' => $user->ID,
144 175 'label' => $user->display_name . ' - ' . $user->user_email,
145 176 ];
146 177 }
147 -
178 +
148 179 return $this->sendSuccess([
149 180 'users' => $formattedUsers,
150 181 ]);
151 182 }
@@ -151,18 +182,18 @@
151 182 }
152 183
153 184 /**
154 185 * Update User of a submission
186 + *
155 187 * @param SubmissionService $submissionService
188 + * @param int $submissionId
156 189 * @return \WP_REST_Response
157 190 */
158 - public function updateSubmissionUser(SubmissionService $submissionService)
191 + public function updateSubmissionUser(SubmissionService $submissionService, $submissionId)
159 192 {
160 193 try {
161 194 $userId = intval($this->request->get('user_id'));
162 - // Use entry_id from route parameter — not submission_id from body —
163 - // to ensure authorization target matches the mutation target.
164 - $submissionId = intval($this->request->get('entry_id'));
195 + $submissionId = intval($submissionId);
165 196 $response = $submissionService->updateSubmissionUser($userId, $submissionId);
166 197 return $this->sendSuccess($response);
167 198 } catch (Exception $e) {
168 199 return $this->sendError([
@@ -169,11 +200,12 @@
169 200 'message' => $e->getMessage(),
170 201 ]);
171 202 }
172 203 }
173 -
204 +
174 205 /**
175 206 * Get All Submissions
207 + *
176 208 * @param Submission $submission
177 209 * @return \WP_REST_Response
178 210 */
179 211 public function all(Submission $submission)
@@ -191,8 +223,9 @@
191 223 }
192 224 }
193 225 /**
194 226 * Get printable content
227 + *
195 228 * @param SubmissionService $submissionService
196 229 * @return \WP_REST_Response
197 230 */
198 231 public function print(SubmissionService $submissionService)
@@ -198,11 +231,11 @@
198 231 public function print(SubmissionService $submissionService)
199 232 {
200 233 try {
201 234 $attributes = $this->request->all();
202 -
235 +
203 236 $sanitizeMap = [
204 - 'submission_ids' => function($value) {
237 + 'submission_ids' => function ($value) {
205 238 if (is_array($value)) {
206 239 return array_map('intval', $value);
207 240 }
208 241 return [];
@@ -218,15 +251,22 @@
218 251 $attributes['submission_ids'] = is_array($entryIds)
219 252 ? array_map('intval', $entryIds)
220 253 : [];
221 254 }
222 -
255 +
256 + // Re-verify against the form actually printed; the policy scopes on a request entry_id.
257 + if (!FormManagerService::hasFormPermission((int) Arr::get($attributes, 'form_id'))) {
258 + return $this->sendError([
259 + 'message' => __('You do not have permission to view this form\'s entries.', 'fluentform'),
260 + ], 403);
261 + }
262 +
223 263 return $this->sendSuccess(
224 264 $submissionService->getPrintContent($attributes)
225 265 );
226 266 } catch (Exception $e) {
227 267 return $this->sendError([
228 - 'message' => $e->getMessage()
268 + 'message' => $e->getMessage(),
229 269 ]);
230 270 }
231 271 }
232 272