PluginProbe
The WP Remote WordPress Plugin / 6.72
The WP Remote WordPress Plugin v6.72
6.72 6.69 6.65 6.62 6.48 6.47 4.87 4.97 5.05 5.09 5.16 5.22 5.24 5.25 5.38 5.41 5.42 5.45 5.47 5.53 5.56 5.65 5.68 5.72 5.73 All 53 releases
wpremote / protect / request.php

request.php in The WP Remote WordPress Plugin 6.72, at protect/request.php

722 lines 19.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 if (!defined('ABSPATH') && !defined('MCDATAPATH')) exit;
4
5 if (!class_exists('WPRProtectRequest_V672')) :
6 class WPRProtectRequest_V672 {
7 public $ip;
8 public $host = '';
9 public $uri;
10 public $method = '';
11 public $path = '';
12 public $timestamp;
13 public $get_params;
14 public $post_params;
15 public $cookies;
16 public $headers = array();
17 public $file_names = array();
18 public $json_params = array();
19 public $raw_body = '';
20 public $files;
21 public $respcode;
22 public $status = WPRProtectRequest_V672::STATUS_ALLOWED;
23 public $category = WPRProtectRequest_V672::CATEGORY_NORMAL;
24
25 public $wp_user;
26
27 private $can_get_raw_body = false;
28 private $can_decode_json = false;
29 private $can_get_uploaded_file_content = false;
30
31 private $max_raw_body_length = 1000000;
32 private $max_json_decode_depth = 512;
33 private $max_uploaded_file_content_length = 8192;
34 private $max_total_uploaded_file_content_length = 65536;
35
36 private $raw_body_status = 'not_loaded';
37 private $json_params_status = 'not_loaded';
38 private $raw_body_truncated = false;
39 private $raw_body_loaded = false;
40 private $json_params_loaded = false;
41 private $uploaded_file_content_statuses = array();
42 private $uploaded_file_content_cache = array();
43 private $uploaded_file_content_bytes_read = 0;
44
45 #XNOTE: SHould be part of Protect.
46 const STATUS_ALLOWED = 1;
47 const STATUS_BLOCKED = 2;
48 const STATUS_BYPASSED = 3;
49
50 const CATEGORY_BLACKLISTED = 1;
51 const CATEGORY_NORMAL = 10;
52 const CATEGORY_WHITELISTED = 20;
53 const CATEGORY_BOT_BLOCKED = 30;
54 const CATEGORY_COUNTRY_BLOCKED = 40;
55 const CATEGORY_USER_BLACKLISTED = 50;
56 const CATEGORY_RULE_BLOCKED = 60;
57 const CATEGORY_RULE_ALLOWED = 70;
58 const CATEGORY_PRIVATEIP = 80;
59 const CATEGORY_GLOBAL_BOT_BLOCKED = 90;
60
61 public function __construct($ip_header, $config) {
62 $this->ip = WPRProtectUtils_V672::getIP($ip_header);
63 $this->timestamp = time();
64 $this->get_params = $_GET; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
65 $this->cookies = $_COOKIE;
66 $this->post_params = $_POST; // phpcs:ignore WordPress.Security.NonceVerification.Missing
67 $this->files = $_FILES; // phpcs:ignore WordPress.Security.NonceVerification.Missing
68
69 if (array_key_exists('cangetrawbody', $config) && is_bool($config['cangetrawbody'])) {
70 $this->can_get_raw_body = $config['cangetrawbody'];
71 }
72
73 if (array_key_exists('maxrawbodylength', $config) && is_int($config['maxrawbodylength'])) {
74 $this->max_raw_body_length = $config['maxrawbodylength'];
75 }
76
77 if (array_key_exists('candecodejson', $config) && is_bool($config['candecodejson'])) {
78 $this->can_decode_json = $config['candecodejson'];
79 }
80
81 if (array_key_exists('maxjsondecodedepth', $config) && is_int($config['maxjsondecodedepth'])) {
82 $this->max_json_decode_depth = $config['maxjsondecodedepth'];
83 }
84
85 if (array_key_exists('cangetuploadedfilecontent', $config) && is_bool($config['cangetuploadedfilecontent'])) {
86 $this->can_get_uploaded_file_content = $config['cangetuploadedfilecontent'];
87 }
88
89 if (array_key_exists('maxuploadedfilecontentlength', $config) && is_int($config['maxuploadedfilecontentlength'])) {
90 $this->max_uploaded_file_content_length = $config['maxuploadedfilecontentlength'];
91 }
92
93 if (array_key_exists('maxtotaluploadedfilecontentlength', $config) && is_int($config['maxtotaluploadedfilecontentlength'])) {
94 $this->max_total_uploaded_file_content_length = $config['maxtotaluploadedfilecontentlength'];
95 }
96
97 if (!empty($_FILES)) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
98 foreach ($_FILES as $input => $file) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
99 $this->file_names[$input] = $file['name'];
100 }
101 }
102 if (is_array($_SERVER)) {
103 foreach ($_SERVER as $key => $value) {
104 if (strpos($key, 'HTTP_') === 0) {
105 $header = $this->normalizeHeaderName($key);
106 $this->headers[$header] = $value;
107 }
108 }
109 $content_type = WPRHelper::getRawParam('SERVER', 'CONTENT_TYPE');
110 if (isset($content_type)) {
111 $this->headers['Content-Type'] = $content_type;
112 }
113 $content_length = WPRHelper::getRawParam('SERVER', 'CONTENT_LENGTH');
114 if (isset($content_length)) {
115 $this->headers['Content-Length'] = $content_length;
116 }
117 $referer = WPRHelper::getRawParam('SERVER', 'REFERER');
118 if (isset($referer)) {
119 $this->headers['Referer'] = $referer;
120 }
121 $http_user_agent = WPRHelper::getRawParam('SERVER', 'HTTP_USER_AGENT');
122 if (isset($http_user_agent)) {
123 $this->headers['User-Agent'] = $http_user_agent;
124 }
125
126 if (array_key_exists('Host', $this->headers)) {
127 $this->host = $this->headers['Host'];
128 } elseif (array_key_exists('SERVER_NAME', $_SERVER)) {
129 $this->host = WPRHelper::getRawParam('SERVER', 'SERVER_NAME');
130 }
131
132 $request_method = WPRHelper::getRawParam('SERVER', 'REQUEST_METHOD');
133 $this->method = isset($request_method) ? $request_method : 'GET';
134 $request_uri = WPRHelper::getRawParam('SERVER', 'REQUEST_URI');
135 $this->uri = isset($request_uri) ? $request_uri : '';
136 $_uri = parse_url($this->uri);
137 $this->path = (is_array($_uri) && array_key_exists('path', $_uri)) ? $_uri['path'] : $this->uri;
138 }
139
140 }
141
142 public static function blacklistedCategories() {
143 return array(
144 WPRProtectRequest_V672::CATEGORY_BOT_BLOCKED,
145 WPRProtectRequest_V672::CATEGORY_COUNTRY_BLOCKED,
146 WPRProtectRequest_V672::CATEGORY_USER_BLACKLISTED,
147 WPRProtectRequest_V672::CATEGORY_GLOBAL_BOT_BLOCKED
148 );
149 }
150
151 public static function whitelistedCategories() {
152 return array(WPRProtectRequest_V672::CATEGORY_WHITELISTED);
153 }
154
155 public function setRespCode($code) {
156 $this->respcode = $code;
157 }
158
159 public function getRespCode() {
160 if (!isset($this->respcode) && function_exists('http_response_code')) {
161 $this->respcode = http_response_code();
162 }
163
164 return $this->respcode;
165 }
166
167 public function getStatus() {
168 return $this->status;
169 }
170
171 public function getCategory() {
172 return $this->category;
173 }
174
175 private function getKeyVal($array, $key) {
176 if (is_array($array)) {
177 if (is_array($key)) {
178 $_key = array_shift($key);
179 if (array_key_exists($_key, $array)) {
180 if (count($key) > 0) {
181 return $this->getKeyVal($array[$_key], $key);
182 } else {
183 return $array[$_key];
184 }
185 }
186 } else {
187 return array_key_exists($key, $array) ? $array[$key] : null;
188 }
189 }
190 return null;
191 }
192
193 private function isJsonContentType($content_type) {
194 if (!is_string($content_type)) {
195 return false;
196 }
197
198 return preg_match('/(^|\s|,)application\/([\w!#\$&-\^\.\+]+\+)?json(\+oembed)?($|\s|;|,)/i', $content_type) === 1;
199 }
200
201 private function normalizeHeaderName($name) {
202 if (!is_string($name)) {
203 return null;
204 }
205
206 $name = trim($name);
207 if (stripos($name, 'HTTP_') === 0) {
208 $name = substr($name, 5);
209 }
210 $name = str_replace(array('-', '_'), ' ', $name);
211 return str_replace(' ', '-', ucwords(strtolower($name)));
212 }
213
214 private function isUploadedFileKey($key) {
215 return is_string($key) || is_int($key);
216 }
217
218 private function normalizeUploadedFileIndexKeys($index) {
219 if ($index === null) {
220 return array();
221 }
222
223 if ($this->isUploadedFileKey($index)) {
224 return array($index);
225 }
226
227 if (!is_array($index) || empty($index)) {
228 return null;
229 }
230
231 foreach ($index as $key) {
232 if (!$this->isUploadedFileKey($key)) {
233 return null;
234 }
235 }
236
237 return array_values($index);
238 }
239
240 private function buildUploadedFileStatusKey($field_name, $index_keys) {
241 $status_key = (string) $field_name;
242 foreach ($index_keys as $key) {
243 $status_key .= '[' . (string) $key . ']';
244 }
245
246 return $status_key;
247 }
248
249 private function setUploadedFileContentStatus($field_name, $index_keys, $status) {
250 $status_key = $this->buildUploadedFileStatusKey($field_name, $index_keys);
251 if ($status_key !== '') {
252 $this->uploaded_file_content_statuses[$status_key] = $status;
253 }
254 }
255
256 private function resolveUploadedFileEntry($field_name) {
257 if (!$this->isUploadedFileKey($field_name)) {
258 return null;
259 }
260
261 if (!is_array($this->files) || !array_key_exists($field_name, $this->files) ||
262 !is_array($this->files[$field_name])) {
263 $this->setUploadedFileContentStatus($field_name, array(), 'missing_file');
264 return null;
265 }
266
267 return $this->files[$field_name];
268 }
269
270 private function getUploadedFileMetaValue($file_entry, $meta_key, $index_keys) {
271 if (!is_array($file_entry) || !array_key_exists($meta_key, $file_entry)) {
272 return null;
273 }
274
275 if (empty($index_keys)) {
276 return $file_entry[$meta_key];
277 }
278
279 return $this->getKeyVal($file_entry[$meta_key], $index_keys);
280 }
281
282 private function isUploadedFilePath($path) {
283 return is_string($path) && $path !== '' && is_uploaded_file($path);
284 }
285
286 private function normalizeUploadedFileSize($size) {
287 if (is_int($size)) {
288 return $size;
289 }
290
291 if (is_string($size) && preg_match('/^\d+$/', $size) === 1) {
292 return (int) $size;
293 }
294
295 return null;
296 }
297
298 private function resolveUploadedFileReadStatus($read_limit, $requested_limit, $per_file_limit, $limited_by_total,
299 $file_size, $content_length) {
300
301 if ($content_length === 0) {
302 return 'empty';
303 }
304
305 if (isset($file_size) && $file_size <= $content_length) {
306 return 'available';
307 }
308
309 if ($limited_by_total) {
310 return 'truncated_by_total_limit';
311 }
312
313 if (isset($file_size) && $file_size > $read_limit) {
314 if ($requested_limit > $per_file_limit) {
315 return 'truncated_by_config_limit';
316 }
317
318 return 'truncated_by_function_limit';
319 }
320
321 if (!isset($file_size) && $content_length >= $read_limit) {
322 if ($requested_limit > $per_file_limit) {
323 return 'truncated_by_config_limit';
324 }
325
326 if ($read_limit < $requested_limit) {
327 return 'truncated_by_function_limit';
328 }
329 }
330
331 return 'available';
332 }
333
334 private function readUploadedFileContent($file_entry, $field_name, $index_keys, $max_bytes) {
335 if (!$this->can_get_uploaded_file_content) {
336 $this->setUploadedFileContentStatus($field_name, $index_keys, 'disabled_by_config');
337 return null;
338 }
339
340 $error = $this->getUploadedFileMetaValue($file_entry, 'error', $index_keys);
341 if ((string) $error !== '0') {
342 $this->setUploadedFileContentStatus($field_name, $index_keys, 'upload_error');
343 return null;
344 }
345
346 $tmp_name = $this->getUploadedFileMetaValue($file_entry, 'tmp_name', $index_keys);
347 if (!is_string($tmp_name) || $tmp_name === '') {
348 $this->setUploadedFileContentStatus($field_name, $index_keys, 'missing_tmp_name');
349 return null;
350 }
351
352 if (!$this->isUploadedFilePath($tmp_name)) {
353 $this->setUploadedFileContentStatus($field_name, $index_keys, 'not_uploaded_file');
354 return null;
355 }
356
357 $requested_limit = max(0, (int) $max_bytes);
358 $per_file_limit = max(0, $this->max_uploaded_file_content_length);
359 $total_limit = max(0, $this->max_total_uploaded_file_content_length);
360 $configured_target = min($requested_limit, $per_file_limit);
361 if ($configured_target <= 0) {
362 $this->setUploadedFileContentStatus($field_name, $index_keys, 'empty');
363 return '';
364 }
365
366 $status_key = $this->buildUploadedFileStatusKey($field_name, $index_keys);
367 $cached_length = 0;
368 if (array_key_exists($status_key, $this->uploaded_file_content_cache)) {
369 $cached_length = strlen($this->uploaded_file_content_cache[$status_key]['content']);
370 }
371
372 $remaining_limit = max(0, $total_limit - $this->uploaded_file_content_bytes_read);
373 $read_limit = min($configured_target, $cached_length + $remaining_limit);
374 $limited_by_total = $read_limit < $configured_target;
375 if ($read_limit <= 0) {
376 $this->setUploadedFileContentStatus($field_name, $index_keys, 'total_limit_exceeded');
377 return null;
378 }
379
380 if (array_key_exists($status_key, $this->uploaded_file_content_cache) &&
381 $cached_length >= $read_limit) {
382
383 $content = substr($this->uploaded_file_content_cache[$status_key]['content'], 0, $read_limit);
384 } else {
385 $content = file_get_contents($tmp_name, false, null, 0, $read_limit);
386 if ($content === false) {
387 $this->setUploadedFileContentStatus($field_name, $index_keys, 'read_failed');
388 return null;
389 }
390
391 $this->uploaded_file_content_bytes_read += max(0, strlen($content) - $cached_length);
392 $this->uploaded_file_content_cache[$status_key] = array(
393 'content' => $content,
394 'limit' => $read_limit
395 );
396 }
397
398 $file_size = $this->normalizeUploadedFileSize($this->getUploadedFileMetaValue($file_entry, 'size', $index_keys));
399 $this->setUploadedFileContentStatus(
400 $field_name,
401 $index_keys,
402 $this->resolveUploadedFileReadStatus(
403 $read_limit,
404 $requested_limit,
405 $per_file_limit,
406 $limited_by_total,
407 $file_size,
408 strlen($content)
409 )
410 );
411
412 return $content;
413 }
414
415 private function readUploadedFileContents($file_entry, $field_name, $index_keys, $max_bytes) {
416 $tmp_name = $this->getUploadedFileMetaValue($file_entry, 'tmp_name', $index_keys);
417
418 if (is_array($tmp_name)) {
419 $contents = array();
420 foreach ($tmp_name as $key => $value) {
421 $contents[$key] = $this->readUploadedFileContents(
422 $file_entry,
423 $field_name,
424 array_merge($index_keys, array($key)),
425 $max_bytes
426 );
427 }
428 return $contents;
429 }
430
431 if ($tmp_name === null) {
432 $this->setUploadedFileContentStatus($field_name, $index_keys, 'missing_file');
433 return null;
434 }
435
436 return $this->readUploadedFileContent($file_entry, $field_name, $index_keys, $max_bytes);
437 }
438
439 private function loadRawBody() {
440 if ($this->raw_body_loaded) {
441 return;
442 }
443
444 $this->raw_body_loaded = true;
445 if (!$this->can_get_raw_body) {
446 $this->raw_body_status = 'disabled_by_config';
447 return;
448 }
449
450 $read_limit = max(0, $this->max_raw_body_length);
451 $_raw_body = file_get_contents("php://input", false, null, 0, $read_limit + 1);
452 if ($_raw_body === false) {
453 $this->raw_body_status = 'read_failed';
454 return;
455 }
456
457 $is_truncated = strlen($_raw_body) > $read_limit;
458 $this->raw_body = $_raw_body;
459 $this->raw_body_truncated = $is_truncated;
460 $this->raw_body_status = $is_truncated ? 'truncated_by_limit' : 'available';
461 }
462
463 private function loadJsonParams() {
464 if ($this->json_params_loaded) {
465 return;
466 }
467
468 $this->json_params_loaded = true;
469 if (!$this->can_decode_json) {
470 $this->json_params_status = 'disabled_by_config';
471 return;
472 }
473
474 if (!$this->isJsonContentType($this->getContentType())) {
475 $this->json_params_status = 'unsupported_content_type';
476 return;
477 }
478
479 $this->loadRawBody();
480 if (!in_array($this->raw_body_status, array('available', 'truncated_by_limit'), true)) {
481 $this->json_params_status = 'raw_body_unavailable';
482 return;
483 }
484
485 if ($this->raw_body_status === 'truncated_by_limit') {
486 $this->json_params_status = 'raw_body_truncated';
487 return;
488 }
489
490 $_json_params = WPRProtectUtils_V672::safeDecodeJSON(
491 $this->raw_body,
492 true,
493 $this->max_json_decode_depth
494 );
495 if (isset($_json_params)) {
496 $this->json_params['JSON'] = $_json_params;
497 $this->json_params_status = 'available';
498 } elseif (function_exists('json_last_error') && json_last_error() === JSON_ERROR_NONE) {
499 $this->json_params_status = 'decoded_null';
500 } else {
501 $this->json_params_status = 'decode_failed';
502 }
503 }
504
505 public function getPostParams() {
506 if (func_num_args() > 0) {
507 $args = func_get_args();
508 return $this->getKeyVal($this->post_params, $args);
509 }
510 return $this->post_params;
511 }
512
513 public function getCookies() {
514 if (func_num_args() > 0) {
515 $args = func_get_args();
516 return $this->getKeyVal($this->cookies, $args);
517 }
518 return $this->cookies;
519 }
520
521 public function getGetParams() {
522 if (func_num_args() > 0) {
523 $args = func_get_args();
524 return $this->getKeyVal($this->get_params, $args);
525 }
526 return $this->get_params;
527 }
528
529 public function getAllParams() {
530 return array("getParams" => $this->get_params, "postParams" => $this->post_params, "jsonParams" => $this->getJsonParams());
531 }
532
533 public function getHeader($key) {
534 $key = $this->normalizeHeaderName($key);
535 return isset($key) && array_key_exists($key, $this->headers) ? $this->headers[$key] : null;
536 }
537
538 public function getHeaders() {
539 if (func_num_args() > 0) {
540 $args = func_get_args();
541 $args[0] = $this->normalizeHeaderName($args[0]);
542 if (!isset($args[0])) {
543 return null;
544 }
545 return $this->getKeyVal($this->headers, $args);
546 }
547 return $this->headers;
548 }
549
550 public function getFiles() {
551 if (func_num_args() > 0) {
552 $args = func_get_args();
553 return $this->getKeyVal($this->files, $args);
554 }
555 return $this->files;
556 }
557
558 public function getFileNames() {
559 if (func_num_args() > 0) {
560 $args = func_get_args();
561 return $this->getKeyVal($this->file_names, $args);
562 }
563 return $this->file_names;
564 }
565
566 public function getUploadedFileContent($field_name, $max_bytes, $index = null) {
567 $index_keys = $this->normalizeUploadedFileIndexKeys($index);
568 if (!is_array($index_keys)) {
569 return null;
570 }
571
572 $file_entry = $this->resolveUploadedFileEntry($field_name);
573 if (!is_array($file_entry)) {
574 return null;
575 }
576
577 return $this->readUploadedFileContents($file_entry, $field_name, $index_keys, $max_bytes);
578 }
579
580 public function getUploadedFileMeta($field_name, $meta_key, $index = null) {
581 if (!is_string($meta_key) || $meta_key === '') {
582 return null;
583 }
584
585 $index_keys = $this->normalizeUploadedFileIndexKeys($index);
586 if (!is_array($index_keys)) {
587 return null;
588 }
589
590 $file_entry = $this->resolveUploadedFileEntry($field_name);
591 if (!is_array($file_entry)) {
592 return null;
593 }
594
595 $meta_value = $this->getUploadedFileMetaValue($file_entry, $meta_key, $index_keys);
596 if ($meta_key === 'size' && !is_array($meta_value)) {
597 return $this->normalizeUploadedFileSize($meta_value);
598 }
599
600 return $meta_value;
601 }
602
603 public function getUploadedFileContentStatus($field_name = null, $index = null) {
604 if ($field_name === null) {
605 return $this->uploaded_file_content_statuses;
606 }
607
608 if (!$this->isUploadedFileKey($field_name)) {
609 return null;
610 }
611
612 $index_keys = $this->normalizeUploadedFileIndexKeys($index);
613 if (!is_array($index_keys)) {
614 return null;
615 }
616
617 $status_key = $this->buildUploadedFileStatusKey($field_name, $index_keys);
618 return array_key_exists($status_key, $this->uploaded_file_content_statuses) ?
619 $this->uploaded_file_content_statuses[$status_key] : null;
620 }
621
622 public function getHost() {
623 return $this->host;
624 }
625
626 public function getURI() {
627 return $this->uri;
628 }
629
630 public function getAction() {
631 $post_action = $this->getPostParams('action');
632 if (isset($post_action)) {
633 return $post_action;
634 } else {
635 return $this->getGetParams('action');
636 }
637 }
638
639 public function getPath() {
640 return $this->path;
641 }
642
643 public function getIP() {
644 return $this->ip;
645 }
646
647 public function getMethod() {
648 return $this->method;
649 }
650
651 public function getTimestamp() {
652 return $this->timestamp;
653 }
654
655 public function getRequestID() {
656 if (!defined("WPR_REQUEST_ID")) {
657 define("WPR_REQUEST_ID", uniqid(mt_rand())); // phpcs:ignore WordPress.WP.AlternativeFunctions.rand_mt_rand
658 }
659
660 return WPR_REQUEST_ID;
661 }
662
663 public function getServerValue($key) {
664 $val = WPRHelper::getRawParam('SERVER', $key);
665 return isset($val) ? $val : false;
666 }
667
668 public function getHeadersV2() {
669 return $this->headers;
670 }
671
672 public function getFilesV2() {
673 return $this->files;
674 }
675
676 public function getFileNamesV2() {
677 return $this->file_names;
678 }
679
680 public function getPostParamsV2() {
681 return $this->post_params;
682 }
683
684 public function getGetParamsV2() {
685 return $this->get_params;
686 }
687
688 public function getCookiesV2() {
689 return $this->cookies;
690 }
691
692 public function getJsonParams() {
693 $this->loadJsonParams();
694 return $this->json_params;
695 }
696
697 public function getRawBody() {
698 $this->loadRawBody();
699 return $this->raw_body;
700 }
701
702 public function getBodyParserStatus() {
703 return array(
704 'raw_body_status' => $this->raw_body_status,
705 'raw_body_truncated' => $this->raw_body_truncated,
706 'json_params_status' => $this->json_params_status,
707 'uploaded_file_content_statuses' => $this->uploaded_file_content_statuses,
708 'uploaded_file_content_bytes_read' => $this->uploaded_file_content_bytes_read,
709 'max_uploaded_file_content_length' => $this->max_uploaded_file_content_length,
710 'max_total_uploaded_file_content_length' => $this->max_total_uploaded_file_content_length
711 );
712 }
713
714 public function getContentType() {
715 return $this->getHeader('Content-Type');
716 }
717
718 public function getContentLength() {
719 return $this->getHeader('Content-Length');
720 }
721 }
722 endif;