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
@@ -45,7 +45,7 @@ class GlobalColorsAdapter implements DiviAdapter
new Call(GlobalData::class . '::sanitize_global_colors_data', 1),
new Call(GlobalData::class . '::set_global_colors', 2),
],
keys: [self::OPTIONS],
keys: [self::OPTIONS, 'et_theme_options'],
features: [DiviFeature::ColorWrite]
);
}
@@ -83,6 +83,8 @@ class GlobalColorsAdapter implements DiviAdapter
throw new RuntimeException('global colors refused: Divi dropped ' . implode(', ', $dropped));
}
// Audit L5: set_global_colors() ends in et_update_option(), which writes Divi's request global back whole.
$GLOBALS['et_theme_options'] = get_option('et_divi');
GlobalData::set_global_colors($clean, ! $replace);
}
}
@@ -21,9 +21,9 @@ defined('ABSPATH') || exit;
* them raw from get_option('et_divi'), sends a value through the customizer's own sanitizer of its setting, and
* writes the way a customizer save ends – et_update_option()/et_delete_option() per key, then the static CSS and the
* builder caches (research A.2/A.3). Limits (W13): et_update_option() writes the whole et_divi array from Divi's
* request global, so a change another process made after this request loaded it is lost; design_rev and the lock of
* the pipeline narrow that window. remove_static_resources() does nothing without a logged-in user. Not final so the
* theme option area can be tested with a mock.
* request global; write() reloads it from et_divi first, under the lock colors and theme options share (audit L5), so
* only Divi's own saves can still race it. remove_static_resources() does nothing without a logged-in user. Not
* final so the theme option area can be tested with a mock.
*/
class ThemeOptionsAdapter implements DiviAdapter
{
@@ -74,7 +74,7 @@ class ThemeOptionsAdapter implements DiviAdapter
new Call('et_pb_force_regenerate_templates', 0),
new Call('et_pb_is_allowed', 1),
],
keys: [self::OPTIONS],
keys: [self::OPTIONS, 'et_theme_options'],
mirrors: [
// shortcut: the fingerprint covers the whole customizer function (about 4,000 lines) and changes with
// every customizer edit; switch to a narrower excerpt when it fires too often.
@@ -150,6 +150,9 @@ class ThemeOptionsAdapter implements DiviAdapter
throw new InvalidArgumentException('not a theme option of the whitelist: ' . implode(', ', $unknown));
}
// Audit L5: et_update_option() writes Divi's request global back whole; under the shared et_divi lock reload
// it so another writer's change (colors) made since this request began is not reverted.
$GLOBALS['et_theme_options'] = get_option('et_divi');
foreach ($set as $key => $value) {
et_update_option($key, $value);
}