Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 22 additions & 4 deletions inc/compat/class-general-compat.php
Original file line number Diff line number Diff line change
Expand Up @@ -465,17 +465,35 @@ private function delete_divi_static_css_cache_directory(string $cache_dir): void
);

foreach ($iterator as $file) {
$path = $file->getRealPath();
$path = $file->getPathname();
$normalized_path = untrailingslashit(wp_normalize_path($path));

if (false === $path) {
if ($real_cache_dir !== $normalized_path && 0 !== strpos(trailingslashit($normalized_path), trailingslashit($real_cache_dir))) {
continue;
}

if ($file->isLink()) {
wp_delete_file($path);
continue;
}

$real_path = $file->getRealPath();

if (false === $real_path) {
continue;
}

$real_path = untrailingslashit(wp_normalize_path($real_path));

if ($real_cache_dir !== $real_path && 0 !== strpos(trailingslashit($real_path), trailingslashit($real_cache_dir))) {
continue;
}

if ($file->isDir()) {
// phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_rmdir, WordPress.PHP.NoSilencedErrors.Discouraged -- Removes an empty generated cache directory after deleting its contents.
@rmdir($path);
@rmdir($real_path);
} else {
wp_delete_file($path);
wp_delete_file($real_path);
}
}

Expand Down
48 changes: 45 additions & 3 deletions tests/WP_Ultimo/General_Compat_Test.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@
* @subpackage Tests
*/

namespace WP_Ultimo\Tests;
namespace WP_Ultimo\Compat;

defined('ABSPATH') || exit;

use WP_Ultimo\Compat\General_Compat;
use WP_UnitTestCase;

/**
Expand Down Expand Up @@ -78,6 +79,47 @@ public function test_clear_divi_static_css_cache_deletes_cloned_site_cache_only(
$this->assertDirectoryExists($other_dir);
}

/**
* Test Divi et-cache symlinks do not delete files outside the cache tree.
*/
public function test_clear_divi_static_css_cache_does_not_follow_symlinks(): void {

if ( ! is_multisite()) {
$this->markTestSkipped('Divi cache purge tests require multisite');
}

if ( ! function_exists('symlink')) {
$this->markTestSkipped('Symlink support is not available');
}

$blog_id = self::factory()->blog->create();
$other_blog_id = self::factory()->blog->create();
$network_id = (int) get_current_network_id();
$cache_root = trailingslashit(WP_CONTENT_DIR) . 'et-cache';
$cache_dir = trailingslashit($cache_root) . $network_id . '/' . $blog_id;
$other_dir = trailingslashit($cache_root) . $network_id . '/' . $other_blog_id;
$outside_file = $other_dir . '/external-target.css';
$symlink = $cache_dir . '/external-target.css';

$this->cache_dirs = [$cache_dir, $other_dir];

wp_mkdir_p($cache_dir);
wp_mkdir_p($other_dir);

// phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_file_put_contents -- Test fixture setup.
file_put_contents($outside_file, 'external divi css');

// phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged, WordPress.WP.AlternativeFunctions.symlink_symlink -- Test fixture setup requires a symlink.
if ( ! @symlink($outside_file, $symlink)) {
$this->markTestSkipped('Symlink fixture could not be created');
}

General_Compat::get_instance()->clear_divi_static_css_cache(['site_id' => $blog_id]);

$this->assertFileExists($outside_file);
$this->assertDirectoryDoesNotExist($cache_dir);
}

/**
* Recursively remove a test directory.
*
Expand All @@ -96,7 +138,7 @@ private function remove_directory(string $dir): void {
);

foreach ($iterator as $file) {
$path = $file->getRealPath();
$path = $file->isLink() ? $file->getPathname() : $file->getRealPath();

if (false === $path) {
continue;
Expand Down
Loading