This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | |||
3 | namespace HM\BackUpWordPress; |
||
4 | |||
5 | /** |
||
6 | * Manages both the backup path and site root |
||
7 | * |
||
8 | * Handles calculating & protecting the directory that backups will be stored in |
||
9 | * as well as the directory that is being backed up |
||
10 | */ |
||
11 | class Path { |
||
12 | |||
13 | /** |
||
14 | * The path to the directory that backup files are stored in |
||
15 | * |
||
16 | * @var string $this->path |
||
17 | */ |
||
18 | private $path; |
||
19 | |||
20 | /** |
||
21 | * The path to the directory that will be backed up |
||
22 | * |
||
23 | * @var string $this->root |
||
24 | */ |
||
25 | private $root; |
||
26 | |||
27 | /** |
||
28 | * The path to the directory that backup files are stored in |
||
29 | * |
||
30 | * @var string $this->path |
||
31 | */ |
||
32 | private $custom_path; |
||
33 | |||
34 | /** |
||
35 | * Contains the instantiated Path instance |
||
36 | * |
||
37 | * @var Path $this->instance |
||
38 | */ |
||
39 | private static $instance; |
||
40 | |||
41 | /** |
||
42 | * Protected constructor to prevent creating a new instance of the |
||
43 | * *Singleton* via the `new` operator from outside of this class. |
||
44 | */ |
||
45 | protected function __construct() {} |
||
46 | |||
47 | /** |
||
48 | * Private clone method to prevent cloning of the instance of the |
||
49 | * *Singleton* instance. |
||
50 | */ |
||
51 | private function __clone() {} |
||
52 | |||
53 | /** |
||
54 | * Private unserialize method to prevent unserializing of the *Singleton* |
||
55 | * instance. |
||
56 | */ |
||
57 | private function __wakeup() {} |
||
58 | |||
59 | /** |
||
60 | * Returns the *Singleton* instance of this class. |
||
61 | * |
||
62 | * @staticvar Path $instance The *Singleton* instances of this class. |
||
63 | * |
||
64 | * @return Path The *Singleton* instance. |
||
65 | */ |
||
66 | public static function get_instance() { |
||
67 | |||
68 | if ( ! ( self::$instance instanceof Path ) ) { |
||
69 | self::$instance = new Path(); |
||
70 | } |
||
71 | |||
72 | return self::$instance; |
||
73 | } |
||
74 | |||
75 | /** |
||
76 | * Convenience method for quickly grabbing the path |
||
77 | */ |
||
78 | public static function get_path() { |
||
0 ignored issues
–
show
|
|||
79 | return self::get_instance()->get_calculated_path(); |
||
80 | } |
||
81 | |||
82 | /** |
||
83 | * Convenience method for quickly grabbing the root |
||
84 | */ |
||
85 | public static function get_root() { |
||
0 ignored issues
–
show
The return type could not be reliably inferred; please add a
@return annotation.
Our type inference engine in quite powerful, but sometimes the code does not
provide enough clues to go by. In these cases we request you to add a ![]() |
|||
86 | return self::get_instance()->get_calculated_root(); |
||
87 | } |
||
88 | |||
89 | /** |
||
90 | * Calculate the path to the site "home" directory. |
||
91 | * |
||
92 | * The home directory is the path equivalent to the home_url. That is, |
||
93 | * the path to the true root of the website. In situations where WordPress is |
||
94 | * installed in a subdirectory the home path is different to ABSPATH |
||
95 | * |
||
96 | * @param string $site_path The site_path to use when calculating the home path, defaults to ABSPATH |
||
97 | */ |
||
98 | public static function get_home_path( $site_path = ABSPATH ) { |
||
0 ignored issues
–
show
The return type could not be reliably inferred; please add a
@return annotation.
Our type inference engine in quite powerful, but sometimes the code does not
provide enough clues to go by. In these cases we request you to add a ![]() |
|||
99 | |||
100 | if ( defined( 'HMBKP_ROOT' ) && HMBKP_ROOT ) { |
||
101 | return wp_normalize_path( HMBKP_ROOT ); |
||
102 | } |
||
103 | |||
104 | $home_path = wp_normalize_path( $site_path ); |
||
105 | |||
106 | if ( path_in_php_open_basedir( dirname( $site_path ) ) ) { |
||
107 | |||
108 | $home = set_url_scheme( get_option( 'home' ), 'http' ); |
||
109 | $siteurl = set_url_scheme( get_option( 'siteurl' ), 'http' ); |
||
110 | if ( ! empty( $home ) && 0 !== strcasecmp( $home, $siteurl ) ) { |
||
111 | $wp_path_rel_to_home = str_ireplace( $home, '', $siteurl ); /* $siteurl - $home */ |
||
112 | $pos = strripos( wp_normalize_path( $_SERVER['SCRIPT_FILENAME'] ), trailingslashit( $wp_path_rel_to_home ) ); |
||
113 | $home_path = substr( wp_normalize_path( $_SERVER['SCRIPT_FILENAME'] ), 0, $pos ); |
||
114 | $home_path = trailingslashit( $home_path ); |
||
115 | } |
||
116 | |||
117 | if ( is_multisite() ) { |
||
118 | $slashed_home = trailingslashit( get_option( 'home' ) ); |
||
119 | $base = parse_url( $slashed_home, PHP_URL_PATH ); |
||
120 | $document_root_fix = wp_normalize_path( realpath( $_SERVER['DOCUMENT_ROOT'] ) ); |
||
121 | $abspath_fix = wp_normalize_path( ABSPATH ); |
||
122 | $home_path = strpos( $abspath_fix, $document_root_fix ) === 0 ? $document_root_fix . $base : $home_path; |
||
123 | } |
||
124 | } |
||
125 | |||
126 | return wp_normalize_path( untrailingslashit( $home_path ) ); |
||
127 | |||
128 | } |
||
129 | |||
130 | /** |
||
131 | * get the calculated path to the directory where backups will be stored |
||
132 | */ |
||
133 | private function get_calculated_path() { |
||
0 ignored issues
–
show
The return type could not be reliably inferred; please add a
@return annotation.
Our type inference engine in quite powerful, but sometimes the code does not
provide enough clues to go by. In these cases we request you to add a ![]() |
|||
134 | |||
135 | // Calculate the path if needed |
||
136 | if ( empty( $this->path ) || ! wp_is_writable( $this->path ) ) { |
||
137 | $this->calculate_path(); |
||
138 | } |
||
139 | |||
140 | // Ensure the backup directory is protected |
||
141 | $this->protect_path(); |
||
142 | |||
143 | return wp_normalize_path( $this->path ); |
||
144 | |||
145 | } |
||
146 | |||
147 | /** |
||
148 | * Set the path directly, overriding the default |
||
149 | * |
||
150 | * @param $path |
||
151 | */ |
||
152 | public function set_path( $path ) { |
||
153 | |||
154 | $this->custom_path = $path; |
||
155 | |||
156 | // Re-calculate the backup path |
||
157 | $this->calculate_path(); |
||
158 | |||
159 | } |
||
160 | |||
161 | /** |
||
162 | * get the calculated path to the directory that will be backed up |
||
163 | */ |
||
164 | private function get_calculated_root() { |
||
0 ignored issues
–
show
The return type could not be reliably inferred; please add a
@return annotation.
Our type inference engine in quite powerful, but sometimes the code does not
provide enough clues to go by. In these cases we request you to add a ![]() |
|||
165 | |||
166 | $root = self::get_home_path(); |
||
167 | |||
168 | if ( defined( 'HMBKP_ROOT' ) && HMBKP_ROOT ) { |
||
169 | $root = HMBKP_ROOT; |
||
170 | } |
||
171 | |||
172 | if ( $this->root ) { |
||
173 | $root = $this->root; |
||
174 | } |
||
175 | |||
176 | return wp_normalize_path( $root ); |
||
177 | |||
178 | } |
||
179 | |||
180 | /** |
||
181 | * Set the root path directly, overriding the default |
||
182 | * |
||
183 | * @param $root |
||
184 | */ |
||
185 | public function set_root( $root ) { |
||
186 | $this->root = $root; |
||
187 | } |
||
188 | |||
189 | public function reset_path() { |
||
190 | $this->path = $this->custom_path = ''; |
||
191 | } |
||
192 | |||
193 | /** |
||
194 | * Get the path to the default backup location in wp-content |
||
195 | */ |
||
196 | public function get_default_path() { |
||
197 | return trailingslashit( wp_normalize_path( WP_CONTENT_DIR ) ) . 'backupwordpress-' . substr( HMBKP_SECURE_KEY, 0, 10 ) . '-backups'; |
||
198 | } |
||
199 | |||
200 | /** |
||
201 | * Get the path to the fallback backup location in uploads |
||
202 | */ |
||
203 | public function get_fallback_path() { |
||
204 | |||
205 | $upload_dir = wp_upload_dir(); |
||
206 | |||
207 | return trailingslashit( wp_normalize_path( $upload_dir['basedir'] ) ) . 'backupwordpress-' . substr( HMBKP_SECURE_KEY, 0, 10 ) . '-backups'; |
||
208 | |||
209 | } |
||
210 | |||
211 | /** |
||
212 | * Get the path to the custom backup location if it's been set |
||
213 | */ |
||
214 | public function get_custom_path() { |
||
0 ignored issues
–
show
The return type could not be reliably inferred; please add a
@return annotation.
Our type inference engine in quite powerful, but sometimes the code does not
provide enough clues to go by. In these cases we request you to add a ![]() |
|||
215 | |||
216 | if ( $this->custom_path ) { |
||
217 | return $this->custom_path; |
||
218 | } |
||
219 | |||
220 | if ( defined( 'HMBKP_PATH' ) && wp_is_writable( HMBKP_PATH ) ) { |
||
221 | return HMBKP_PATH; |
||
222 | } |
||
223 | |||
224 | return ''; |
||
225 | |||
226 | } |
||
227 | |||
228 | /** |
||
229 | * Builds an array containing existing backups folders. |
||
230 | * |
||
231 | * @return array |
||
232 | */ |
||
233 | public function get_existing_paths() { |
||
234 | |||
235 | if ( false === $default = glob( WP_CONTENT_DIR . '/backupwordpress-*-backups', GLOB_ONLYDIR ) ) { |
||
236 | $default = array(); |
||
237 | } |
||
238 | |||
239 | $upload_dir = wp_upload_dir(); |
||
240 | |||
241 | if ( false === $fallback = glob( $upload_dir['basedir'] . '/backupwordpress-*-backups', GLOB_ONLYDIR ) ) { |
||
242 | $fallback = array(); |
||
243 | } |
||
244 | |||
245 | $paths = array_merge( $default, $fallback ); |
||
246 | $paths = array_map( 'wp_normalize_path', $paths ); |
||
247 | |||
248 | return $paths; |
||
249 | |||
250 | } |
||
251 | |||
252 | /** |
||
253 | * Returns the first existing path if there is one |
||
254 | * |
||
255 | * @return string Backup path if found empty string if not |
||
256 | */ |
||
257 | public function get_existing_path() { |
||
258 | |||
259 | $paths = $this->get_existing_paths(); |
||
260 | |||
261 | if ( ! empty( $paths[0] ) ) { |
||
262 | return $paths[0]; |
||
263 | } |
||
264 | |||
265 | return ''; |
||
266 | |||
267 | } |
||
268 | |||
269 | /** |
||
270 | * Calculate the backup path and create the directory if it doesn't exist. |
||
271 | * |
||
272 | * Tries all possible locations and uses the first one possible |
||
273 | * |
||
274 | * @return |
||
275 | */ |
||
276 | public function calculate_path() { |
||
277 | |||
278 | $paths = array(); |
||
279 | |||
280 | // If we have a custom path then try to use it |
||
281 | if ( $this->get_custom_path() ) { |
||
282 | $paths[] = $this->get_custom_path(); |
||
283 | } |
||
284 | |||
285 | // If there is already a backups directory then try to use that |
||
286 | if ( $this->get_existing_path() ) { |
||
287 | $paths[] = $this->get_existing_path(); |
||
288 | } |
||
289 | |||
290 | // If not then default to a new directory in wp-content |
||
291 | $paths[] = $this->get_default_path(); |
||
292 | |||
293 | // If that didn't work then fallback to a new directory in uploads |
||
294 | $paths[] = $this->get_fallback_path(); |
||
295 | |||
296 | // Loop through possible paths, use the first one that exists/can be created and is writable |
||
297 | foreach ( $paths as $path ) { |
||
298 | if ( wp_mkdir_p( $path ) && wp_is_writable( $path ) ) { // Also handles fixing perms / directory already exists |
||
299 | break; |
||
300 | } |
||
301 | } |
||
302 | |||
303 | if ( file_exists( $path ) && wp_is_writable( $path ) ) { |
||
304 | $this->path = $path; |
||
0 ignored issues
–
show
The variable
$path seems to be defined by a foreach iteration on line 297 . Are you sure the iterator is never empty, otherwise this variable is not defined?
It seems like you are relying on a variable being defined by an iteration: foreach ($a as $b) {
}
// $b is defined here only if $a has elements, for example if $a is array()
// then $b would not be defined here. To avoid that, we recommend to set a
// default value for $b.
// Better
$b = 0; // or whatever default makes sense in your context
foreach ($a as $b) {
}
// $b is now guaranteed to be defined here.
![]() |
|||
305 | } |
||
306 | |||
307 | } |
||
308 | |||
309 | /** |
||
310 | * Protect the directory that backups are stored in |
||
311 | * |
||
312 | * - Adds an index.html file in an attempt to disable directory browsing |
||
313 | * - Adds a .httaccess file to deny direct access if on Apache |
||
314 | * |
||
315 | * @param string $reset |
||
316 | */ |
||
317 | public function protect_path( $reset = 'no' ) { |
||
318 | |||
319 | global $is_apache; |
||
320 | |||
321 | // Protect against directory browsing by including an index.html file |
||
322 | $index = $this->path . '/index.html'; |
||
323 | |||
324 | if ( 'reset' === $reset && file_exists( $index ) ) { |
||
325 | @unlink( $index ); |
||
0 ignored issues
–
show
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.
If you suppress an error, we recommend checking for the error condition explicitly: // For example instead of
@mkdir($dir);
// Better use
if (@mkdir($dir) === false) {
throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
![]() |
|||
326 | } |
||
327 | |||
328 | if ( ! file_exists( $index ) && wp_is_writable( $this->path ) ) { |
||
329 | file_put_contents( $index, '' ); |
||
330 | } |
||
331 | |||
332 | $htaccess = $this->path . '/.htaccess'; |
||
333 | |||
334 | if ( ( 'reset' === $reset ) && file_exists( $htaccess ) ) { |
||
335 | @unlink( $htaccess ); |
||
0 ignored issues
–
show
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.
If you suppress an error, we recommend checking for the error condition explicitly: // For example instead of
@mkdir($dir);
// Better use
if (@mkdir($dir) === false) {
throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
![]() |
|||
336 | } |
||
337 | |||
338 | // Protect the directory with a .htaccess file on Apache servers |
||
339 | if ( $is_apache && function_exists( 'insert_with_markers' ) && ! file_exists( $htaccess ) && wp_is_writable( $this->path ) ) { |
||
340 | |||
341 | $contents[] = '# ' . sprintf( __( 'This %s file ensures that other people cannot download your backup files.', 'backupwordpress' ), '.htaccess' ); |
||
0 ignored issues
–
show
Coding Style
Comprehensibility
introduced
by
$contents was never initialized. Although not strictly required by PHP, it is generally a good practice to add $contents = array(); before regardless.
Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code. Let’s take a look at an example: foreach ($collection as $item) {
$myArray['foo'] = $item->getFoo();
if ($item->hasBar()) {
$myArray['bar'] = $item->getBar();
}
// do something with $myArray
}
As you can see in this example, the array This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop. ![]() |
|||
342 | $contents[] = ''; |
||
343 | $contents[] = '<IfModule mod_rewrite.c>'; |
||
344 | $contents[] = 'RewriteEngine On'; |
||
345 | $contents[] = 'RewriteCond %{QUERY_STRING} !key=' . HMBKP_SECURE_KEY; |
||
346 | $contents[] = 'RewriteRule (.*) - [F]'; |
||
347 | $contents[] = '</IfModule>'; |
||
348 | $contents[] = ''; |
||
349 | |||
350 | file_put_contents( $htaccess, '' ); |
||
351 | |||
352 | insert_with_markers( $htaccess, 'BackUpWordPress', $contents ); |
||
353 | |||
354 | } |
||
355 | |||
356 | } |
||
357 | |||
358 | /** |
||
359 | * If we have more than one path then move any existing backups to the current path and remove them |
||
360 | */ |
||
361 | public function merge_existing_paths() { |
||
362 | |||
363 | $paths = $this->get_existing_paths(); |
||
364 | |||
365 | if ( ( $paths && $this->get_custom_path() ) || count( $paths ) > 1 ) { |
||
0 ignored issues
–
show
The expression
$paths of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent. Consider making the comparison explicit by using ![]() |
|||
366 | foreach ( $paths as $old_path ) { |
||
367 | $this->move_old_backups( $old_path ); |
||
368 | } |
||
369 | } |
||
370 | |||
371 | } |
||
372 | |||
373 | /** |
||
374 | * Move backup files from an existing directory and the new |
||
375 | * location |
||
376 | * |
||
377 | * @param string $path The path to move the backups from |
||
0 ignored issues
–
show
There is no parameter named
$path . Was it maybe removed?
This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. Consider the following example. The parameter /**
* @param array $germany
* @param array $island
* @param array $italy
*/
function finale($germany, $island) {
return "2:1";
}
The most likely cause is that the parameter was removed, but the annotation was not. ![]() |
|||
378 | */ |
||
379 | public function move_old_backups( $from ) { |
||
380 | |||
381 | if ( ! is_readable( $from ) ) { |
||
382 | return; |
||
383 | } |
||
384 | |||
385 | if ( ! wp_is_writable( Path::get_path() ) ) { |
||
386 | return; |
||
387 | } |
||
388 | |||
389 | // Move any existing backups |
||
390 | if ( $handle = opendir( $from ) ) { |
||
391 | |||
392 | // Loop through the backup directory |
||
393 | while ( false !== ( $file = readdir( $handle ) ) ) { |
||
394 | |||
395 | // Find all zips |
||
396 | if ( 'zip' === pathinfo( $file, PATHINFO_EXTENSION ) ) { |
||
397 | |||
398 | // Try to move them |
||
399 | if ( ! @rename( trailingslashit( $from ) . $file, trailingslashit( Path::get_path() ) . $file ) ) { |
||
400 | |||
401 | // If we can't move them then try to copy them |
||
402 | copy( trailingslashit( $from ) . $file, trailingslashit( Path::get_path() ) . $file ); |
||
403 | |||
404 | } |
||
405 | } |
||
406 | } |
||
407 | |||
408 | closedir( $handle ); |
||
409 | |||
410 | } |
||
411 | |||
412 | // Delete the old directory if it's inside WP_CONTENT_DIR |
||
413 | if ( false !== strpos( $from, WP_CONTENT_DIR ) && Path::get_path() !== $from ) { |
||
414 | rmdirtree( $from ); |
||
415 | } |
||
416 | |||
417 | } |
||
418 | |||
419 | /** |
||
420 | * Clean any temporary / incomplete backups from the backups directory |
||
421 | */ |
||
422 | public function cleanup() { |
||
423 | |||
424 | // Don't cleanup a custom path, who knows what other stuff is there |
||
425 | if ( Path::get_path() === $this->get_custom_path() ) { |
||
426 | return; |
||
427 | } |
||
428 | |||
429 | foreach ( new CleanUpIterator( new \DirectoryIterator( Path::get_path() ) ) as $file ) { |
||
430 | |||
431 | if ( $file->isDot() || ! $file->isReadable() || ! $file->isFile() ) { |
||
432 | continue; |
||
433 | } |
||
434 | |||
435 | @unlink( $file->getPathname() ); |
||
0 ignored issues
–
show
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.
If you suppress an error, we recommend checking for the error condition explicitly: // For example instead of
@mkdir($dir);
// Better use
if (@mkdir($dir) === false) {
throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
![]() |
|||
436 | |||
437 | } |
||
438 | } |
||
439 | } |
||
440 | |||
441 | class CleanUpIterator extends \FilterIterator { |
||
0 ignored issues
–
show
|
|||
442 | |||
443 | // Don't match index.html, files with zip extension or status logfiles. |
||
444 | public function accept() { |
||
445 | |||
446 | // Don't remove existing backups |
||
447 | if ( 'zip' === pathinfo( $this->current()->getFilename(), PATHINFO_EXTENSION ) ) { |
||
448 | return false; |
||
449 | } |
||
450 | |||
451 | // Don't remove the index.html file |
||
452 | if ( 'index.html' === $this->current()->getBasename() ) { |
||
453 | return false; |
||
454 | } |
||
455 | |||
456 | // Don't remove the file manifest |
||
457 | if ( '.files' === $this->current()->getBasename() ) { |
||
458 | return false; |
||
459 | } |
||
460 | |||
461 | // Don't cleanup the backup running file |
||
462 | return ! preg_match( '/(.*-running)/', $this->current() ); |
||
463 | |||
464 | } |
||
465 | } |
||
466 |
Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a
@return
annotation as described here.