Skip to content

Commit e5af20d

Browse files
authored
Update
1 parent 33e1b77 commit e5af20d

1 file changed

Lines changed: 94 additions & 69 deletions

File tree

simple-wp-site-exporter.php

Lines changed: 94 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -770,7 +770,60 @@ function sse_validate_file_extension($filePath) {
770770
* @return string|false Resolved file path or false on failure.
771771
*/
772772
function sse_resolve_nonexistent_file_path($normalizedFilePath) {
773-
// Security: Get WordPress upload directory as our base validation point
773+
$uploadInfo = sse_get_upload_directory_info();
774+
if ($uploadInfo === false) {
775+
return false;
776+
}
777+
778+
return sse_build_validated_file_path($normalizedFilePath, $uploadInfo);
779+
}
780+
781+
/**
782+
* Builds validated file path from components.
783+
*
784+
* @param string $normalizedFilePath The normalized file path.
785+
* @param array $uploadInfo Upload directory information.
786+
* @return string|false Real file path on success, false on failure.
787+
*/
788+
function sse_build_validated_file_path($normalizedFilePath, $uploadInfo) {
789+
$parentDir = dirname($normalizedFilePath);
790+
$filename = basename($normalizedFilePath);
791+
792+
if (!sse_validate_parent_directory_safety($parentDir, $uploadInfo['basedir'])) {
793+
return false;
794+
}
795+
796+
return sse_construct_final_file_path($parentDir, $filename, $uploadInfo['realpath']);
797+
}
798+
799+
/**
800+
* Constructs final file path after validation.
801+
*
802+
* @param string $parentDir Parent directory path.
803+
* @param string $filename File name.
804+
* @param string $uploadRealPath Upload directory real path.
805+
* @return string|false Final file path on success, false on failure.
806+
*/
807+
function sse_construct_final_file_path($parentDir, $filename, $uploadRealPath) {
808+
$realParentDir = sse_resolve_parent_directory($parentDir, $uploadRealPath);
809+
if ($realParentDir === false) {
810+
return false;
811+
}
812+
813+
$sanitizedFilename = sse_sanitize_filename($filename);
814+
if ($sanitizedFilename === false) {
815+
return false;
816+
}
817+
818+
return trailingslashit($realParentDir) . $sanitizedFilename;
819+
}
820+
821+
/**
822+
* Gets WordPress upload directory information with validation.
823+
*
824+
* @return array|false Upload directory info array or false on failure.
825+
*/
826+
function sse_get_upload_directory_info() {
774827
$uploadDir = wp_upload_dir();
775828
if (!isset($uploadDir['basedir']) || empty($uploadDir['basedir'])) {
776829
sse_log('Could not determine WordPress upload directory for validation', 'error');
@@ -783,102 +836,74 @@ function sse_resolve_nonexistent_file_path($normalizedFilePath) {
783836
return false;
784837
}
785838

786-
// Security: Only allow paths within WordPress upload directory (strict allowlist)
787-
$parentDir = dirname($normalizedFilePath);
788-
$filename = basename($normalizedFilePath);
789-
790-
// Pre-validate that parent directory path looks safe before any filesystem operations
839+
return array(
840+
'basedir' => $uploadDir['basedir'],
841+
'realpath' => $wpUploadDir
842+
);
843+
}
844+
845+
/**
846+
* Validates parent directory path safety.
847+
*
848+
* @param string $parentDir The parent directory path.
849+
* @param string $uploadDir The upload directory path.
850+
* @return bool True if safe, false otherwise.
851+
*/
852+
function sse_validate_parent_directory_safety($parentDir, $uploadDir) {
853+
// Pre-validate that parent directory path looks safe
791854
if (strpos($parentDir, '..') !== false || strpos($parentDir, 'wp-config') !== false) {
792855
sse_log('Rejected unsafe parent directory path: ' . $parentDir, 'security');
793856
return false;
794857
}
795858

796-
// Security: Ensure parent directory is within WordPress upload directory
859+
// Ensure parent directory is within WordPress upload directory
797860
$normalizedParentDir = wp_normalize_path($parentDir);
798-
$normalizedUploadDir = wp_normalize_path($uploadDir['basedir']);
861+
$normalizedUploadDir = wp_normalize_path($uploadDir);
799862

800863
if (strpos($normalizedParentDir, $normalizedUploadDir) !== 0) {
801864
sse_log('Parent directory not within WordPress upload directory: ' . $parentDir, 'security');
802865
return false;
803866
}
804867

805-
// Now it's safe to check if directory exists (after validation)
806-
if ( !is_dir( $parentDir ) || !is_readable( $parentDir ) ) {
868+
return true;
869+
}
870+
871+
/**
872+
* Resolves and validates parent directory.
873+
*
874+
* @param string $parentDir The parent directory path.
875+
* @param string $uploadDir The upload directory path.
876+
* @return string|false Real parent directory path or false on failure.
877+
*/
878+
function sse_resolve_parent_directory($parentDir, $uploadDir) {
879+
if (!is_dir($parentDir) || !is_readable($parentDir)) {
807880
sse_log('Parent directory validation failed: ' . $parentDir, 'security');
808881
return false;
809882
}
810883

811-
$realParentDir = realpath( $parentDir );
812-
if ( $realParentDir === false || strpos($realParentDir, $wpUploadDir) !== 0 ) {
884+
$realParentDir = realpath($parentDir);
885+
if ($realParentDir === false || strpos($realParentDir, $uploadDir) !== 0) {
813886
sse_log('Parent directory real path validation failed', 'security');
814887
return false;
815888
}
816889

817-
// Sanitize filename to prevent directory traversal
818-
$filename = sanitize_file_name( $filename );
819-
if (strpos($filename, '..') !== false || strpos($filename, '/') !== false || strpos($filename, '\\') !== false) {
820-
sse_log('Filename contains invalid characters: ' . $filename, 'security');
821-
return false;
822-
}
823-
824-
return trailingslashit( $realParentDir ) . $filename;
890+
return $realParentDir;
825891
}
826892

827893
/**
828-
* Resolves real file path, handling non-existent files securely.
894+
* Sanitizes filename to prevent directory traversal.
829895
*
830-
* @param string $normalizedFilePath The normalized file path.
831-
* @return string|false Real file path on success, false on failure.
896+
* @param string $filename The filename to sanitize.
897+
* @return string|false Sanitized filename or false on failure.
832898
*/
833-
function sse_resolve_file_path($normalizedFilePath) {
834-
// Security: Only allow files with safe extensions and within WordPress directory
835-
$allowedExtensions = array('zip', 'sql');
836-
$fileExtension = strtolower(pathinfo($normalizedFilePath, PATHINFO_EXTENSION));
837-
838-
if (!in_array($fileExtension, $allowedExtensions, true)) {
839-
sse_log('Rejected file access - invalid extension: ' . $fileExtension, 'security');
899+
function sse_sanitize_filename($filename) {
900+
$filename = sanitize_file_name($filename);
901+
if (strpos($filename, '..') !== false || strpos($filename, '/') !== false || strpos($filename, '\\') !== false) {
902+
sse_log('Filename contains invalid characters: ' . $filename, 'security');
840903
return false;
841904
}
842905

843-
$realFilePath = realpath($normalizedFilePath);
844-
845-
// If realpath fails for the file (doesn't exist), validate parent directory more securely
846-
if ($realFilePath === false) {
847-
$parentDir = dirname($normalizedFilePath);
848-
$filename = basename($normalizedFilePath);
849-
850-
// Security: Additional validation to prevent SSRF - ensure parent is within WordPress
851-
$wpContentDir = realpath(WP_CONTENT_DIR);
852-
if ($wpContentDir === false) {
853-
sse_log('Could not resolve WP_CONTENT_DIR for security validation', 'error');
854-
return false;
855-
}
856-
857-
// Validate parent directory exists, is readable, and is within WP_CONTENT_DIR
858-
if ( !is_dir( $parentDir ) || !is_readable( $parentDir ) ) {
859-
sse_log('Parent directory validation failed: ' . $parentDir, 'security');
860-
return false;
861-
}
862-
863-
$realParentDir = realpath( $parentDir );
864-
if ( $realParentDir === false || strpos($realParentDir, $wpContentDir) !== 0 ) {
865-
sse_log('Parent directory not within WordPress content directory', 'security');
866-
return false;
867-
}
868-
869-
$realFileDir = $realParentDir;
870-
871-
// Sanitize filename to prevent directory traversal
872-
$filename = sanitize_file_name( $filename );
873-
if (strpos($filename, '..') !== false || strpos($filename, '/') !== false || strpos($filename, '\\') !== false) {
874-
sse_log('Filename contains invalid characters: ' . $filename, 'security');
875-
return false;
876-
}
877-
878-
$realFilePath = trailingslashit( $realFileDir ) . $filename;
879-
}
880-
881-
return $realFilePath;
906+
return $filename;
882907
}
883908

884909
/**

0 commit comments

Comments
 (0)