diff --git a/Lib/WeDevs_Settings_API.php b/Lib/WeDevs_Settings_API.php index 31e8bcb86..aae61602c 100644 --- a/Lib/WeDevs_Settings_API.php +++ b/Lib/WeDevs_Settings_API.php @@ -107,7 +107,7 @@ function admin_init() { if ( isset($section['desc']) && !empty($section['desc']) ) { $section['desc'] = '
' . $section['desc'] . '
'; - $callback = create_function('', 'echo "' . str_replace( '"', '\"', $section['desc'] ) . '";'); // phpcs:ignore + $callback = function () use ( $section ) { echo wp_kses_post( $section['desc'] ); }; // phpcs:ignore } else if ( isset( $section['callback'] ) ) { $callback = $section['callback']; } else { diff --git a/includes/Admin/Admin_Tools.php b/includes/Admin/Admin_Tools.php index c51649b32..0a8c19a56 100644 --- a/includes/Admin/Admin_Tools.php +++ b/includes/Admin/Admin_Tools.php @@ -184,13 +184,29 @@ public static function import_json_file( $file ) { $errors = new WP_Error(); + $allowed_post_types = [ 'wpuf_forms', 'wpuf_profile' ]; + $allowed_post_statuses = [ 'publish', 'draft', 'pending' ]; + foreach ( $options as $key => $value ) { + // Allowlist post_type and post_status to prevent mass-assignment of an + // arbitrary post type/status through an imported JSON file. + $post_type = $value['post_data']['post_type'] ?? ''; + $post_status = $value['post_data']['post_status'] ?? ''; + + if ( ! in_array( $post_type, $allowed_post_types, true ) ) { + $post_type = 'wpuf_forms'; + } + + if ( ! in_array( $post_status, $allowed_post_statuses, true ) ) { + $post_status = 'publish'; + } + $generate_post = [ - 'post_title' => $value['post_data']['post_title'], - 'post_status' => $value['post_data']['post_status'], - 'post_type' => $value['post_data']['post_type'], - 'ping_status' => $value['post_data']['ping_status'], - 'comment_status' => $value['post_data']['comment_status'], + 'post_title' => $value['post_data']['post_title'] ?? '', + 'post_status' => $post_status, + 'post_type' => $post_type, + 'ping_status' => $value['post_data']['ping_status'] ?? '', + 'comment_status' => $value['post_data']['comment_status'] ?? '', ]; $post_id = wp_insert_post( $generate_post, true ); diff --git a/includes/Traits/FieldableTrait.php b/includes/Traits/FieldableTrait.php index d85bea091..8d5266cc7 100644 --- a/includes/Traits/FieldableTrait.php +++ b/includes/Traits/FieldableTrait.php @@ -887,7 +887,12 @@ public static function prepare_meta_fields( $meta_vars ) { if ( in_array( $inner_field['template'], [ 'checkbox_field', 'multiple_select' ] ) ) { // For checkbox and multiselect, keep as array and sanitize each element if ( is_array( $row[ $fname ] ) ) { - $sanitized_row[ $fname ] = array_map( function( $item ) { return strip_shortcodes( sanitize_text_field( $item ) ); }, $row[ $fname ] ); + $sanitized_row[ $fname ] = array_map( + function ( $item ) { + return strip_shortcodes( sanitize_text_field( $item ) ); + }, + $row[ $fname ] + ); } else { $sanitized_row[ $fname ] = strip_shortcodes( sanitize_text_field( $row[ $fname ] ) ); } @@ -903,10 +908,21 @@ public static function prepare_meta_fields( $meta_vars ) { } $meta_key_value[ $value['name'] ] = $rows; } else { - // Fallback to old logic for single-field repeaters or legacy structure - $meta_key_value[ $value['name'] ] = is_array( $repeater_value ) ? implode( - self::$separator, $repeater_value - ) : ''; + // Fallback to old logic for single-field repeaters or legacy structure. + // Sanitize each element at the storage layer (no output escaping so + // structured/multi-value data still round-trips). + if ( is_array( $repeater_value ) ) { + $sanitized_repeater = array_map( + function ( $item ) { + return strip_shortcodes( sanitize_text_field( $item ) ); + }, + $repeater_value + ); + + $meta_key_value[ $value['name'] ] = implode( self::$separator, $sanitized_repeater ); + } else { + $meta_key_value[ $value['name'] ] = ''; + } } break; diff --git a/includes/class-frontend-render-form.php b/includes/class-frontend-render-form.php index fd745f74f..276062a52 100644 --- a/includes/class-frontend-render-form.php +++ b/includes/class-frontend-render-form.php @@ -783,8 +783,22 @@ public static function prepare_meta_fields( $meta_vars ) { $formatted_key = $parent_name . '_' . $i . '_' . $meta_key; if ( '' !== $meta_key && ! empty( $_POST[ $formatted_key ] ) ) { - $repeat_fields['fields'][ $formatted_key ] = wp_unslash( $_POST[ $formatted_key ] ); - $meta_key_value[ $formatted_key ] = wp_unslash( $_POST[ $formatted_key ] ); // phpcs:ignore WordPress.Security + // Storage-layer sanitization for repeat-field child values. Use the + // textarea sanitizer for textarea children (preserve newlines), + // otherwise the text-field sanitizer. Sanitize per element so + // multi-value repeat data still round-trips. + $repeat_sanitize_cb = ( isset( $value['input_type'] ) && 'textarea' === $value['input_type'] ) + ? 'sanitize_textarea_field' + : 'sanitize_text_field'; + + $raw_repeat_value = wp_unslash( $_POST[ $formatted_key ] ); // phpcs:ignore WordPress.Security.NonceVerification + + $sanitized_repeat_value = is_array( $raw_repeat_value ) + ? array_map( $repeat_sanitize_cb, $raw_repeat_value ) + : call_user_func( $repeat_sanitize_cb, $raw_repeat_value ); + + $repeat_fields['fields'][ $formatted_key ] = $sanitized_repeat_value; + $meta_key_value[ $formatted_key ] = $sanitized_repeat_value; } } } diff --git a/wpuf-functions.php b/wpuf-functions.php index fe2f0bc2e..f3941ba19 100644 --- a/wpuf-functions.php +++ b/wpuf-functions.php @@ -1245,7 +1245,7 @@ function wpuf_show_custom_fields( $content ) { $address_html .= '
  • '; if ( 'no' === $hide_label ) { - $address_html .= ' '; + $address_html .= ' '; } $address_html .= ' ' . $value . '
  • '; @@ -1275,7 +1275,7 @@ function wpuf_show_custom_fields( $content ) { $repeat_html = '
  • '; if ( 'no' === $hide_label ) { - $repeat_html .= ''; + $repeat_html .= ''; } $repeat_html .= '