PluginProbe
The WP Remote WordPress Plugin / trunk
The WP Remote WordPress Plugin vtrunk
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
← All changes | protect/request.php +428 -42 6.48trunk View file →
@@ -1,10 +1,10 @@
1 1 <?php
2 2
3 3 if (!defined('ABSPATH') && !defined('MCDATAPATH')) exit;
4 4
5 -if (!class_exists('WPRProtectRequest_V648')) :
6 -class WPRProtectRequest_V648 {
5 +if (!class_exists('WPRProtectRequest_V672')) :
6 +class WPRProtectRequest_V672 {
7 7 public $ip;
8 8 public $host = '';
9 9 public $uri;
10 10 public $method = '';
@@ -18,18 +18,31 @@
18 18 public $json_params = array();
19 19 public $raw_body = '';
20 20 public $files;
21 21 public $respcode;
22 - public $status = WPRProtectRequest_V648::STATUS_ALLOWED;
23 - public $category = WPRProtectRequest_V648::CATEGORY_NORMAL;
22 + public $status = WPRProtectRequest_V672::STATUS_ALLOWED;
23 + public $category = WPRProtectRequest_V672::CATEGORY_NORMAL;
24 24
25 25 public $wp_user;
26 26
27 27 private $can_get_raw_body = false;
28 + private $can_decode_json = false;
29 + private $can_get_uploaded_file_content = false;
30 +
28 31 private $max_raw_body_length = 1000000;
29 - private $can_decode_json = false;
30 32 private $max_json_decode_depth = 512;
33 + private $max_uploaded_file_content_length = 8192;
34 + private $max_total_uploaded_file_content_length = 65536;
31 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 +
32 45 #XNOTE: SHould be part of Protect.
33 46 const STATUS_ALLOWED = 1;
34 47 const STATUS_BLOCKED = 2;
35 48 const STATUS_BYPASSED = 3;
@@ -45,9 +58,9 @@
45 58 const CATEGORY_PRIVATEIP = 80;
46 59 const CATEGORY_GLOBAL_BOT_BLOCKED = 90;
47 60
48 61 public function __construct($ip_header, $config) {
49 - $this->ip = WPRProtectUtils_V648::getIP($ip_header);
62 + $this->ip = WPRProtectUtils_V672::getIP($ip_header);
50 63 $this->timestamp = time();
51 64 $this->get_params = $_GET; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
52 65 $this->cookies = $_COOKIE;
53 66 $this->post_params = $_POST; // phpcs:ignore WordPress.Security.NonceVerification.Missing
@@ -68,8 +81,20 @@
68 81 if (array_key_exists('maxjsondecodedepth', $config) && is_int($config['maxjsondecodedepth'])) {
69 82 $this->max_json_decode_depth = $config['maxjsondecodedepth'];
70 83 }
71 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 +
72 97 if (!empty($_FILES)) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
73 98 foreach ($_FILES as $input => $file) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
74 99 $this->file_names[$input] = $file['name'];
75 100 }
@@ -76,12 +101,9 @@
76 101 }
77 102 if (is_array($_SERVER)) {
78 103 foreach ($_SERVER as $key => $value) {
79 104 if (strpos($key, 'HTTP_') === 0) {
80 - $header = substr($key, 5);
81 - $header = str_replace(array(' ', '_'), array('', ' '), $header);
82 - $header = ucwords(strtolower($header));
83 - $header = str_replace(' ', '-', $header);
105 + $header = $this->normalizeHeaderName($key);
84 106 $this->headers[$header] = $value;
85 107 }
86 108 }
87 109 $content_type = WPRHelper::getRawParam('SERVER', 'CONTENT_TYPE');
@@ -114,37 +136,21 @@
114 136 $_uri = parse_url($this->uri);
115 137 $this->path = (is_array($_uri) && array_key_exists('path', $_uri)) ? $_uri['path'] : $this->uri;
116 138 }
117 139
118 - if ($this->can_get_raw_body) {
119 - $_raw_body = file_get_contents("php://input", false, null, 0, $this->max_raw_body_length);
120 - if ($_raw_body !== false) {
121 - $this->raw_body = $_raw_body;
122 - }
123 - }
124 -
125 - if ($this->can_decode_json) {
126 - if ($this->getContentType() === "application/json" && !empty($this->raw_body)) {
127 - $_json_params = WPRProtectUtils_V648::safeDecodeJSON($this->raw_body,
128 - true, $this->max_json_decode_depth);
129 - if (isset($_json_params)) {
130 - $this->json_params['JSON'] = $_json_params;
131 - }
132 - }
133 - }
134 140 }
135 141
136 142 public static function blacklistedCategories() {
137 143 return array(
138 - WPRProtectRequest_V648::CATEGORY_BOT_BLOCKED,
139 - WPRProtectRequest_V648::CATEGORY_COUNTRY_BLOCKED,
140 - WPRProtectRequest_V648::CATEGORY_USER_BLACKLISTED,
141 - WPRProtectRequest_V648::CATEGORY_GLOBAL_BOT_BLOCKED
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
142 148 );
143 149 }
144 150
145 151 public static function whitelistedCategories() {
146 - return array(WPRProtectRequest_V648::CATEGORY_WHITELISTED);
152 + return array(WPRProtectRequest_V672::CATEGORY_WHITELISTED);
147 153 }
148 154
149 155 public function setRespCode($code) {
150 156 $this->respcode = $code;
@@ -183,8 +189,320 @@
183 189 }
184 190 return null;
185 191 }
186 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 +
187 505 public function getPostParams() {
188 506 if (func_num_args() > 0) {
189 507 $args = func_get_args();
190 508 return $this->getKeyVal($this->post_params, $args);
@@ -208,21 +526,23 @@
208 526 return $this->get_params;
209 527 }
210 528
211 529 public function getAllParams() {
212 - return array("getParams" => $this->get_params, "postParams" => $this->post_params, "jsonParams" => $this->json_params);
530 + return array("getParams" => $this->get_params, "postParams" => $this->post_params, "jsonParams" => $this->getJsonParams());
213 531 }
214 532
215 533 public function getHeader($key) {
216 - if (array_key_exists($key, $this->headers)) {
217 - return $this->headers[$key];
218 - }
219 - return null;
534 + $key = $this->normalizeHeaderName($key);
535 + return isset($key) && array_key_exists($key, $this->headers) ? $this->headers[$key] : null;
220 536 }
221 537
222 538 public function getHeaders() {
223 539 if (func_num_args() > 0) {
224 540 $args = func_get_args();
541 + $args[0] = $this->normalizeHeaderName($args[0]);
542 + if (!isset($args[0])) {
543 + return null;
544 + }
225 545 return $this->getKeyVal($this->headers, $args);
226 546 }
227 547 return $this->headers;
228 548 }
@@ -242,8 +562,64 @@
242 562 }
243 563 return $this->file_names;
244 564 }
245 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 +
246 622 public function getHost() {
247 623 return $this->host;
248 624 }
249 625
@@ -313,24 +689,34 @@
313 689 return $this->cookies;
314 690 }
315 691
316 692 public function getJsonParams() {
693 + $this->loadJsonParams();
317 694 return $this->json_params;
318 695 }
319 696
320 697 public function getRawBody() {
698 + $this->loadRawBody();
321 699 return $this->raw_body;
322 700 }
323 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 +
324 714 public function getContentType() {
325 - if (array_key_exists('Content-Type', $this->headers)) {
326 - return $this->headers['Content-Type'];
327 - }
715 + return $this->getHeader('Content-Type');
328 716 }
329 717
330 718 public function getContentLength() {
331 - if (array_key_exists('Content-Length', $this->headers)) {
332 - return $this->headers['Content-Length'];
333 - }
719 + return $this->getHeader('Content-Length');
334 720 }
335 721 }
336 722 endif;