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
wpremote / helper.php

helper.php in The WP Remote WordPress Plugin trunk, at helper.php

411 lines 12.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 if (!defined('ABSPATH') && !defined('MCDATAPATH') && !defined('PHP_ERR_MONIT_PATH')) exit;
3
4 if (!class_exists('WPRHelper')) :
5 class WPRHelper {
6 const MIN_SALT_LENGTH = 32;
7 const SALT_PLACEHOLDER = 'put your unique phrase here';
8 const SALT_CONSTANTS = array(
9 'AUTH_KEY', 'AUTH_SALT', 'SECURE_AUTH_KEY', 'SECURE_AUTH_SALT',
10 'LOGGED_IN_KEY', 'LOGGED_IN_SALT', 'NONCE_KEY', 'NONCE_SALT'
11 );
12
13 public static function configSalt($constant) {
14 if (!defined($constant)) {
15 return null;
16 }
17
18 $value = constant($constant);
19 if (!is_string($value) || strlen($value) < self::MIN_SALT_LENGTH) {
20 return null;
21 }
22
23 if (self::isSaltPlaceholder($value) || self::isSharedSalt($constant, $value)) {
24 return null;
25 }
26
27 return $value;
28 }
29
30 private static function isSaltPlaceholder($value) {
31 if ($value === self::SALT_PLACEHOLDER) {
32 return true;
33 }
34
35 #wp-config-sample.php is localized for some locales, so the placeholder is
36 #not always the English string. wp_salt() guards the translated form too.
37 // phpcs:ignore WordPress.WP.I18n.MissingArgDomain
38 return function_exists('__') && $value === __('put your unique phrase here');
39 }
40
41 private static function isSharedSalt($constant, $value) {
42 foreach (self::SALT_CONSTANTS as $other) {
43 if ($other !== $constant && defined($other) && constant($other) === $value) {
44 return true;
45 }
46 }
47
48 return false;
49 }
50
51 public static function safePregMatch($pattern, $subject, &$matches = null, $flags = 0, $offset = 0) {
52 if (!is_string($pattern) || !is_string($subject)) {
53 return false;
54 }
55 return preg_match($pattern, $subject, $matches, $flags, $offset);
56 }
57
58 # XNOTE - The below function assumes valid input
59 # $array should be an array and $keys should be an array of string, or integer data
60 public static function filterArray($array, $keys) {
61 $filteredArray = array();
62 foreach ($keys as $key) {
63 if (array_key_exists($key, $array)) {
64 $filteredArray[$key] = $array[$key];
65 }
66 }
67 return $filteredArray;
68 }
69
70 # XNOTE - The below function assumes valid input
71 # $array should be an array and $keys should be an array of string, or integer data
72 public static function digArray($array, $keys) {
73 if (empty($keys)) {
74 return null;
75 }
76 $curr_array = $array;
77 foreach ($keys as $key) {
78 if (is_array($curr_array) && array_key_exists($key, $curr_array)) {
79 $curr_array = $curr_array[$key];
80 } else {
81 return null;
82 }
83 }
84 return $curr_array;
85 }
86
87 public static function arrayKeyFirst($array) {
88 if (!function_exists('array_key_first')) {
89 foreach ($array as $key => $value) {
90 return $key;
91 }
92 return null;
93 }
94
95 return array_key_first($array);
96 }
97
98 public static function safePregReplace($replace_regex, $replace_string, $element, $limit = -1) {
99 if (!is_string($replace_regex) || !is_string($replace_string) || !is_string($element) || !is_int($limit)) {
100 return $element;
101 }
102
103 $updated_element = preg_replace($replace_regex, $replace_string, $element, $limit);
104
105 if ($updated_element === null && preg_last_error() !== PREG_NO_ERROR) {
106 return $element;
107 }
108
109 return $updated_element;
110 }
111
112 public static function safeStrReplace($search, $replace, $subject) {
113 if (!is_string($search) || !is_string($replace) || !is_string($subject)) {
114 return $subject;
115 }
116 $updated_subject = str_replace($search, $replace, $subject);
117 if ($updated_subject === null) {
118 return $subject;
119 }
120 return $updated_subject;
121 }
122
123 public static function safeStrReplaceFirst($search, $replace, $subject) {
124 if (!is_string($search) || !is_string($replace) || !is_string($subject) || $search === '') {
125 return $subject;
126 }
127
128 $position = strpos($subject, $search);
129 if ($position === false) {
130 return $subject;
131 }
132
133 return substr_replace($subject, $replace, $position, strlen($search));
134 }
135
136 public static function preInitWPHook($hook_name, $function_name, $priority, $accepted_args) {
137 global $wp_filter;
138
139 // Check if $wp_filter is not initialized or not an array
140 if (!isset($wp_filter) || !is_array($wp_filter)) {
141 $wp_filter = array();
142 }
143
144 // Check if the hook exists in $wp_filter
145 if (!isset($wp_filter[$hook_name])) {
146 $wp_filter[$hook_name] = array();
147 }
148
149 // Check if the priority exists for the hook
150 if (!isset($wp_filter[$hook_name][$priority])) {
151 $wp_filter[$hook_name][$priority] = array();
152 }
153
154 // Add the filter function information to the $wp_filter array
155 $wp_filter[$hook_name][$priority][] = array(
156 'function' => $function_name,
157 'accepted_args' => $accepted_args,
158 );
159 }
160
161 public static function removePatternFromWpConfig($pattern) {
162 if (!defined('ABSPATH')) {
163 return;
164 }
165
166 $wp_conf_paths = array(
167 rtrim(ABSPATH, DIRECTORY_SEPARATOR) . "/wp-config.php",
168 rtrim(ABSPATH, DIRECTORY_SEPARATOR) . "../wp-config.php"
169 );
170
171 if (file_exists($wp_conf_paths[0])) {
172 $fname = $wp_conf_paths[0];
173 } elseif (file_exists($wp_conf_paths[1])) {
174 $fname = $wp_conf_paths[1];
175 } else {
176 return;
177 }
178
179 self::fileRemovePattern($fname, $pattern);
180 }
181
182 public static function fileRemovePattern($fname, $pattern, $is_regex = false) {
183 if (!is_string($fname) || !is_string($pattern)) {
184 return;
185 }
186
187 if (!WPRWPFileSystem::getInstance()->exists($fname)) {
188 return;
189 }
190
191 $content = WPRWPFileSystem::getInstance()->getContents($fname);
192 if ($content !== false) {
193 if ($is_regex !== false) {
194 $modified_content = preg_replace($pattern, "", $content);
195 } else {
196 $modified_content = str_replace($pattern, "", $content);
197 }
198
199 if (empty($modified_content)) {
200 return;
201 }
202
203 if ($content !== $modified_content) {
204 WPRWPFileSystem::getInstance()->putContents($fname, $modified_content,
205 WPRWPFileSystem::getInstance()->getchmodOctal($fname));
206 }
207 }
208 }
209
210 public static function opensslEncrypt($plain_text, $cipher_algo, $encryption_key, $iv = null) {
211 if (!function_exists('openssl_encrypt') || !function_exists('openssl_get_cipher_methods') ||
212 !function_exists('openssl_random_pseudo_bytes') || !function_exists('openssl_cipher_iv_length')) {
213 return array(false, "OpenSSL extension not found.");
214 }
215
216 if (empty($plain_text) || !is_string($plain_text) ||
217 empty($encryption_key) || !is_string($encryption_key)) {
218 return array(false, "Plain text or encryption key is not a valid string.");
219 }
220
221 if (!in_array($cipher_algo, openssl_get_cipher_methods(), true)) {
222 return array(false, "Invalid cipher algorithm - " . $cipher_algo);
223 }
224
225 if ($iv === null) {
226 $iv_length = openssl_cipher_iv_length($cipher_algo);
227 if ($iv_length === false) {
228 return array(false, "IV length not found.");
229 }
230 $iv = openssl_random_pseudo_bytes($iv_length);
231 if ($iv === false) {
232 return array(false, "IV generation failed.");
233 }
234 }
235
236 if (strlen($iv) !== $iv_length) {
237 return array(false, "Invalid IV length. Expected length is " . $iv_length . " bytes.");
238 }
239
240 $encrypted_data = openssl_encrypt($plain_text, $cipher_algo, $encryption_key, OPENSSL_RAW_DATA, $iv);
241 if ($encrypted_data === false) {
242 return array(false, "Encryption failed.");
243 }
244
245 return array(true, ($iv . $encrypted_data));
246 }
247
248 public static function opensslDecrypt($data, $cipher_algo, $encryption_key) {
249 if (!function_exists('openssl_decrypt') || !function_exists('openssl_get_cipher_methods') ||
250 !function_exists('openssl_cipher_iv_length')) {
251 return array(false, "OpenSSL extension not found.");
252 }
253
254 if (empty($data) || !is_string($data) || empty($encryption_key) || !is_string($encryption_key)) {
255 return array(false, "Encrypted secret or encryption key is not a valid string.");
256 }
257
258 if (!in_array($cipher_algo, openssl_get_cipher_methods(), true)) {
259 return array(false, "Invalid cipher algorithm - " . $cipher_algo);
260 }
261
262 $iv_length = openssl_cipher_iv_length($cipher_algo);
263 if ($iv_length === false) {
264 return array(false, "IV length not found.");
265 }
266
267 if (strlen($data) <= $iv_length) {
268 return array(false, "Data length is insufficient to contain IV.");
269 }
270
271 $iv = substr($data, 0, $iv_length);
272 $encrypted_data = substr($data, $iv_length);
273
274 if ($iv === false || $encrypted_data === false) {
275 return array(false, "IV or encrypted data not found.");
276 }
277
278 $decrypted_data = openssl_decrypt($encrypted_data, $cipher_algo, $encryption_key, OPENSSL_RAW_DATA, $iv);
279
280 if ($decrypted_data === false) {
281 return array(false, "Decryption failed.");
282 }
283
284 return array(true, $decrypted_data);
285 }
286
287 public static function get_direct_filesystem() {
288 require_once ABSPATH . 'wp-admin/includes/class-wp-filesystem-base.php';
289 require_once ABSPATH . 'wp-admin/includes/class-wp-filesystem-direct.php';
290 return new WP_Filesystem_Direct(new StdClass());
291 }
292
293 /**
294 * Maybe unslash a value if WordPress is loaded
295 *
296 * @param string $value The value to potentially unslash
297 * @return string The unslashed value if WP is loaded, original value otherwise
298 */
299 public static function maybeUnslashValue($value) {
300 if (function_exists('wp_unslash')) {
301 return wp_unslash($value);
302 }
303 return $value;
304 }
305
306 /**
307 * Get and sanitize a string parameter from superglobal
308 *
309 * @param string $superglobal The superglobal type ('GET', 'POST', etc.)
310 * @param string $key The parameter key to retrieve
311 * @param string $context The sanitization context ('text', 'email', 'url')
312 * @return string|null Sanitized string value or null if invalid or unknown context
313 */
314 public static function getStringParamSanitized($superglobal, $key, $context) {
315 $raw_value = self::getRawParam($superglobal, $key);
316
317 if (!is_string($raw_value)) {
318 return null;
319 }
320
321 switch ($context) {
322 case 'text':
323 if (!function_exists('sanitize_text_field')) {
324 return null;
325 }
326 return sanitize_text_field($raw_value);
327 case 'email':
328 if (!function_exists('sanitize_email')) {
329 return null;
330 }
331 return sanitize_email($raw_value);
332 case 'url':
333 if (!function_exists('esc_url_raw')) {
334 return null;
335 }
336 return esc_url_raw($raw_value);
337 default:
338 return null;
339 }
340 }
341
342 /**
343 * Get and escape a string parameter from superglobal
344 *
345 * @param string $superglobal The superglobal type ('GET', 'POST', etc.)
346 * @param string $key The parameter key to retrieve
347 * @param string $context The escaping context ('attr', 'html', 'url')
348 * @return string|null Escaped string value or null if invalid or unknown context
349 */
350 public static function getStringParamEscaped($superglobal, $key, $context) {
351 $raw_value = self::getRawParam($superglobal, $key);
352
353 if (!is_string($raw_value)) {
354 return null;
355 }
356
357 switch ($context) {
358 case 'attr':
359 if (!function_exists('esc_attr')) {
360 return null;
361 }
362 return esc_attr($raw_value);
363 case 'html':
364 if (!function_exists('esc_html')) {
365 return null;
366 }
367 return esc_html($raw_value);
368 case 'url':
369 if (!function_exists('esc_url')) {
370 return null;
371 }
372 return esc_url($raw_value);
373 default:
374 return null;
375 }
376 }
377
378 // phpcs:disable WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.NonceVerification.Missing
379 /**
380 * Get raw parameter value from superglobal
381 *
382 * @param string $superglobal The superglobal type ('GET', 'POST', etc.)
383 * @param string $key The parameter key to retrieve
384 * @return mixed Raw parameter value or null if not found
385 */
386 public static function getRawParam($superglobal, $key) {
387 $value = null;
388
389 switch (strtoupper($superglobal)) {
390 case 'GET':
391 $value = isset($_GET[$key]) ? $_GET[$key] : null;
392 break;
393 case 'POST':
394 $value = isset($_POST[$key]) ? $_POST[$key] : null;
395 break;
396 case 'COOKIE':
397 $value = isset($_COOKIE[$key]) ? $_COOKIE[$key] : null;
398 break;
399 case 'REQUEST':
400 $value = isset($_REQUEST[$key]) ? $_REQUEST[$key] : null;
401 break;
402 case 'SERVER':
403 $value = isset($_SERVER[$key]) ? $_SERVER[$key] : null;
404 break;
405 }
406
407 return $value !== null ? self::maybeUnslashValue($value) : null;
408 }
409 // phpcs:enable WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.NonceVerification.Missing
410 }
411 endif;