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/Modules/Form/FormDataParser.php +119 -49 3.6.606.2.14 View file →
@@ -2,13 +2,13 @@
2 2
3 3 namespace FluentForm\App\Modules\Form;
4 4
5 5 use FluentForm\Framework\Helpers\ArrayHelper;
6 -use WpFluent\Exception;
7 6
8 7 class FormDataParser
9 8 {
10 9 protected static $data = null;
10 + protected static $submissionId = null;
11 11
12 12 public static function parseFormEntries($entries, $form, $fields = null)
13 13 {
14 14 $fields = $fields ? $fields : FormFieldsParser::getEntryInputs($form);
@@ -24,9 +24,12 @@
24 24 {
25 25 $fields = $fields ? $fields : FormFieldsParser::getEntryInputs($form);
26 26
27 27 $entry->user_inputs = static::parseData(
28 - json_decode($entry->response), $fields, $form->id, $isHtml
28 + json_decode($entry->response),
29 + $fields,
30 + $form->id,
31 + $isHtml
29 32 );
30 33
31 34 return $entry;
32 35 }
@@ -32,12 +35,19 @@
32 35 }
33 36
34 37 public static function parseFormSubmission($submission, $form, $fields, $isHtml = false)
35 38 {
36 - if (is_null(static::$data)) {
39 + // Sometimes submission will change inside loop. So we need to parse submission data for new one
40 + $newSubmission = $submission->id != static::$submissionId;
41 +
42 + if (is_null(static::$data) || $newSubmission) {
37 43 static::$data = static::parseData(
38 - json_decode($submission->response), $fields, $form->id, $isHtml
44 + json_decode($submission->response),
45 + $fields,
46 + $form->id,
47 + $isHtml
39 48 );
49 + static::$submissionId = $submission->id;
40 50 }
41 51
42 52 $submission->user_inputs = static::$data;
43 53
@@ -48,11 +58,26 @@
48 58 {
49 59 $trans = [];
50 60 foreach ($fields as $field_key => $field) {
51 61 if (isset($response->{$field_key})) {
62 + $value = $response->{$field_key};
63 +
64 + $value = apply_filters_deprecated(
65 + 'fluentform_response_render_' . $field['element'],
66 + [
67 + $value,
68 + $field,
69 + $formId,
70 + $isHtml
71 + ],
72 + FLUENTFORM_FRAMEWORK_UPGRADE,
73 + 'fluentform/response_render_' . $field['element'],
74 + 'Use fluentform/response_render_' . $field['element'] . ' instead of fluentform_response_render_' . $field['element']
75 + );
76 +
52 77 $value = apply_filters(
53 - 'fluentform_response_render_' . $field['element'],
54 - $response->{$field_key},
78 + 'fluentform/response_render_' . $field['element'],
79 + $value,
55 80 $field,
56 81 $formId,
57 82 $isHtml
58 83 );
@@ -67,15 +92,15 @@
67 92
68 93 public static function formatValue($value)
69 94 {
70 95 if (is_array($value) || is_object($value)) {
71 - return fluentImplodeRecursive(', ', array_filter(array_values((array)$value)));
96 + return fluentImplodeRecursive(', ', array_filter(array_values((array) $value)));
72 97 }
73 98
74 99 return $value;
75 100 }
76 101
77 - public static function formatFileValues($values, $isHtml)
102 + public static function formatFileValues($values, $isHtml, $form_id = null)
78 103 {
79 104 if (!$values) {
80 105 return $values;
81 106 }
@@ -84,10 +109,13 @@
84 109 return $values;
85 110 }
86 111
87 112 if (!$isHtml) {
88 - return fluentImplodeRecursive(', ', array_filter(array_values((array)$values)));
113 + return fluentImplodeRecursive(', ', array_filter(array_values((array) $values)));
89 114 }
115 + if ($form_id && \FluentForm\App\Helpers\Helper::isEntryAutoDeleteEnabled($form_id)) {
116 + return '';
117 + }
90 118
91 119 $html = '<ul class="ff_entry_list">';
92 120 foreach ($values as $value) {
93 121 if (!$value) {
@@ -92,9 +120,13 @@
92 120 foreach ($values as $value) {
93 121 if (!$value) {
94 122 continue;
95 123 }
96 - $html .= '<li><a href="' . $value . '" target="_blank">' . basename($value) . '</a></li>';
124 + // SECURITY (FINDING-23): escape the submitted upload value. It reaches this HTML sink
125 + // via an unauthenticated submission and is only sanitize_text_field'd (keeps " and :),
126 + // so a javascript: URL or an " onmouseover=" attribute breakout would otherwise render
127 + // in the admin entry view, notification email and PDF. esc_url enforces a safe scheme.
128 + $html .= '<li><a href="' . esc_url($value) . '" target="_blank">' . esc_html(basename($value)) . '</a></li>';
97 129 }
98 130
99 131 $html .= '</ul>';
100 132 return $html;
@@ -99,9 +131,9 @@
99 131 $html .= '</ul>';
100 132 return $html;
101 133 }
102 134
103 - public static function formatImageValues($values, $isHtml)
135 + public static function formatImageValues($values, $isHtml, $form_id = null)
104 136 {
105 137 if (!$values) {
106 138 return $values;
107 139 }
@@ -108,19 +140,25 @@
108 140
109 141 if (is_string($values)) {
110 142 return $values;
111 143 }
144 +
145 + $isHtml = apply_filters('fluentform/render_field_as_html', $isHtml, $values, $form_id);
112 146
147 +
113 148 if (!$isHtml) {
114 - return fluentImplodeRecursive(', ', array_filter(array_values((array)$values)));
149 + return fluentImplodeRecursive(', ', array_filter(array_values((array) $values)));
115 150 }
116 -
117 - if (count($values) == 1) {
151 + if ($form_id && \FluentForm\App\Helpers\Helper::isEntryAutoDeleteEnabled($form_id)) {
152 + return '';
153 + }
154 + if (1 == count($values)) {
118 155 $value = $values[0];
119 156 if (!$value) {
120 157 return '';
121 158 }
122 - return '<a href="' . $value . '" target="_blank"><img style="max-width:180px" src="' . $value . '" /></a>';
159 + // SECURITY (FINDING-23): escape the submitted upload value (see formatFileValues).
160 + return '<a href="' . esc_url($value) . '" target="_blank"><img style="max-width:180px" src="' . esc_url($value) . '" /></a>';
123 161 }
124 162
125 163 $html = '<ul class="ff_entry_list ff_entry_images">';
126 164 foreach ($values as $value) {
@@ -126,9 +164,10 @@
126 164 foreach ($values as $value) {
127 165 if (!$value) {
128 166 continue;
129 167 }
130 - $html .= '<li style="margin: 20px 20px 20px 0px; display: inline-block; margin-right: 20px;"><a href="' . $value . '" target="_blank"><img style="max-width:180px" src="' . $value . '" /></a></li>';
168 + // SECURITY (FINDING-23): escape the submitted upload value (see formatFileValues).
169 + $html .= '<li style="margin: 20px 20px 20px 0px; display: inline-block; margin-right: 20px;"><a href="' . esc_url($value) . '" target="_blank"><img style="max-width:180px" src="' . esc_url($value) . '" /></a></li>';
131 170 }
132 171
133 172 $html .= '</ul>';
134 173 return $html;
@@ -155,25 +194,26 @@
155 194 ?>
156 195 <div class="ff_entry_table_wrapper">
157 196 <table class="ff_entry_table_field ff-table">
158 197 <thead>
159 - <tr>
160 - <?php foreach ($repeatColumns as $repeatColumn) : ?>
161 - <th><?php echo ArrayHelper::get($repeatColumn, 'settings.label'); ?></th>
162 - <?php endforeach; ?>
163 - </tr>
198 + <tr>
199 + <?php foreach ($repeatColumns as $repeatColumn) : ?>
200 + <th><?php echo fluentform_sanitize_html(ArrayHelper::get($repeatColumn, 'settings.label')); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- fluentform_sanitize_html() removes XSS vectors and uses wp_kses() with allowed tags ?>
201 + </th>
202 + <?php endforeach; ?>
203 + </tr>
164 204 </thead>
165 205
166 206 <tbody>
167 - <?php for ($i = 0; $i < $rows; $i++) : ?>
207 + <?php for ($i = 0; $i < $rows; $i++) : ?>
168 208 <tr>
169 209 <?php for ($j = 0; $j < $columns; $j++) : ?>
170 - <td>
171 - <?php echo $value[$j][$i] ?>
172 - </td>
210 + <td>
211 + <?php echo fluentform_sanitize_html($value[$j][$i]); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- fluentform_sanitize_html() removes XSS vectors and uses wp_kses() with allowed tags ?>
212 + </td>
173 213 <?php endfor; ?>
174 214 </tr>
175 - <?php endfor; ?>
215 + <?php endfor; ?>
176 216 </tbody>
177 217 </table>
178 218 </div>
179 219 <?php
@@ -178,10 +218,9 @@
178 218 </div>
179 219 <?php
180 220 }
181 221 return ob_get_clean();
182 - } catch (Exception $e) {
183 -
222 + } catch (\Exception $e) {
184 223 }
185 224
186 225 return $value;
187 226 }
@@ -196,9 +235,9 @@
196 235 return $value;
197 236 }
198 237
199 238 if (is_array($value)) {
200 - $value = (object)$value;
239 + $value = (object) $value;
201 240 }
202 241 try {
203 242 if (empty($field['raw'])) {
204 243 return $value;
@@ -204,19 +243,18 @@
204 243 return $value;
205 244 }
206 245 $columnLabels = $field['raw']['settings']['grid_columns'];
207 246 $fieldType = $field['raw']['settings']['tabular_field_type'];
208 - $columnHeaders = implode('</th><th>', array_values($columnLabels));
247 + $columnHeaders = implode('</th><th style="text-align: center;">', array_values($columnLabels));
209 248
210 - $elMarkup = "<table class='ff-table'><thead><tr><th></th><th>{$columnHeaders}</th></tr></thead><tbody>";
249 + $elMarkup = "<table class='ff-table'><thead><tr><th></th><th style='text-align: center;'>{$columnHeaders}</th></tr></thead><tbody>";
211 250
212 251 foreach (static::makeTabularData($field['raw']) as $row) {
213 -
214 - $elMarkup .= "<tr>";
252 + $elMarkup .= '<tr>';
215 253 $elMarkup .= "<td>{$row['label']}</td>";
216 254 foreach ($row['columns'] as $column) {
217 255 $isChecked = '';
218 - if ($fieldType == 'radio') {
256 + if ('radio' == $fieldType) {
219 257 if (isset($value->{$row['name']})) {
220 258 $isChecked = $value->{$row['name']} == $column['name'] ? 'checked' : '';
221 259 }
222 260 } else {
@@ -227,18 +265,17 @@
227 265 $icon = "<input disabled type='{$fieldType}' {$isChecked}>";
228 266 if ($isChecked) {
229 267 $icon = '✔';
230 268 }
231 - $elMarkup .= "<td>" . $icon . "</td>";
269 + $elMarkup .= "<td style='text-align: center;'>" . $icon . '</td>';
232 270 }
233 - $elMarkup .= "</tr>";
271 + $elMarkup .= '</tr>';
234 272 }
235 273
236 - $elMarkup .= "</tbody></table>";
274 + $elMarkup .= '</tbody></table>';
237 275
238 276 return $elMarkup;
239 - } catch (Exception $e) {
240 -
277 + } catch (\Exception $e) {
241 278 }
242 279 return '';
243 280 }
244 281
@@ -248,22 +285,22 @@
248 285 $rows = $data['settings']['grid_rows'];
249 286 $columns = $data['settings']['grid_columns'];
250 287
251 288 foreach ($rows as $rowKey => $rowValue) {
289 + $rowKey = trim(sanitize_text_field($rowKey));
252 290 $table[$rowKey] = [
253 291 'name' => $rowKey,
254 292 'label' => $rowValue,
255 - 'columns' => []
293 + 'columns' => [],
256 294 ];
257 295
258 296 foreach ($columns as $columnKey => $columnValue) {
259 297 $table[$rowKey]['columns'][] = [
260 - 'name' => $columnKey,
261 - 'label' => $columnValue
298 + 'name' => trim(sanitize_text_field($columnKey)),
299 + 'label' => $columnValue,
262 300 ];
263 301 }
264 302 }
265 -
266 303 return $table;
267 304 }
268 305
269 306 /**
@@ -275,9 +312,16 @@
275 312 */
276 313 public static function formatName($value)
277 314 {
278 315 if (is_array($value) || is_object($value)) {
279 - return fluentImplodeRecursive(' ', array_filter(array_values((array)$value)));
316 + $value = (array) $value;
317 + $order = ['first_name', 'middle_name', 'last_name'];
318 + uksort($value, function($a, $b) use ($order) {
319 + $posA = array_search($a, $order);
320 + $posB = array_search($b, $order);
321 + return $posA - $posB;
322 + });
323 + return fluentImplodeRecursive(' ', array_filter(array_values($value)));
280 324 }
281 325
282 326 return $value;
283 327 }
@@ -283,25 +327,51 @@
283 327 }
284 328
285 329 public static function formatCheckBoxValues($values, $field, $isHtml = false)
286 330 {
287 - if(!$isHtml) {
331 + if (!$isHtml) {
332 + if (
333 + defined('FLUENTFORM_RENDERING_ENTRIES') &&
334 + $values && is_array($values) &&
335 + $options = ArrayHelper::get($field, 'raw.settings.advanced_options', [])
336 + ) {
337 + $options = \FluentForm\App\Helpers\Helper::advancedOptionsValueLabelMap($options);
338 + foreach ($values as &$value) {
339 + if ($label = ArrayHelper::get($options, $value)) {
340 + $value = $label;
341 + }
342 + }
343 + }
288 344 return self::formatValue($values);
289 345 }
290 346
291 - if(!is_array($values) || empty($values)) {
347 + if (!is_array($values)) {
292 348 return $values;
293 349 }
294 350
351 + if (empty($values)) {
352 + return '';
353 + }
354 +
355 + if (!isset($field['options'])) {
356 + $field['options'] = \FluentForm\App\Helpers\Helper::advancedOptionsValueLabelMap(
357 + ArrayHelper::get($field, 'raw.settings.advanced_options', [])
358 + );
359 + }
360 +
295 361 $html = '<ul style="white-space: normal;">';
296 362 foreach ($values as $value) {
297 363 $item = $value;
298 - if($itemLabel = ArrayHelper::get($field, 'options.'.$item)) {
364 + if ($itemLabel = ArrayHelper::get($field, 'options.' . $item)) {
299 365 $item = $itemLabel;
300 366 }
301 - $html .= '<li>'.$item.'</li>';
367 + $html .= '<li>' . $item . '</li>';
302 368 }
303 369
304 - return $html.'</ul>';
370 + return $html . '</ul>';
371 + }
305 372
373 + public static function resetData()
374 + {
375 + static::$data = null;
306 376 }
307 377 }