fix(audit L5): et_theme_options unter der gemeinsamen Sperre aus et_divi neu laden, bevor Farben oder Theme-Optionen schreiben

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
Jan-Niclas Loosen
2026-10-19 23:52:38 +02:00
co-authored by Claude Sonnet 5.5
parent b59a0c045f
commit 1b3ef552e7
4 changed files with 41 additions and 8 deletions
@@ -39,7 +39,7 @@ final class GlobalColorsAdapterTest extends TestCase
array_map(static fn (Call $call): string => $call->label(), $manifest->calls)
);
self::assertSame([0, 1, 2], array_map(static fn (Call $call): ?int => $call->args, $manifest->calls));
self::assertSame(['et_divi'], $manifest->keys);
self::assertSame(['et_divi', 'et_theme_options'], $manifest->keys);
self::assertSame([DiviFeature::ColorWrite], $manifest->features);
}
@@ -94,11 +94,21 @@ final class GlobalColorsAdapterTest extends TestCase
$divi = Mockery::mock('alias:' . self::GLOBAL_DATA);
$divi->expects('sanitize_global_colors_data')->twice()->with($entries)->andReturn($clean);
// Normal write: merge ("already sanitized" = true); restore: replace all (false).
$divi->expects('set_global_colors')->once()->with($clean, true)->ordered();
// Audit L5: Divi's request global is reloaded from et_divi before the write.
$GLOBALS['et_theme_options'] = ['stale' => true];
Functions\when('get_option')->alias(static fn (string $name): mixed => $name === 'et_divi' ? ['fresh' => 1] : false);
$seen = [];
$divi->expects('set_global_colors')->once()->with($clean, true)->ordered()
->andReturnUsing(static function () use (&$seen): void {
$seen[] = $GLOBALS['et_theme_options'];
});
$divi->expects('set_global_colors')->once()->with($clean, false)->ordered();
(new GlobalColorsAdapter())->save($entries, false);
(new GlobalColorsAdapter())->save($entries, true);
self::assertSame([['fresh' => 1]], $seen);
unset($GLOBALS['et_theme_options']);
}
/** @runInSeparateProcess */
@@ -54,7 +54,7 @@ final class ThemeOptionsAdapterTest extends TestCase
[1, 1, 1, 1, 1, 2, 1, 2, 0, 1],
array_map(static fn (Call $call): ?int => $call->args, $manifest->calls)
);
self::assertSame(['et_divi'], $manifest->keys);
self::assertSame(['et_divi', 'et_theme_options'], $manifest->keys);
self::assertSame([DiviFeature::ThemeOptionWrite], $manifest->features);
self::assertCount(1, $manifest->mirrors);
@@ -196,6 +196,18 @@ final class ThemeOptionsAdapterTest extends TestCase
$log[] = 'regenerate templates after ' . count(\ET_Core_PageResource::$removed) . ' static CSS clear';
});
\ET_Core_PageResource::$removed = [];
// Audit L5: Divi's request global may be stale; et_update_option() writes it back whole.
$GLOBALS['et_theme_options'] = ['stale' => true];
$seen = [];
Functions\when('et_update_option')->alias(static function (string $key, mixed $value) use (
&$stored,
&$log,
&$seen
): void {
$seen[] = $GLOBALS['et_theme_options'];
$stored[$key] = $value;
$log[] = 'update ' . $key;
});
$result = (new ThemeOptionsAdapter())->write(
['body_font_size' => 16, 'body_font_height' => 1.8],
@@ -211,6 +223,12 @@ final class ThemeOptionsAdapterTest extends TestCase
$log
);
self::assertSame([['all', 'all', false]], \ET_Core_PageResource::$removed, 'like a customizer save');
self::assertSame(
['body_font_size' => 14, 'heading_font' => 'Roboto', 'accent_color' => '#ff0000'],
$seen[0],
'et_theme_options is reloaded from et_divi before the first write'
);
unset($GLOBALS['et_theme_options']);
self::assertSame('#ff0000', $stored['accent_color'], 'other theme options stay');
}