Skip to content

Commit 7aedce5

Browse files
committed
WP API Fixes
1 parent b638e43 commit 7aedce5

13 files changed

Lines changed: 442 additions & 99 deletions

CHANGELOG.md

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,17 @@
66

77
- **Multisite Export Authorization**: Full-site export access now uses a shared permission helper. On multisite, exporter page access, export creation, secure download, and manual delete require a super admin or `manage_network_options`; single-site installs continue to require `manage_options`.
88
- **Per-Export Private Storage**: Each export is now staged in a random private child directory under the configured private temp export base. The exporter rejects symlinked or unsafe pre-existing directories and enforces `0700` directory permissions.
9-
- **Private File Modes**: Sensitive export artifacts now use a private umask during export and explicit `0600` chmod verification after database dumps, compressed database payloads, file archives, manifests, final ZIPs, and protection files are created.
9+
- **Private File Modes**: Sensitive export artifacts now use a private umask during export and WordPress Filesystem-backed `0600` chmod verification after database dumps, compressed database payloads, file archives, manifests, final ZIPs, and protection files are created.
1010
- **WP-CLI Trust Boundary**: WP-CLI discovery now prefers trusted system paths (`/usr/local/bin/wp`, `/usr/bin/wp`). Alternate executables must be explicitly configured with `SSE_WP_CLI_PATH` or the `sse_wp_cli_path` filter and must pass ownership and writable-mode checks.
1111
- **Export Action Binding**: Generated download and delete actions include the private export directory identifier in their request data and nonce action so requests are tied to the generated export location.
1212

1313
### Architecture
1414

15-
- **Private Export Cleanup**: Bulk cleanup now scans generated private export directories, scheduled/manual deletion can clean up empty private export directories, and failed exports remove their private staging directory.
16-
- **Private Storage Helpers**: Added shared helpers for multisite-aware capability checks, private export directory naming, generated directory validation, and private filesystem mode enforcement.
15+
- **Private Export Cleanup**: Bulk cleanup now scans generated private export directories with the WordPress Filesystem API, scheduled/manual deletion can clean up empty private export directories, and failed exports remove their private staging directory through the WordPress Filesystem API for VIP compatibility.
16+
- **WordPress API Coverage**: Replaced direct file metadata checks, generated artifact verification, export cleanup directory scans, and filename basename extraction with WordPress Filesystem API methods and native WordPress helpers where available.
17+
- **Private Storage Helpers**: Added shared helpers for multisite-aware capability checks, private export directory naming, generated directory validation, generated file verification, and private filesystem mode enforcement.
18+
- **VIPCS Tooling**: Added VIP Coding Standards as a Composer-managed dev dependency so the project PHPCS script can run the VIP ruleset reproducibly.
19+
- **Semver Test Prep**: Added a Composer-managed semantic versioning library for future version-related tests.
1720

1821
### Documentation
1922

composer.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,9 @@
2626
"php-stubs/wordpress-stubs": "^6.9",
2727
"szepeviktor/phpstan-wordpress": "^2.0",
2828
"phpstan/phpstan": "^2.1",
29-
"phpcompatibility/phpcompatibility-wp": "^2.1"
29+
"phpcompatibility/phpcompatibility-wp": "^2.1",
30+
"automattic/vipwpcs": "^3.0.1",
31+
"z4kn4fein/php-semver": "^3.0"
3032
},
3133
"scripts": {
3234
"phpcs": "phpcs --standard=phpcs.xml",

composer.lock

Lines changed: 165 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

enginescript-site-exporter.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,6 @@
7878
* @see PharData - PHP PharData class
7979
* @see RecursiveIteratorIterator - PHP SPL iterator
8080
* @see RecursiveDirectoryIterator - PHP SPL directory iterator
81-
* @see DirectoryIterator - PHP SPL directory iterator for cleanup
8281
* @see SplFileInfo - PHP SPL file information class
8382
* @see RuntimeException - PHP runtime exception class
8483
* @see Exception - PHP base exception class

includes/admin-page.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ function sse_render_exporter_notices(): void {
123123
* @return void
124124
*/
125125
function sse_render_export_success_notice( array $zip_result ): void {
126-
$export_dir_name = basename( dirname( $zip_result['filepath'] ) );
126+
$export_dir_name = wp_basename( dirname( $zip_result['filepath'] ) );
127127
$has_private_dir_param = sse_is_export_private_directory_name( $export_dir_name );
128128
$download_args = [
129129
'action' => 'sse_secure_download',

includes/archive.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,7 @@ function sse_create_compressed_database_file( string $source_path, string $targe
236236
fclose( $source_handle ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose -- Closing local file handle opened above.
237237
gzclose( $target_handle );
238238

239-
if ( ! file_exists( $target_path ) || filesize( $target_path ) <= 0 ) { // phpcs:ignore WordPressVIPMinimum.Functions.RestrictedFunctions.file_exists_file_exists, WordPress.WP.AlternativeFunctions.file_system_operations_filesize -- Verifying generated local gzip payload.
239+
if ( ! sse_filesystem_file_has_content( $target_path ) ) {
240240
sse_cleanup_files( [ $target_path ] );
241241
return new WP_Error( 'db_compress_verify_failed', __( 'Compressed database file was not created successfully.', 'enginescript-site-exporter' ) );
242242
}
@@ -297,7 +297,7 @@ function sse_create_wordpress_files_archive( string $files_archive_path, string
297297
);
298298
}
299299

300-
if ( ! file_exists( $files_archive_path ) || filesize( $files_archive_path ) <= 0 ) { // phpcs:ignore WordPressVIPMinimum.Functions.RestrictedFunctions.file_exists_file_exists, WordPress.WP.AlternativeFunctions.file_system_operations_filesize -- Verifying generated local tar.gz payload.
300+
if ( ! sse_filesystem_file_has_content( $files_archive_path ) ) {
301301
return new WP_Error( 'files_archive_verify_failed', __( 'WordPress files archive was not created successfully.', 'enginescript-site-exporter' ) );
302302
}
303303

@@ -364,7 +364,7 @@ function sse_create_combined_engine_script_zip( array $bundle_paths ) {
364364
sprintf(
365365
/* translators: %s: filename */
366366
__( 'Could not create ZIP file at %s', 'enginescript-site-exporter' ),
367-
basename( $bundle_paths['combined_zip_path'] ) // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.system_calls_basename -- Safe usage: path is constructed from controlled export directory and sanitized filename.
367+
wp_basename( $bundle_paths['combined_zip_path'] )
368368
)
369369
);
370370
}
@@ -394,7 +394,7 @@ function sse_create_combined_engine_script_zip( array $bundle_paths ) {
394394

395395
$zip_close_status = $zip->close();
396396

397-
if ( ! $zip_close_status || ! file_exists( $bundle_paths['combined_zip_path'] ) ) { // phpcs:ignore WordPressVIPMinimum.Functions.RestrictedFunctions.file_exists_file_exists -- Verifying generated export archive.
397+
if ( ! $zip_close_status || ! sse_filesystem_file_has_content( $bundle_paths['combined_zip_path'] ) ) {
398398
sse_cleanup_files( [ $bundle_paths['combined_zip_path'] ] );
399399
return new WP_Error( 'zip_finalize_failed', __( 'Failed to finalize or save the ZIP archive after processing files.', 'enginescript-site-exporter' ) );
400400
}

0 commit comments

Comments
 (0)