PluginProbe
The Innovative Form Builder – IvyForms / 0.2.1
The Innovative Form Builder – IvyForms v0.2.1
1.5.1 1.5 1.4.1 1.4 trunk 0.1.2 0.2 0.2.1 0.3 0.3.1 0.4 0.5 0.6 0.6.1 0.6.1-backup 0.6.1.1 0.7 0.8 0.8.1 0.8.2 0.9 0.9.1 1.0 1.1 1.1.1 All 27 releases
ivyforms / backend / src / Common / Sanitizer / Sanitizer.php

Sanitizer.php in The Innovative Form Builder – IvyForms 0.2.1, at backend/src/Common/Sanitizer/Sanitizer.php

307 lines 9.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * @copyright © TMS-Plugins. All rights reserved.
5 * @licence See COPYING.md for license details.
6 */
7
8 namespace IvyForms\Common\Sanitizer;
9
10 // phpcs:disable PSR1.Files.SideEffects
11 if (!defined('ABSPATH')) {
12 exit; // Exit if accessed directly
13 }
14
15 use IvyForms\Common\Exceptions\ForbiddenException;
16 use IvyForms\Common\Exceptions\InvalidArgumentException;
17 use IvyForms\Services\Field\FieldType;
18
19 class Sanitizer
20 {
21 /**
22 * Sanitize all form data
23 *
24 * @param mixed[] $data
25 *
26 * @return mixed[]
27 */
28 public static function sanitizeFormData(array $data): array
29 {
30
31 return [
32 'id' => (int)($data['id'] ?? 0),
33 'name' => sanitize_text_field($data['name'] ?? ''),
34 'description' => sanitize_text_field($data['description'] ?? ''),
35 'showTitle' => (bool)($data['showTitle'] ?? false),
36 'published' => (bool)($data['published'] ?? false),
37 'showDescription' => (bool)($data['showDescription'] ?? false),
38 'fields' => self::sanitizeFields($data['fields'] ?? []),
39 ];
40 }
41 /**
42 * Sanitize all notification data
43 *
44 * @param mixed[] $data
45 *
46 * @return mixed[]
47 */
48 public static function sanitizeNotificationData(array $data): array
49 {
50 return [
51 'id' => (int)($data['id'] ?? 0),
52 'name' => sanitize_text_field($data['name'] ?? ''),
53 'sender' => sanitize_email($data['sender'] ?? ''),
54 'receiver' => sanitize_email($data['receiver'] ?? ''),
55 'enabled' => (bool)($data['enabled'] ?? true),
56 'subject' => sanitize_text_field($data['subject'] ?? ''),
57 'message' => sanitize_textarea_field($data['message'] ?? ''),
58 'smartLogic' => (bool)($data['smartLogic'] ?? false),
59 'formId' => (int)($data['formId'] ?? 0),
60 ];
61 }
62 /**
63 * Sanitize all confirmation data
64 *
65 * @param mixed[] $data
66 *
67 * @return mixed[]
68 */
69 public static function sanitizeConfirmationData(array $data): array
70 {
71 return [
72 'id' => (int)($data['id'] ?? 0),
73 'formId' => (int)($data['formId'] ?? 0),
74 'type' => sanitize_text_field($data['type'] ?? ''),
75 'enabled' => (bool)($data['enabled'] ?? true),
76 'showForm' => (bool)($data['showForm'] ?? false),
77 'message' => sanitize_textarea_field($data['message'] ?? ''),
78 'url' => sanitize_url(isset($data['url']) ? urldecode($data['url']) : ''),
79 'page' => sanitize_text_field($data['page'] ?? ''),
80 ];
81 }
82 /**
83 * Sanitize form fields
84 *
85 * @param mixed[] $fields
86 *
87 * @return mixed[]
88 */
89 private static function sanitizeFields(array $fields): array
90 {
91 return array_map(function ($field) {
92 $fieldType = FieldType::sanitize($field['type'] ?? '');
93 return [
94 'id' => (int)($field['id'] ?? 0),
95 'fieldIndex' => (int)($field['fieldIndex'] ?? 0),
96 'type' => sanitize_text_field($fieldType),
97 'label' => sanitize_text_field($field['label'] ?? ''),
98 'required' => (bool)($field['required'] ?? false),
99 'defaultValue' => sanitize_text_field($field['defaultValue'] ?? ''),
100 'placeholder' => sanitize_text_field($field['placeholder'] ?? ''),
101 'position' => (int)($field['position'] ?? 0)
102 ];
103 }, $fields);
104 }
105
106 /**
107 * Sanitize form/notification ID
108 *
109 * @param int $id
110 *
111 * @return int
112 *
113 * @throws InvalidArgumentException
114 */
115 public static function sanitizeId(int $id): int
116 {
117 if ($id <= 0) {
118 throw new InvalidArgumentException('Invalid form ID.');
119 }
120
121 return $id;
122 }
123
124 /**
125 * Sanitize form/notification IDs
126 *
127 * @param int[] $ids
128 *
129 * @return int[]
130 *
131 * @throws InvalidArgumentException
132 */
133 public static function sanitizeIds(array $ids): array
134 {
135 return array_map(function ($id) {
136 return (int)$id;
137 }, $ids);
138 }
139
140 /**
141 * Sanitize search parameters
142 *
143 * @param array<string, mixed> $params
144 *
145 * @return array<string, mixed>
146 */
147 public static function sanitizeSearchParams(array $params): array
148 {
149 $filters = [
150 'starred' => array_key_exists('starred', $params['filters'] ?? [])
151 ? (int)filter_var($params['filters']['starred'], FILTER_VALIDATE_BOOLEAN)
152 : null,
153 'published' => array_key_exists('published', $params['filters'] ?? [])
154 ? (int)filter_var($params['filters']['published'], FILTER_VALIDATE_BOOLEAN)
155 : null,
156 'status' => sanitize_text_field($params['filters']['read'] ?? ''),
157 ];
158 // Add formId to filters if present and valid
159 if (
160 isset($params['filters']['formId']) &&
161 is_numeric($params['filters']['formId']) &&
162 (int)$params['filters']['formId'] > 0
163 ) {
164 $filters['formId'] = self::sanitizeId((int)$params['filters']['formId']);
165 }
166 return [
167 'page' => max((int)($params['page'] ?? 1), 1),
168 'perPage' => max((int)($params['perPage'] ?? 10), 1),
169 'search' => sanitize_text_field($params['search'] ?? ''),
170 'orderBy' => sanitize_text_field($params['orderBy'] ?? 'id'),
171 'order' => strtolower(sanitize_text_field($params['order'] ?? 'asc')) === 'desc' ? 'desc' : 'asc',
172 'filters' => $filters,
173 'dateRange' => [
174 sanitize_text_field($params['dateRange'][0] ?? ''),
175 sanitize_text_field($params['dateRange'][1] ?? ''),
176 ],
177 'shouldGetCount' => (bool)($params['shouldGetCount'] ?? false),
178 ];
179 }
180
181 /**
182 * Sanitize form submission data based on field types
183 *
184 * @param mixed[] $data Form submission data
185 * @param mixed[] $formFields Form fields configuration
186 *
187 * @return mixed[] Sanitized submission data
188 */
189 public static function sanitizeFormSubmissionData(array $data, array $formFields): array
190 {
191 $sanitizedData = [];
192 $fieldMaps = self::buildFieldMaps($formFields);
193 $fieldLabelMap = $fieldMaps['labels'];
194 $fieldTypeMap = $fieldMaps['types'];
195
196 // Sanitize each form field based on its type
197 foreach ($data['values'] as $keyIndex => $value) {
198 // Skip WordPress internal fields
199 if (in_array($keyIndex, ['nonce', 'formId'])) {
200 $sanitizedData[$keyIndex] = $value;
201 continue;
202 }
203
204 $fieldType = $fieldTypeMap[$keyIndex] ?? '';
205 $fieldLabel = $fieldLabelMap[$keyIndex] ?? '';
206
207 $sanitizedData[$fieldLabel] = self::sanitizeByFieldType($fieldType, $value);
208 }
209
210 // Add form ID to sanitized data if present
211 if (isset($data['formId'])) {
212 $sanitizedData['formId'] = (int)$data['formId'];
213 }
214
215 return $sanitizedData;
216 }
217
218 /**
219 * Build field mapping arrays for labels and types
220 *
221 * @param mixed[] $formFields
222 *
223 * @return array<string, array<string, string>> Associative array with 'labels' and 'types' mappings
224 */
225 private static function buildFieldMaps(array $formFields): array
226 {
227 $fieldLabelMap = [];
228 $fieldTypeMap = [];
229
230 foreach ($formFields as $field) {
231 $fieldName = $field->getLabel() ?? '';
232 $key = $field->getType() . "_" . $field->getIndex();
233 $fieldLabelMap[$key] = $fieldName;
234 $fieldTypeMap[$key] = $field->getType() ?? '';
235 }
236
237 return [
238 'labels' => $fieldLabelMap,
239 'types' => $fieldTypeMap
240 ];
241 }
242
243 /**
244 * Sanitize a value based on its field type
245 *
246 * @param string $fieldType The type of field
247 * @param mixed $value The value to sanitize
248 *
249 * @return mixed Sanitized value
250 */
251 private static function sanitizeByFieldType(string $fieldType, $value)
252 {
253 switch ($fieldType) {
254 case 'email':
255 return sanitize_email($value);
256
257 case 'number':
258 return is_numeric($value) ? intval($value) : 0;
259
260 case 'phone':
261 // Remove everything except digits, +, and spaces
262 return preg_replace('/[^\d\s+]/', '', $value);
263
264 case 'paragraph':
265 return sanitize_textarea_field($value);
266
267 case 'text':
268 default:
269 // Default to text field sanitization
270 return sanitize_text_field($value);
271 }
272 }
273
274 /**
275 * Verify nonce with custom error handling
276 *
277 * @param string|null $nonce
278 *
279 * @throws ForbiddenException
280 */
281 public static function verifyNonce(?string $nonce): void
282 {
283 $nonce = sanitize_text_field($nonce);
284
285 if (!$nonce || !wp_verify_nonce($nonce, 'wp_rest')) {
286 throw new ForbiddenException('Invalid nonce.');
287 }
288 }
289 /**
290 * Verify submission nonce with custom error handling
291 *
292 * @param string|null $nonce
293 * @param int $formId
294 *
295 * @throws ForbiddenException
296 */
297 public static function verifySubmissionNonce(?string $nonce, int $formId): void
298 {
299 $nonce = sanitize_text_field($nonce);
300
301 // Verify the form-specific nonce
302 if (!wp_verify_nonce($nonce, 'ivyformsFrontSubmissionNonce_' . $formId)) {
303 throw new ForbiddenException('Invalid security token.');
304 }
305 }
306 }
307