Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions includes/Admin/Admin_Tools.php
Original file line number Diff line number Diff line change
Expand Up @@ -456,6 +456,18 @@ public function enable_json_upload( $file ) {
*/
public function import_forms() {
check_ajax_referer( 'wpuf_admin_tools' );

// Security: Check user has proper admin capabilities
if ( ! current_user_can( wpuf_admin_role() ) ) {
wp_send_json_error(
new WP_Error(
'wpuf_ajax_import_forms_error',
__( 'Unauthorized operation', 'wp-user-frontend' )
),
WP_Http::FORBIDDEN
);
}

if ( ! isset( $_POST['file_id'] ) ) {
wp_send_json_error(
new WP_Error(
Expand Down
7 changes: 7 additions & 0 deletions includes/Ajax/Admin_Form_Builder_Ajax.php
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@
$cat .= '<div class="wpuf-mt-6 wpuf-input-container taxonomy-container" data-taxonomy="' . esc_attr( $tax->name ) . '">';
$cat .= '<div class="wpuf-flex wpuf-items-center">';
$cat .= '<label for="' . esc_attr( $select_id ) . '" class="wpuf-text-sm wpuf-text-gray-700 wpuf-my-2">';
$cat .= sprintf( __( 'Default %s %s', 'wp-user-frontend' ), $post_type, $tax->label );

Check failure on line 189 in includes/Ajax/Admin_Form_Builder_Ajax.php

View workflow job for this annotation

GitHub Actions / Run PHPCS inspection

Multiple placeholders in translatable strings should be ordered. Expected "%1$s, %2$s", but got "%s, %s" in 'Default %s %s'.

Check failure on line 189 in includes/Ajax/Admin_Form_Builder_Ajax.php

View workflow job for this annotation

GitHub Actions / Run PHPCS inspection

A function call to __() with texts containing placeholders was found, but was not accompanied by a "translators:" comment on the line above to clarify the meaning of the placeholders.
$cat .= '</label></div>';

$cat .= '<select
Expand All @@ -201,7 +201,7 @@

if ( ! is_wp_error( $categories ) && ! empty( $categories ) ) {
foreach ( $categories as $category ) {
$selected = in_array( $category->term_id, (array) $current_value ) ? 'selected="selected"' : '';

Check failure on line 204 in includes/Ajax/Admin_Form_Builder_Ajax.php

View workflow job for this annotation

GitHub Actions / Run PHPCS inspection

Not using strict comparison for in_array; supply true for $strict argument.
$cat .= '<option value="' . esc_attr( $category->term_id ) . '" ' . $selected . '>' . esc_html( $category->name ) . '</option>';
}
}
Expand All @@ -219,6 +219,13 @@
}

public function get_roles() {
// Security: Check nonce and user capabilities
check_ajax_referer( 'wpuf-form-builder' );

if ( ! current_user_can( wpuf_admin_role() ) ) {
wp_send_json_error( __( 'Unauthorized operation', 'wp-user-frontend' ) );
}

$roles = wpuf_get_user_roles();

$html = '<div class="wpuf-mt-6 wpuf-input-container"><div class="wpuf-flex wpuf-items-center"><label for="default_category" class="wpuf-text-sm wpuf-text-gray-700 wpuf-my-2">' . __( 'Choose who can submit post ', 'wp-user-frontend' ) . '</label></div>';
Expand Down
22 changes: 19 additions & 3 deletions includes/Ajax/Frontend_Form_Ajax.php
Original file line number Diff line number Diff line change
Expand Up @@ -159,9 +159,9 @@ public function submit_post() {
'post_type' => ! empty( $this->form_settings['post_type'] ) ? $this->form_settings['post_type'] : 'post',
'post_status' => isset( $this->form_settings['post_status'] ) ? $this->form_settings['post_status'] : 'publish',
'post_author' => $post_author,
'post_title' => isset( $_POST['post_title'] ) ? sanitize_text_field( wp_unslash( $_POST['post_title'] ) ) : '',
'post_content' => isset( $_POST['post_content'] ) ? wp_kses( wp_unslash( $_POST['post_content'] ), $allowed_tags ) : '',
'post_excerpt' => isset( $_POST['post_excerpt'] ) ? wp_kses( wp_unslash( $_POST['post_excerpt'] ), $allowed_tags ) : '',
'post_title' => isset( $_POST['post_title'] ) ? strip_shortcodes( sanitize_text_field( wp_unslash( $_POST['post_title'] ) ) ) : '',
'post_content' => isset( $_POST['post_content'] ) ? strip_shortcodes( wp_kses( wp_unslash( $_POST['post_content'] ), $allowed_tags ) ) : '',
'post_excerpt' => isset( $_POST['post_excerpt'] ) ? strip_shortcodes( wp_kses( wp_unslash( $_POST['post_excerpt'] ), $allowed_tags ) ) : '',
];

// $charging_enabled = wpuf_get_option( 'charge_posting', 'wpuf_payment' );
Expand Down Expand Up @@ -228,6 +228,22 @@ public function submit_post() {
// if post_id is passed, we update the post
if ( isset( $_POST['post_id'] ) ) {
$post_id = intval( wp_unslash( $_POST['post_id'] ) );

// Verify the post exists
$post = get_post( $post_id );
if ( ! $post || is_wp_error( $post ) ) {
wpuf()->ajax->send_error( __( 'Post not found.', 'wp-user-frontend' ) );
}

// Security: Check if user has permission to edit this post (Broken Access Control fix)
$post_author = (int) get_post_field( 'post_author', $post_id );
$current_user_id = get_current_user_id();

// Allow edit if: user is post author OR user has edit_others_posts capability
if ( $current_user_id !== $post_author && ! current_user_can( 'edit_others_posts' ) ) {
wpuf()->ajax->send_error( __( 'You do not have permission to edit this post.', 'wp-user-frontend' ) );
}

$is_update = true;
$postarr['ID'] = $post_id;
$postarr['post_date'] = isset( $_POST['post_date'] ) ? sanitize_text_field( wp_unslash( $_POST['post_date'] ) ) : '';
Expand Down
4 changes: 2 additions & 2 deletions includes/Fields/Form_Field_Textarea.php
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
<?php

namespace WeDevs\Wpuf\Fields;

Check failure on line 3 in includes/Fields/Form_Field_Textarea.php

View workflow job for this annotation

GitHub Actions / Run PHPCS inspection

There must be one blank line after the namespace declaration


/**
* Textarea Field Class

Check warning on line 7 in includes/Fields/Form_Field_Textarea.php

View workflow job for this annotation

GitHub Actions / Run PHPCS inspection

Found precision alignment of 1 spaces.
*/

Check warning on line 8 in includes/Fields/Form_Field_Textarea.php

View workflow job for this annotation

GitHub Actions / Run PHPCS inspection

Found precision alignment of 1 spaces.
class Form_Field_Textarea extends Field_Contract {

public function __construct() {
Expand All @@ -14,8 +14,8 @@
$this->icon = 'menu-alt-2';
}

/**

Check warning on line 17 in includes/Fields/Form_Field_Textarea.php

View workflow job for this annotation

GitHub Actions / Run PHPCS inspection

Found precision alignment of 1 spaces.
* Render the Textarea field

Check warning on line 18 in includes/Fields/Form_Field_Textarea.php

View workflow job for this annotation

GitHub Actions / Run PHPCS inspection

Found precision alignment of 1 spaces.
*
* @param array $field_settings
* @param int $form_id
Expand Down Expand Up @@ -189,7 +189,7 @@
<?php if ( ! $hide_label ) : ?>
<label><?php echo esc_html( $field['label'] ); ?>:</label>
<?php endif; ?>
<?php echo wp_kses_post( wpautop( make_clickable( $data ) ) ); ?>
<?php echo wp_kses_post( wpautop( make_clickable( strip_shortcodes( $data ) ) ) ); ?>
</li>
<?php
return ob_get_clean();
Expand All @@ -206,6 +206,6 @@
* @return string
*/
public function sanitize_field_data( $data, $field ) {
return wp_kses_post( $data );
return strip_shortcodes( wp_kses_post( $data ) );
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Guard strip_shortcodes() against array inputs

sanitize_field_data() is invoked from FieldableTrait::prepare_meta_fields() for every field exposing that method, regardless of whether the submitted value is scalar or an array. It’s easy to end up with textarea data posted as an array (e.g., field[], repeatable rows, multi-language variants). With the new code we now hand that array directly to strip_shortcodes(), which throws a fatal TypeError on PHP 8+, whereas the previous implementation handled arrays gracefully. Please make sure the shortcode stripping only runs on scalar values and recurse when needed. For example:

     public function sanitize_field_data( $data, $field ) {
-        return strip_shortcodes( wp_kses_post( $data ) );
+        $sanitized = wp_kses_post( $data );
+
+        if ( is_array( $sanitized ) ) {
+            return array_map(
+                static function ( $value ) {
+                    return is_string( $value ) ? strip_shortcodes( $value ) : $value;
+                },
+                $sanitized
+            );
+        }
+
+        return strip_shortcodes( $sanitized );
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public function sanitize_field_data( $data, $field ) {
return wp_kses_post( $data );
return strip_shortcodes( wp_kses_post( $data ) );
}
public function sanitize_field_data( $data, $field ) {
$sanitized = wp_kses_post( $data );
if ( is_array( $sanitized ) ) {
return array_map(
static function ( $value ) {
return is_string( $value ) ? strip_shortcodes( $value ) : $value;
},
$sanitized
);
}
return strip_shortcodes( $sanitized );
}
🧰 Tools
🪛 PHPMD (2.15.0)

208-208: Avoid unused parameters such as '$field'. (undefined)

(UnusedFormalParameter)

🤖 Prompt for AI Agents
In includes/Fields/Form_Field_Textarea.php around lines 208-210,
sanitize_field_data() currently calls strip_shortcodes() directly on $data,
which will throw a TypeError if $data is an array; update the method to only
call strip_shortcodes() on scalar values and to recurse over arrays (preserving
keys) so nested/repeatable inputs are sanitized element-wise (apply wp_kses_post
then strip_shortcodes for each scalar), leaving non-scalar non-array values
unchanged.

}
29 changes: 18 additions & 11 deletions includes/Frontend/Form_Preview.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,19 +29,26 @@
private $form_id;

/**
* is_preview

Check failure on line 32 in includes/Frontend/Form_Preview.php

View workflow job for this annotation

GitHub Actions / Run PHPCS inspection

Doc comment short description must start with a capital letter
*
* @var string
*/
private $is_preview = true;

public function __construct() {
if ( ! isset( $_GET['wpuf_preview'] ) && empty( $_GET['wpuf'] ) ) {

Check warning on line 39 in includes/Frontend/Form_Preview.php

View workflow job for this annotation

GitHub Actions / Run PHPCS inspection

Processing form data without nonce verification.

Check warning on line 39 in includes/Frontend/Form_Preview.php

View workflow job for this annotation

GitHub Actions / Run PHPCS inspection

Processing form data without nonce verification.
return;
}
$this->form_id = isset( $_GET['form_id'] ) ? intval( $_GET['form_id'] ) : 0;

// Security: Check user has proper capabilities before allowing preview
if ( ! is_user_logged_in() || ! current_user_can( wpuf_admin_role() ) ) {
wp_die( __( 'You do not have permission to preview this form.', 'wp-user-frontend' ), 403 );
}

// Security: Validate and sanitize form_id parameter
$this->form_id = isset( $_GET['form_id'] ) ? absint( $_GET['form_id'] ) : 0;

Check warning on line 49 in includes/Frontend/Form_Preview.php

View workflow job for this annotation

GitHub Actions / Run PHPCS inspection

Processing form data without nonce verification.

Check warning on line 49 in includes/Frontend/Form_Preview.php

View workflow job for this annotation

GitHub Actions / Run PHPCS inspection

Processing form data without nonce verification.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

recheck nonce verification

add_action( 'pre_get_posts', [ $this, 'pre_get_posts' ] );
// add_filter( 'template_include', [ $this, 'template_include' ] );

Check warning on line 51 in includes/Frontend/Form_Preview.php

View workflow job for this annotation

GitHub Actions / Run PHPCS inspection

This comment is 59% valid code; is this commented out code?
add_filter( 'the_title', [ $this, 'the_title' ] );
add_filter( 'the_content', [ $this, 'the_content' ] );
add_filter( 'get_the_excerpt', [ $this, 'the_content' ] );
Expand Down Expand Up @@ -76,19 +83,19 @@
*
* @return string
*/
public function the_content( $content ) {

Check warning on line 86 in includes/Frontend/Form_Preview.php

View workflow job for this annotation

GitHub Actions / Run PHPCS inspection

The method parameter $content is never used
if ( $this->is_preview ) {
if ( ! is_user_logged_in() ) {
return __( 'You must be logged in to preview this form.', 'wp-user-frontend' );
}
$viewing_capability = apply_filters( 'wpuf_preview_form_cap',
'edit_posts' ); // at least has to be contributor
if ( ! current_user_can( $viewing_capability ) ) {
return __( 'Sorry, you are not eligible to preview this form.', 'wp-user-frontend' );
}
// Security: Double-check admin capabilities
if ( ! current_user_can( wpuf_admin_role() ) ) {
return __( 'You do not have permission to preview this form.', 'wp-user-frontend' );
}

// Security: Validate form_id is a valid integer to prevent injection
$form_id = absint( $this->form_id );
if ( $form_id === 0 ) {
return __( 'Invalid form ID.', 'wp-user-frontend' );
}

return do_shortcode( sprintf( '[wpuf_form id="%d"]', $this->form_id ) );
return do_shortcode( sprintf( '[wpuf_form id="%d"]', $form_id ) );
}

/**
Expand Down
5 changes: 5 additions & 0 deletions includes/Frontend_Render_Form.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,13 @@


/**
* render submit button

Check failure on line 42 in includes/Frontend_Render_Form.php

View workflow job for this annotation

GitHub Actions / Run PHPCS inspection

Doc comment short description must start with a capital letter
*
* @param [type] $form_id [description]
* @param [type] $form_settings [description]
* @param [type] $post_id [description]
*/
public function submit_button( $form_id, $form_settings, $post_id = null ) { ?>

Check failure on line 48 in includes/Frontend_Render_Form.php

View workflow job for this annotation

GitHub Actions / Run PHPCS inspection

Opening brace must be the last content on the line

<li class="wpuf-submit">
<div class="wpuf-label">
Expand Down Expand Up @@ -84,7 +84,7 @@
}

/**
* guest post field

Check failure on line 87 in includes/Frontend_Render_Form.php

View workflow job for this annotation

GitHub Actions / Run PHPCS inspection

Doc comment short description must start with a capital letter
*
* @param [type] $form_settings [description]
*/
Expand Down Expand Up @@ -118,6 +118,11 @@
* @return void
*/
public function preview_form() {
// Security: Check user has proper admin capabilities
if ( ! current_user_can( wpuf_admin_role() ) ) {
wp_send_json_error( __( 'Unauthorized operation', 'wp-user-frontend' ) );
}

$form_id = isset( $_GET['form_id'] ) ? intval( wp_unslash( $_GET['form_id'] ) ) : 0;

if ( $form_id ) {
Expand All @@ -130,7 +135,7 @@
<head>
<meta charset="UTF-8">
<title>__( 'Form Preview', 'wp-user-frontend' )</title>
<link rel="stylesheet" href="<?php echo esc_url( plugins_url( 'assets/css/frontend-forms.css', __DIR__ ) ); ?>">

Check failure on line 138 in includes/Frontend_Render_Form.php

View workflow job for this annotation

GitHub Actions / Run PHPCS inspection

Stylesheets must be registered/enqueued via wp_enqueue_style()

<style type="text/css">
body {
Expand All @@ -150,7 +155,7 @@
}
</style>

<script type="text/javascript" src="<?php echo esc_url( includes_url( 'js/jquery/jquery.js' ) ); ?>"></script>

Check failure on line 158 in includes/Frontend_Render_Form.php

View workflow job for this annotation

GitHub Actions / Run PHPCS inspection

Scripts must be registered/enqueued via wp_enqueue_script()
</head>
<body>
<div class="container">
Expand Down
10 changes: 5 additions & 5 deletions includes/Traits/FieldableTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -589,9 +589,9 @@ public static function prepare_meta_fields( $meta_vars ) {
$meta_key_value[ $value['name'] ] = $wpuf_field->sanitize_field_data( $posted_field_data, $value );
continue;
} elseif ( isset( $post_data[ $value['name'] ] ) && is_array( $post_data[ $value['name'] ] ) ) {
$value_name = isset( $post_data[ $value['name'] ] ) ? array_map( 'sanitize_text_field', wp_unslash( $post_data[ $value['name'] ] ) ) : '';
$value_name = isset( $post_data[ $value['name'] ] ) ? array_map( function( $item ) { return strip_shortcodes( sanitize_text_field( $item ) ); }, wp_unslash( $post_data[ $value['name'] ] ) ) : '';
} else {
$value_name = isset( $post_data[ $value['name'] ] ) ? sanitize_text_field( wp_unslash( $post_data[ $value['name'] ] ) ) : '';
$value_name = isset( $post_data[ $value['name'] ] ) ? strip_shortcodes( sanitize_text_field( wp_unslash( $post_data[ $value['name'] ] ) ) ) : '';
}

if ( isset( $post_data['wpuf_files'][ $value['name'] ] ) ) {
Expand Down Expand Up @@ -635,13 +635,13 @@ 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( 'sanitize_text_field', $row[ $fname ] );
$sanitized_row[ $fname ] = array_map( function( $item ) { return strip_shortcodes( sanitize_text_field( $item ) ); }, $row[ $fname ] );
} else {
$sanitized_row[ $fname ] = sanitize_text_field( $row[ $fname ] );
$sanitized_row[ $fname ] = strip_shortcodes( sanitize_text_field( $row[ $fname ] ) );
}
} else {
// For other fields, sanitize as string
$sanitized_row[ $fname ] = sanitize_text_field( $row[ $fname ] );
$sanitized_row[ $fname ] = strip_shortcodes( sanitize_text_field( $row[ $fname ] ) );
}
} else {
$sanitized_row[ $fname ] = '';
Expand Down
20 changes: 10 additions & 10 deletions wpuf-functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -1202,15 +1202,15 @@ function wpuf_show_custom_fields( $content ) {
// Handle different field types
if ( 'checkbox' === $inner_field['input_type'] && is_array( $inner_field_value ) ) {
// For checkbox fields, join multiple values
$repeat_html .= '<span>' . make_clickable( implode( ', ', $inner_field_value ) ) . '</span>';
$repeat_html .= '<span>' . make_clickable( strip_shortcodes( implode( ', ', $inner_field_value ) ) ) . '</span>';
} elseif ( 'multiselect' === $inner_field['input_type'] && is_array( $inner_field_value ) ) {
$repeat_html .= '<span>' . make_clickable( implode( ', ', $inner_field_value ) ) . '</span>';
$repeat_html .= '<span>' . make_clickable( strip_shortcodes( implode( ', ', $inner_field_value ) ) ) . '</span>';
} elseif ( 'radio' === $inner_field['input_type'] || 'select' === $inner_field['input_type'] ) {
// For radio and select fields, display single value
$repeat_html .= '<span>' . make_clickable( $inner_field_value ) . '</span>';
$repeat_html .= '<span>' . make_clickable( strip_shortcodes( $inner_field_value ) ) . '</span>';
} else {
// For text and other fields
$repeat_html .= '<span>' . make_clickable( $inner_field_value ) . '</span>';
$repeat_html .= '<span>' . make_clickable( strip_shortcodes( $inner_field_value ) ) . '</span>';
}

$repeat_html .= '</li>';
Expand Down Expand Up @@ -1279,7 +1279,7 @@ function wpuf_show_custom_fields( $content ) {
$html .= '<label>' . $attr['label'] . ':</label>';
}

$html .= sprintf( ' %s</li>', make_clickable( $value ) );
$html .= sprintf( ' %s</li>', make_clickable( strip_shortcodes( $value ) ) );
break;

case 'country_list':
Expand All @@ -1297,7 +1297,7 @@ function wpuf_show_custom_fields( $content ) {
$html .= '<label>' . $attr['label'] . ':</label>';
}

$html .= sprintf( ' %s</li>', make_clickable( $value ) );
$html .= sprintf( ' %s</li>', make_clickable( strip_shortcodes( $value ) ) );
break;

default:
Expand All @@ -1318,7 +1318,7 @@ function wpuf_show_custom_fields( $content ) {
$html .= '<label>' . $attr['label'] . ':</label>';
}

$html .= sprintf( ' %s</li>', make_clickable( $modified_value ) );
$html .= sprintf( ' %s</li>', make_clickable( strip_shortcodes( $modified_value ) ) );
}
} elseif ( ( 'checkbox' === $attr['input_type'] || 'multiselect' === $attr['input_type'] ) && is_array( $value[0] ) ) {
if ( ! empty( $value[0] ) ) {
Expand All @@ -1331,7 +1331,7 @@ function wpuf_show_custom_fields( $content ) {
$html .= '<label>' . $attr['label'] . ':</label>';
}

$html .= sprintf( ' %s</li>', make_clickable( $modified_value ) );
$html .= sprintf( ' %s</li>', make_clickable( strip_shortcodes( $modified_value ) ) );
}
}
} else {
Expand All @@ -1344,7 +1344,7 @@ function wpuf_show_custom_fields( $content ) {
$html .= '<label>' . $attr['label'] . ':</label>';
}

$html .= sprintf( ' %s</li>', make_clickable( $new ) );
$html .= sprintf( ' %s</li>', make_clickable( strip_shortcodes( $new ) ) );
}
}

Expand Down Expand Up @@ -1519,7 +1519,7 @@ function wpuf_meta_shortcode( $atts ) {
} elseif ( 'normal' === $type ) {
return implode( ', ', get_post_meta( $post->ID, $name ) );
} else {
return make_clickable( implode( ', ', get_post_meta( $post->ID, $name ) ) );
return make_clickable( strip_shortcodes( implode( ', ', get_post_meta( $post->ID, $name ) ) ) );
}
}

Expand Down
Loading