Below is a differential between the unpatched vulnerable code and the patched update, for reference.
--- a/kirki/ComponentLibrary/controller/CompLibFormHandler.php
+++ b/kirki/ComponentLibrary/controller/CompLibFormHandler.php
@@ -6,6 +6,7 @@
exit; // Exit if accessed directly.
}
+use KirkiAjaxPage;
use KirkiHelperFunctions;
use WP_REST_Server;
use WP_REST_Controller;
@@ -267,14 +268,16 @@
if ( strlen( $username ) === 0 && isset( $form_data['email'] ) && strlen( $email ) > 0 ) {
$user = get_user_by( 'email', $email );
- if ( $user ) {
- $username = $user->get( 'user_login' );
- } else {
- $response = array(
- 'message' => 'User not found',
- );
- return new WP_REST_Response( $response, 404 );
+
+ if ( ! $user ) {
+ return new WP_REST_Response( array( 'message' => 'User not found' ), 404 );
}
+
+ $username = $user->get( 'user_login' );
+ }
+
+ if ( empty( $username ) ) {
+ return new WP_REST_Response( array( 'message' => 'Invalid request' ), 400 );
}
if ( isset( $username ) && strlen( $username ) > 0 ) {
@@ -287,6 +290,15 @@
return new WP_REST_Response( $response, 404 );
}
+ $user_email = $user->get( 'user_email' );
+ if($email !== $user_email) {
+ $response = array(
+ 'message' => 'Invalid email address',
+ );
+ return new WP_REST_Response( $response, 404 );
+ }
+ $email = $user_email;
+
$key = get_password_reset_key( $user );
if ( is_wp_error( $key ) ) {
$response = array(
@@ -296,7 +308,7 @@
}
// Prepare email content.
- $url = HelperFunctions::get_utility_page_url( 'reset_password' );
+ $url = HelperFunctions::get_utility_page_url( Page::TYPE_FORGOT_PASSWORD );
$username = $user->user_login;
$chip_data = array(
@@ -311,12 +323,12 @@
$email_body = '';
if ( isset( $form_data['emailBody'] ) ) {
- $email_body = json_decode( $form_data['emailBody'], true );
- foreach ( $email_body as $key => $body_data ) {
+ $email_body_array = json_decode( $form_data['emailBody'], true );
+ foreach ( $email_body_array as $key => $body_data ) {
if ( isset( $body_data['type'] ) && isset( $body_data['value'] ) && $body_data['type'] === 'text' ) {
- $email_body = $email_body . $body_data['value'];
+ $email_body .= $body_data['value'];
} elseif ( isset( $body_data['type'] ) && isset( $body_data['value'] ) && $body_data['type'] === 'chip' ) {
- $email_body = $email_body . $chip_data[ $body_data['value'] ];
+ $email_body .= $chip_data[ $body_data['value'] ];
}
}
}
--- a/kirki/includes/API.php
+++ b/kirki/includes/API.php
@@ -30,12 +30,8 @@
* @return void
*/
public function __construct() {
- add_action( 'rest_api_init', array( $this, 'register_api' ) );
-
- if ( isset( $_GET['page-export'], $_GET['file-name'] ) && $_GET['page-export'] === 'true' ) {
- // TODO: need to check nonce
- $this->downloadZIP();
- }
+ add_action( 'rest_api_init', array( $this, 'register_api' ) );
+ add_action( 'init', array( $this, 'download_zip_endpoint' ) );
}
/**
@@ -57,12 +53,28 @@
FrontendApi::register();
}
+ public function download_zip_endpoint() {
+ if (
+ ! isset( $_GET['page-export'], $_GET['file-name'] ) ||
+ 'true' !== $_GET['page-export']
+ ) {
+ return;
+ }
+
+ if ( ! HelperFunctions::has_access( KIRKI_ACCESS_LEVELS['FULL_ACCESS'] ) ) {
+ wp_send_json_error( 'Not authorized', 401 );
+ }
+
+ // TODO: need to check nonce
+ $this->downloadZIP();
+ }
+
private function downloadZIP() {
$upload_dir = wp_upload_dir();
$file_name = HelperFunctions::sanitize_text( $_GET['file-name'] );
$file_name = basename( $file_name );
// Check if the file has a .zip extension
- if ( ! pathinfo( $file_name, PATHINFO_EXTENSION ) === 'zip' ) {
+ if ( pathinfo( $file_name, PATHINFO_EXTENSION ) !== 'zip' ) {
echo 'Invalid file type.';
die();
}
--- a/kirki/includes/API/Frontend/Controllers/FormController.php
+++ b/kirki/includes/API/Frontend/Controllers/FormController.php
@@ -639,6 +639,11 @@
foreach ( $form_data as $name => $value ) {
$type = isset( $form_data_types[ $name ]['type'] ) ? $form_data_types[ $name ]['type'] : 'text';
+ // Handle array values by serializing them
+ if (is_array($value)) {
+ $value = serialize($value);
+ }
+
array_push(
$values,
$form_id,
@@ -646,7 +651,7 @@
$session_id,
$timestamp,
"$name",
- "$value",
+ $value,
"$type"
);
--- a/kirki/includes/Admin/AdminMenu.php
+++ b/kirki/includes/Admin/AdminMenu.php
@@ -110,7 +110,11 @@
* @return void
*/
public function admin_menu() {
- add_menu_page( 'Kirki - Home', 'Kirki', 'edit_posts', 'kirki', array( $this, 'plugin_page' ), 'dashicons-kirki', 25 );
+ /* FREE_START */
+ $menu_title = 'Kirki';
+ /* FREE_END */
+
+ add_menu_page( 'Kirki - Home', $menu_title, 'edit_posts', 'kirki', array( $this, 'plugin_page' ), 'dashicons-kirki', 25 );
foreach ( $this->dashboard_toolbar_submenus as $slug => $submenu ) {
add_submenu_page(
--- a/kirki/includes/Ajax.php
+++ b/kirki/includes/Ajax.php
@@ -309,7 +309,7 @@
//phpcs:ignore WordPress.Security.NonceVerification.Missing,WordPress.Security.NonceVerification.Recommended,WordPress.Security.ValidatedSanitizedInput.MissingUnslash,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
$endpoint = HelperFunctions::sanitize_text( isset( $_GET['endpoint'] ) ? $_GET['endpoint'] : null );
if ( in_array( $endpoint, array( 'collect-collaboration-actions', 'delete-collaboration-connection' ), true ) ) {
- if ( ! $this->user_can_access_collaboration() ) {
+ if ( ! $this->user_can_access_wp_apis() ) {
wp_send_json_error( 'Not authorized' );
}
} else {
@@ -609,11 +609,11 @@
}
/**
- * Check if the current request can access collaboration endpoints.
+ * Check if the current request can access wp endpoints.
*
* @return bool
*/
- private function user_can_access_collaboration() {
+ private function user_can_access_wp_apis() {
return is_user_logged_in() && HelperFunctions::has_access(
array(
KIRKI_ACCESS_LEVELS['FULL_ACCESS'],
@@ -704,7 +704,11 @@
*/
public function kirki_wp_admin_get_apis() {
if ( ! is_admin() ) {
- wp_send_json_error( 'Not authorized' );
+ wp_send_json_error( 'Not authorized', 401 );
+ }
+
+ if ( ! HelperFunctions::has_access( KIRKI_ACCESS_LEVELS['FULL_ACCESS'] ) ) {
+ wp_send_json_error( 'Not authorized', 401 );
}
//phpcs:ignore WordPress.Security.NonceVerification.Missing,WordPress.Security.NonceVerification.Recommended,WordPress.Security.ValidatedSanitizedInput.MissingUnslash,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
--- a/kirki/includes/Ajax/Form.php
+++ b/kirki/includes/Ajax/Form.php
@@ -804,11 +804,17 @@
$data[ $form_data_item['timestamp'] ]['id'] = $form_data_item['timestamp'];
}
+ // Handle array values by unserializing them
+ $input_value = $form_data_item['input_value'];
+ if (is_string($input_value) && @unserialize($input_value) !== false) {
+ $input_value = unserialize($input_value);
+ }
+
if ( isset( $data[ $form_data_item['timestamp'] ] ) ) {
- $data[ $form_data_item['timestamp'] ][ $form_data_item['input_key'] ] = $form_data_item['input_value'];
+ $data[$form_data_item['timestamp']][$form_data_item['input_key']] = $input_value;
} else {
$data[ $form_data_item['timestamp'] ] = array(
- $form_data_item['input_key'] => $form_data_item['input_value'],
+ $form_data_item['input_key'] => $input_value,
);
}
--- a/kirki/includes/Ajax/Page.php
+++ b/kirki/includes/Ajax/Page.php
@@ -18,6 +18,9 @@
*/
class Page {
+ const TYPE_FORGOT_PASSWORD = 'forgot_password';
+ const TYPE_RESET_PASSWORD = 'reset_password';
+
/**
* Save page data
*
--- a/kirki/includes/Frontend/Preview/Preview.php
+++ b/kirki/includes/Frontend/Preview/Preview.php
@@ -893,9 +893,71 @@
}
foreach ( $props as $prop_name => $prop_value ) {
- if ( is_array( $prop_value ) && isset( $prop_value['value'], $prop_value['unit'] ) ) {
- $css_declarations .= $prop_name . ':' . $prop_value['value'] . $prop_value['unit'] . ';';
- } elseif ( is_string( $prop_value ) || is_numeric( $prop_value ) ) {
+
+ // font-feature-settings
+ if (
+ $prop_name === 'font-feature-settings' &&
+ is_array( $prop_value ) &&
+ isset( $prop_value['value'] ) &&
+ is_array( $prop_value['value'] )
+ ) {
+ $css_string = implode(
+ ', ',
+ array_map(
+ function( $key ) {
+ return '"' . $key . '"';
+ },
+ array_keys( $prop_value['value'] )
+ )
+ );
+
+ $css_declarations .= $prop_name . ':' . $css_string . ';';
+ continue;
+ }
+
+ // value + unit object
+ if (
+ is_array( $prop_value ) &&
+ isset( $prop_value['value'], $prop_value['unit'] )
+ ) {
+
+ // font-size clamp
+ if (
+ $prop_name === 'font-size' &&
+ isset( $prop_value['type'] ) &&
+ str_contains( $prop_value['type'], 'clamp-' )
+ ) {
+
+ $min_value = isset($prop_value['min'], $prop_value['min']['value'] ) ? $prop_value['min']['value'] : 0;
+ $min_unit = isset($prop_value['min'], $prop_value['min']['unit'] ) ? $prop_value['min']['unit'] : 'px';
+
+ $base_value = isset($prop_value['base'], $prop_value['base']['value'] ) ? $prop_value['base']['value'] : 0;
+ $base_unit = isset($prop_value['base'], $prop_value['base']['unit'] ) ? $prop_value['base']['unit'] : 'px';
+
+ $max_value = isset($prop_value['max'], $prop_value['max']['value'] ) ? $prop_value['max']['value'] : 0;
+ $max_unit = isset($prop_value['max'], $prop_value['max']['unit'] ) ? $prop_value['max']['unit'] : 'px';
+
+ $css_declarations .= "{$prop_name}:clamp({$min_value}{$min_unit}, {$base_value}{$base_unit}, {$max_value}{$max_unit});";
+
+ // special max-width values
+ } elseif (
+ $prop_name === 'max-width' &&
+ in_array( $prop_value['value'], array( 'none', 'fit-content', 'max-content', 'min-content' ), true )
+ ) {
+
+ $css_declarations .= $prop_name . ':' . $prop_value['value'] . ';';
+
+ } else {
+
+ $css_declarations .= $prop_name . ':' . $prop_value['value'] . $prop_value['unit'] . ';';
+ }
+
+ // string or number
+ } elseif (
+ ( is_string( $prop_value ) && $prop_value !== '' ) ||
+ is_numeric( $prop_value )
+ ) {
+
$css_declarations .= $prop_name . ':' . $prop_value . ';';
}
}
@@ -1174,7 +1236,7 @@
if ( isset( $element['properties'] ) ) {
$properties = $element['properties'];
- if ( isset( $properties['interactions'] ) && ( ! isset( $element['stylePanels'] ) || ( isset( $element['stylePanels'], $element['stylePanels']['interaction'] ) && $element['stylePanels']['interaction'] ) ) ) {
+ if ( isset( $properties['interactions'] ) ) {
$this->interactions[ $id ] = $this->updateClassListForInteractionFromStyleBlockId( $properties['interactions'], $element );
}
@@ -1917,10 +1979,10 @@
}
if ( $dynamic_content['type'] === 'post' ) {
- if ( isset( $options['post'] ) && isset( $options['post']->{$dynamic_content['value']} ) ) {
+ if ( isset( $options['post'], $dynamic_content['value'] ) && isset( $options['post']->{$dynamic_content['value']} ) ) {
$href = $options['post']->{$dynamic_content['value']};
} else {
- $href = HelperFunctions::get_post_dynamic_content( $dynamic_content['value'], isset( $options['post'] ) ? $options['post'] : null );
+ $href = HelperFunctions::get_post_dynamic_content( isset($dynamic_content['value']) ? $dynamic_content['value'] : false, isset( $options['post'] ) ? $options['post'] : null );
}
} elseif ( $dynamic_content['type'] === 'term' && isset( $options['term'], $options['term']['term_id'] ) ) {
@@ -2279,15 +2341,6 @@
$ele_class_names = isset( $this_element['className'] ) ? explode( ' ', $this_element['className'] ) : array();
$class_array = array_merge( $ele_class_names, $class_array );
- // Check for disabled styles panels.
- if ( isset( $this_element['stylePanels'] ) && is_array( $this_element['stylePanels'] ) ) {
- foreach ( $this_element['stylePanels'] as $name => $value ) {
- if ( ! $value ) {
- array_push( $class_array,'kirki-disabled-' . $name );
- }
- }
- }
-
if ( in_array( $this_element['name'], $this->inline_elements, true ) ) {
array_push( $class_array, 'kirki-inline-element' );
}
--- a/kirki/includes/Frontend/Preview/Utils.php
+++ b/kirki/includes/Frontend/Preview/Utils.php
@@ -289,7 +289,7 @@
return $html;
}
// Post excerpt length for frontend. If post excerpt is used.
- if ( $dynamic_content['value'] === 'post_excerpt' && isset( $dynamic_content['postExcerptLength'] ) ) {
+ if (isset($dynamic_content['value']) && $dynamic_content['value'] === 'post_excerpt' && isset( $dynamic_content['postExcerptLength'] ) ) {
$post_excerpt_length = $dynamic_content['postExcerptLength'] ?? 55;
$GLOBALS['kirki_post_excerpt_length'] = $post_excerpt_length;
}
@@ -309,10 +309,15 @@
return $content;
}
+ // fix warning
+ if(!isset($dynamic_content['value'])){
+ $dynamic_content['value'] = false;
+ }
+
if ( $dynamic_content['type'] === 'post' ) {
$dynamic_options = array();
$cm_field_type = isset( $dynamic_content['cmFieldType'] ) ? $dynamic_content['cmFieldType'] : '';
- if ( 'post_date' === $dynamic_content['value'] && isset( $dynamic_content['format'] ) ) {
+ if ( isset( $dynamic_content['format'] ) && 'post_date' === $dynamic_content['value'] ) {
$dynamic_options['format'] = $dynamic_content['format'];
} elseif ( ( 'post_time' === $dynamic_content['value'] || 'time' === $cm_field_type ) && isset( $dynamic_content['timeFormat'] ) ) {
$dynamic_options['timeFormat'] = $dynamic_content['timeFormat'];
--- a/kirki/includes/HelperFunctions.php
+++ b/kirki/includes/HelperFunctions.php
@@ -2969,6 +2969,10 @@
* @param string|string[] $access_level The access level to check access.
*/
public static function has_access( $access_level ) {
+ if ( ! function_exists( 'wp_get_current_user' ) ) {
+ return false;
+ }
+
$user = wp_get_current_user();
$roles = $user->roles;
$has_access = false;
--- a/kirki/kirki.php
+++ b/kirki/kirki.php
@@ -7,7 +7,7 @@
* Plugin Name: Kirki
* Plugin URI: https://kirki.com
* Description: Kirki is an all-in-one no-code builder that empowers users to build professional-grade WordPress sites without writing any code. It’s a promising glimpse into the future of website development.
- * Version: 6.0.6
+ * Version: 6.0.7
* Author: Kirki
* Author URI: https://kirki.com
* Text Domain: kirki
@@ -24,7 +24,7 @@
// Define KIRKI_VERSION early to prevent bundled Kirki versions from loading.
if ( ! defined( 'KIRKI_VERSION' ) ) {
- define( 'KIRKI_VERSION', '6.0.6' );
+ define( 'KIRKI_VERSION', '6.0.7' );
}