ABSPATH, '@content' => WP_CONTENT_DIR, '@plugins' => WP_PLUGIN_DIR, '@themes' => get_theme_root() ); if ( defined( 'WPMU_PLUGIN_DIR' ) ) { $roots['@mu-plugins'] = WPMU_PLUGIN_DIR; } if ( empty( $up['error'] ) && ! empty( $up['basedir'] ) ) { $roots['@uploads'] = $up['basedir']; } foreach ( $roots as $key => $path ) { $roots[ $key ] = vsai_path_normalize( realpath( $path ) ?: $path ); } return $roots; } function vsai_path_within( $path, $root ) { return $path === $root || 0 === strpos( $path, $root . '/' ); } function vsai_path_valid_relative( $rel ) { return is_string( $rel ) && '' !== $rel && ! preg_match( '~[\\\\:\x00-\x1f\x7f]|^/|(?:^|/)\.{1,2}(?:/|$)|//|%(?:2e|2f|5c)~i', $rel ); } /** Return an empty string for traversal, unknown roots or escaping symlinks. */ function vsai_path_resolve( $rel ) { if ( ! vsai_path_valid_relative( $rel ) ) { return ''; } $roots = vsai_path_roots(); $key = ''; if ( '@' === $rel[0] ) { $parts = explode( '/', $rel, 2 ); $key = $parts[0]; $rel = $parts[1] ?? ''; if ( ! isset( $roots[ $key ] ) || ! vsai_path_valid_relative( $rel ) ) { return ''; } } $root = $roots[ $key ]; $path = $root . '/' . $rel; // Check existing parents as well, so deleted files cannot traverse a symlink. $probe = $path; while ( ! file_exists( $probe ) && ! is_link( $probe ) && $probe !== $root ) { $probe = dirname( $probe ); } $real = realpath( $probe ); if ( false === $real || vsai_path_normalize( $real ) !== vsai_path_normalize( $probe ) ) { return ''; } return vsai_path_within( vsai_path_normalize( $real ), $root ) ? $path : ''; } /** Preserve normal-installation keys; external roots receive unique namespaces. */ function vsai_path_relative( $abs ) { $abs = vsai_path_normalize( $abs ); foreach ( vsai_path_roots() as $key => $root ) { if ( $abs !== $root && vsai_path_within( $abs, $root ) ) { $rel = ( '' === $key ? '' : $key . '/' ) . substr( $abs, strlen( $root ) + 1 ); return vsai_path_resolve( $rel ) ? $rel : ''; } } return ''; }