Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
2 changes: 1 addition & 1 deletion Lib/WeDevs_Settings_API.php
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ function admin_init() {

if ( isset($section['desc']) && !empty($section['desc']) ) {
$section['desc'] = '<div class="inside">' . $section['desc'] . '</div>';
$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 {
Expand Down
26 changes: 21 additions & 5 deletions includes/Admin/Admin_Tools.php
Original file line number Diff line number Diff line change
Expand Up @@ -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 );
Expand Down
26 changes: 21 additions & 5 deletions includes/Traits/FieldableTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
$taxonomy_templates['post_tag'] = new Form_Field_Post_Tags();
} else {
$taxonomy_templates[ $tax_name ] = new Form_Field_Post_Taxonomy( $tax_name, $taxonomy );
// $taxonomy_templates[ 'taxonomy' ] = new WPUF_Form_Field_Post_Taxonomy($tax_name, $taxonomy);

Check warning on line 50 in includes/Traits/FieldableTrait.php

View workflow job for this annotation

GitHub Actions / Run PHPCS inspection

This comment is 64% valid code; is this commented out code?
}
}
}
Expand Down Expand Up @@ -85,22 +85,22 @@
$wpuf_post_types[] = 'download';
}

$ignore_taxonomies = apply_filters( 'wpuf-ignore-taxonomies', [

Check warning on line 88 in includes/Traits/FieldableTrait.php

View workflow job for this annotation

GitHub Actions / Run PHPCS inspection

Words in hook names should be separated using underscores. Expected: 'wpuf_ignore_taxonomies', but found: 'wpuf-ignore-taxonomies'.

Check failure on line 88 in includes/Traits/FieldableTrait.php

View workflow job for this annotation

GitHub Actions / Run PHPCS inspection

Opening parenthesis of a multi-line function call must be the last content on the line
'post_format',
] );

Check failure on line 90 in includes/Traits/FieldableTrait.php

View workflow job for this annotation

GitHub Actions / Run PHPCS inspection

Closing parenthesis of a multi-line function call must be on a line by itself
foreach ( $wpuf_post_types as $post_type ) {
$this->wp_post_types[ $post_type ] = [];
$taxonomies = get_object_taxonomies( $post_type, 'object' );
foreach ( $taxonomies as $tax_name => $taxonomy ) {
if ( ! in_array( $tax_name, $ignore_taxonomies ) ) {

Check failure on line 95 in includes/Traits/FieldableTrait.php

View workflow job for this annotation

GitHub Actions / Run PHPCS inspection

Not using strict comparison for in_array; supply true for $strict argument.
$this->wp_post_types[ $post_type ][ $tax_name ] = [
'title' => $taxonomy->label,
'hierarchical' => $taxonomy->hierarchical,
];
$this->wp_post_types[ $post_type ][ $tax_name ]['terms'] = get_terms( [

Check failure on line 100 in includes/Traits/FieldableTrait.php

View workflow job for this annotation

GitHub Actions / Run PHPCS inspection

Opening parenthesis of a multi-line function call must be the last content on the line
'taxonomy' => $tax_name,
'hide_empty' => false,
] );

Check failure on line 103 in includes/Traits/FieldableTrait.php

View workflow job for this annotation

GitHub Actions / Run PHPCS inspection

Closing parenthesis of a multi-line function call must be on a line by itself
}
}

Expand Down Expand Up @@ -150,7 +150,7 @@
}

/**
* get Input fields

Check failure on line 153 in includes/Traits/FieldableTrait.php

View workflow job for this annotation

GitHub Actions / Run PHPCS inspection

Doc comment short description must start with a capital letter
*
* @param array $form_vars
*
Expand All @@ -158,28 +158,28 @@
*/
public function get_input_fields( $form_vars ) {
$ignore_lists = [ 'section_break', 'html' ];
$post_vars = $meta_vars = $taxonomy_vars = [];

Check failure on line 161 in includes/Traits/FieldableTrait.php

View workflow job for this annotation

GitHub Actions / Run PHPCS inspection

Assignments must be the first block of code on a line

Check failure on line 161 in includes/Traits/FieldableTrait.php

View workflow job for this annotation

GitHub Actions / Run PHPCS inspection

Assignments must be the first block of code on a line

foreach ( $form_vars as $key => $value ) {
// get column field input fields
if ( $value['input_type'] == 'column_field' ) {

Check warning on line 165 in includes/Traits/FieldableTrait.php

View workflow job for this annotation

GitHub Actions / Run PHPCS inspection

Loose comparisons are not allowed. Expected: "==="; Found: "=="
$inner_fields = $value['inner_fields'];

foreach ( $inner_fields as $column_key => $column_fields ) {
if ( ! empty( $column_fields ) ) {
// ignore section break and HTML input type
foreach ( $column_fields as $column_field_key => $column_field ) {
if ( in_array( $column_field['input_type'], $ignore_lists ) ) {

Check failure on line 172 in includes/Traits/FieldableTrait.php

View workflow job for this annotation

GitHub Actions / Run PHPCS inspection

Not using strict comparison for in_array; supply true for $strict argument.
continue;
}

//separate the post and custom fields
if ( isset( $column_field['is_meta'] ) && $column_field['is_meta'] == 'yes' ) {

Check warning on line 177 in includes/Traits/FieldableTrait.php

View workflow job for this annotation

GitHub Actions / Run PHPCS inspection

Loose comparisons are not allowed. Expected: "==="; Found: "=="
$meta_vars[] = $column_field;
continue;
}

if ( $column_field['input_type'] == 'taxonomy' ) {

Check warning on line 182 in includes/Traits/FieldableTrait.php

View workflow job for this annotation

GitHub Actions / Run PHPCS inspection

Loose comparisons are not allowed. Expected: "==="; Found: "=="

// don't add "category"
// if ( $column_field['name'] == 'category' ) {
Expand All @@ -197,17 +197,17 @@
}

// ignore section break and HTML input type
if ( in_array( $value['input_type'], $ignore_lists ) ) {

Check failure on line 200 in includes/Traits/FieldableTrait.php

View workflow job for this annotation

GitHub Actions / Run PHPCS inspection

Not using strict comparison for in_array; supply true for $strict argument.
continue;
}

//separate the post and custom fields
if ( isset( $value['is_meta'] ) && $value['is_meta'] == 'yes' ) {

Check warning on line 205 in includes/Traits/FieldableTrait.php

View workflow job for this annotation

GitHub Actions / Run PHPCS inspection

Loose comparisons are not allowed. Expected: "==="; Found: "=="
$meta_vars[] = $value;
continue;
}

if ( $value['input_type'] == 'taxonomy' ) {

Check warning on line 210 in includes/Traits/FieldableTrait.php

View workflow job for this annotation

GitHub Actions / Run PHPCS inspection

Loose comparisons are not allowed. Expected: "==="; Found: "=="

// don't add "category"
// if ( $value['name'] == 'category' ) {
Expand Down Expand Up @@ -259,10 +259,10 @@
wpuf()->ajax->send_error( __( 'Empty reCaptcha Field', 'wp-user-frontend' ) );
}

if ( $recaptcha_type == 'enable_no_captcha' ) {

Check warning on line 262 in includes/Traits/FieldableTrait.php

View workflow job for this annotation

GitHub Actions / Run PHPCS inspection

Loose comparisons are not allowed. Expected: "==="; Found: "=="
$no_captcha = 1;
$invisible_captcha = 0;
} elseif ( $recaptcha_type == 'invisible_recaptcha' ) {

Check warning on line 265 in includes/Traits/FieldableTrait.php

View workflow job for this annotation

GitHub Actions / Run PHPCS inspection

Loose comparisons are not allowed. Expected: "==="; Found: "=="
$invisible_captcha = 1;
$no_captcha = 0;
} else {
Expand Down Expand Up @@ -370,7 +370,7 @@
$remote_addr = isset( $_SERVER['REMOTE_ADDR'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REMOTE_ADDR'] ) ) : '';
$g_recaptcha_response = isset( $_POST['g-recaptcha-response'] ) ? sanitize_text_field( wp_unslash( $_POST['g-recaptcha-response'] ) ) : '';

if ( $no_captcha == 1 && 0 == $invisible ) {

Check warning on line 373 in includes/Traits/FieldableTrait.php

View workflow job for this annotation

GitHub Actions / Run PHPCS inspection

Loose comparisons are not allowed. Expected: "==="; Found: "=="
if ( ! class_exists( 'WPUF_ReCaptcha' ) ) {
require_once WPUF_ROOT . '/Lib/recaptchalib_noCaptcha.php';
}
Expand Down Expand Up @@ -887,7 +887,12 @@
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 ] ) );
}
Expand All @@ -903,10 +908,21 @@
}
$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;

Expand Down
18 changes: 16 additions & 2 deletions includes/class-frontend-render-form.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Comment on lines +786 to +801

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Confirm which prepare_meta_fields is actually invoked for repeat fields at runtime,
# and whether output for this storage path (formatted_key meta) is escaped/stripped downstream.
rg -n "prepare_meta_fields" --type=php -C3

Repository: weDevsOfficial/wp-user-frontend

Length of output: 169


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Map the relevant files and locate repeat-field sanitization and any shortcode stripping.
git ls-files 'includes/class-frontend-render-form.php' 'includes/Traits/FieldableTrait.php' \
  | xargs -r -n1 sh -c 'echo "### $1"; wc -l "$1"; echo; ast-grep outline "$1" --view expanded || true; echo' sh

printf '\n## strip_shortcodes references\n'
rg -n "strip_shortcodes|prepare_meta_fields|repeat" includes/class-frontend-render-form.php includes/Traits/FieldableTrait.php -C 3

Repository: weDevsOfficial/wp-user-frontend

Length of output: 19831


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the exact surrounding code for the repeater sanitization paths.
sed -n '760,830p' includes/class-frontend-render-form.php
printf '\n---\n'
sed -n '860,950p' includes/Traits/FieldableTrait.php

Repository: weDevsOfficial/wp-user-frontend

Length of output: 8720


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Find all repeater-related sanitization paths and any downstream shortcode stripping.
rg -n "sanitize_text_field|sanitize_textarea_field|strip_shortcodes|repeat" includes -g '*.php' -C 2

Repository: weDevsOfficial/wp-user-frontend

Length of output: 50388


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Find the call sites for both prepare_meta_fields implementations and the submit path that reaches them.
rg -n "class-frontend-render-form::prepare_meta_fields|FieldableTrait::prepare_meta_fields|prepare_meta_fields\(" includes -g '*.php' -C 2

printf '\n## class definitions / inheritance\n'
rg -n "class .*Frontend_Render_Form|use FieldableTrait|extends .*FieldableTrait|trait FieldableTrait" includes -g '*.php' -C 2

Repository: weDevsOfficial/wp-user-frontend

Length of output: 4506


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Find the call sites for both prepare_meta_fields implementations and the submit path that reaches them.
rg -n "prepare_meta_fields\(" includes -g '*.php' -C 2

printf '\n## class definitions / inheritance\n'
rg -n "class .*Frontend_Render_Form|use FieldableTrait|extends .*FieldableTrait|trait FieldableTrait" includes -g '*.php' -C 2

Repository: weDevsOfficial/wp-user-frontend

Length of output: 4506


Strip shortcodes from repeat-child storage too includes/class-frontend-render-form.php:786-801 only applies sanitize_text_field() / sanitize_textarea_field() here, while the other repeater storage path also runs strip_shortcodes(). Add the same stripping here so shortcode handling stays consistent.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@includes/class-frontend-render-form.php` around lines 786 - 801, The
repeat-child storage path in class-frontend-render-form is missing shortcode
stripping, unlike the other repeater storage flow. Update the sanitization in
the repeat field handling around the raw_repeat_value and sanitized_repeat_value
logic to apply strip_shortcodes alongside the existing
sanitize_text_field/sanitize_textarea_field behavior for both scalar and array
values. Keep the change localized to the repeat storage branch so the
repeat_fields and meta_key_value assignments remain consistent with the other
storage path.

}
}
}
Expand Down
50 changes: 30 additions & 20 deletions wpuf-functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -1245,7 +1245,7 @@ function wpuf_show_custom_fields( $content ) {
$address_html .= '<li>';

if ( 'no' === $hide_label ) {
$address_html .= '<label>' . $attr['address'][ $field_key ]['label'] . ': </label> ';
$address_html .= '<label>' . esc_html( $attr['address'][ $field_key ]['label'] ) . ': </label> ';
}

$address_html .= ' ' . $value . '</li>';
Expand Down Expand Up @@ -1275,7 +1275,7 @@ function wpuf_show_custom_fields( $content ) {
$repeat_html = '<li>';

if ( 'no' === $hide_label ) {
$repeat_html .= '<label>' . $attr['label'] . ':</label>';
$repeat_html .= '<label>' . esc_html( $attr['label'] ) . ':</label>';
}

$repeat_html .= '<ul class="wpuf-repeat-field-data">';
Expand All @@ -1294,7 +1294,7 @@ function wpuf_show_custom_fields( $content ) {
$repeat_html .= '<li>';

if ( 'no' === $inner_field['hide_field_label'] ) {
$repeat_html .= '<label>' . $inner_field['label'] . ':</label> ';
$repeat_html .= '<label>' . esc_html( $inner_field['label'] ) . ':</label> ';
}

// Handle different field types
Expand Down Expand Up @@ -1338,12 +1338,12 @@ function wpuf_show_custom_fields( $content ) {

$preview_width = isset( $attr['preview_width'] ) ? $attr['preview_width'] : '123';
$preview_height = isset( $attr['preview_height'] ) ? $attr['preview_height'] : '456';
$shortcode = '[embed width="' . $preview_width . '" height="' . $preview_height . '"]' . $value . '[/embed]';
$shortcode = '[embed width="' . $preview_width . '" height="' . $preview_height . '"]' . esc_url_raw( $value ) . '[/embed]';

$preview = '<li>';

if ( 'no' === $hide_label ) {
$preview .= sprintf( '<label>%s: </label>', $attr['label'] );
$preview .= sprintf( '<label>%s: </label>', esc_html( $attr['label'] ) );
}

$preview .= "<div class='wpuf-embed-preview'>";
Expand All @@ -1360,10 +1360,10 @@ function wpuf_show_custom_fields( $content ) {
$link = '<li>';

if ( 'no' === $hide_label ) {
$link .= '<label>' . $attr['label'] . ':</label>';
$link .= '<label>' . esc_html( $attr['label'] ) . ':</label>';
}

$link .= sprintf( " <a href='%s' target = '%s'>%s</a></li>", $value, $open_in, $value );
$link .= sprintf( " <a href='%s' target = '%s'>%s</a></li>", esc_url( $value ), esc_attr( $open_in ), esc_html( $value ) );

$html .= $link;
break;
Expand All @@ -1374,7 +1374,7 @@ function wpuf_show_custom_fields( $content ) {
$html .= '<li>';

if ( 'no' === $hide_label ) {
$html .= '<label>' . $attr['label'] . ':</label>';
$html .= '<label>' . esc_html( $attr['label'] ) . ':</label>';
}

$html .= sprintf( ' %s</li>', make_clickable( strip_shortcodes( $value ) ) );
Expand All @@ -1392,7 +1392,7 @@ function wpuf_show_custom_fields( $content ) {
$html .= '<li>';

if ( 'no' === $hide_label ) {
$html .= '<label>' . $attr['label'] . ':</label>';
$html .= '<label>' . esc_html( $attr['label'] ) . ':</label>';
}

$html .= sprintf( ' %s</li>', make_clickable( strip_shortcodes( $value ) ) );
Expand All @@ -1413,7 +1413,7 @@ function wpuf_show_custom_fields( $content ) {
$html .= '<li>';

if ( 'no' === $hide_label ) {
$html .= '<label>' . $attr['label'] . ':</label>';
$html .= '<label>' . esc_html( $attr['label'] ) . ':</label>';
}

$html .= sprintf( ' %s</li>', make_clickable( strip_shortcodes( $modified_value ) ) );
Expand All @@ -1426,7 +1426,7 @@ function wpuf_show_custom_fields( $content ) {
$html .= '<li>';

if ( 'no' === $hide_label ) {
$html .= '<label>' . $attr['label'] . ':</label>';
$html .= '<label>' . esc_html( $attr['label'] ) . ':</label>';
}

$html .= sprintf( ' %s</li>', make_clickable( strip_shortcodes( $modified_value ) ) );
Expand All @@ -1439,7 +1439,7 @@ function wpuf_show_custom_fields( $content ) {
$html .= '<li>';

if ( 'no' === $hide_label ) {
$html .= '<label>' . $attr['label'] . ':</label>';
$html .= '<label>' . esc_html( $attr['label'] ) . ':</label>';
}

$html .= sprintf( ' %s</li>', make_clickable( strip_shortcodes( $new ) ) );
Expand Down Expand Up @@ -2367,19 +2367,22 @@ function wpuf_get_completed_transactions( $args = [] ) {

$args = wp_parse_args( $args, $defaults );

if ( ! in_array( $args['orderby'], $orderby ) ) {
if ( ! in_array( $args['orderby'], $orderby, true ) ) {
$args['orderby'] = 'id';
}

if ( ! in_array( $args['order'], $order ) ) {
if ( ! in_array( $args['order'], $order, true ) ) {
$args['order'] = 'DESC';
}

if ( $args['count'] ) {
return $wpdb->get_var( "SELECT COUNT(*) FROM {$wpdb->prefix}wpuf_transaction" );
}

$result = $wpdb->get_results( "SELECT * FROM {$wpdb->prefix}wpuf_transaction ORDER BY `{$args['orderby']}` {$args['order']} LIMIT {$args['offset']}, {$args['number']}", OBJECT );
$offset = absint( $args['offset'] );
$number = absint( $args['number'] );

$result = $wpdb->get_results( "SELECT * FROM {$wpdb->prefix}wpuf_transaction ORDER BY `{$args['orderby']}` {$args['order']} LIMIT {$offset}, {$number}", OBJECT );
Comment on lines +2370 to +2385

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Use $wpdb->prepare() for the LIMIT clause instead of raw interpolation.

$offset/$number are safely absint()'d, so this isn't currently exploitable, but the raw string interpolation into LIMIT {$offset}, {$number} doesn't follow the project's SQL-safety convention. wpuf_get_all_transactions() (below) already uses $wpdb->prepare() with %d placeholders for the same purpose — this function should match that pattern for consistency and defense-in-depth. As per coding guidelines, "Use $wpdb->prepare() for all dynamic SQL values to prevent SQL injection."

🛡️ Proposed fix
-    $offset = absint( $args['offset'] );
-    $number = absint( $args['number'] );
-
-    $result = $wpdb->get_results( "SELECT * FROM {$wpdb->prefix}wpuf_transaction ORDER BY `{$args['orderby']}` {$args['order']} LIMIT {$offset}, {$number}", OBJECT );
+    $offset = absint( $args['offset'] );
+    $number = absint( $args['number'] );
+
+    $result = $wpdb->get_results(
+        $wpdb->prepare(
+            "SELECT * FROM {$wpdb->prefix}wpuf_transaction ORDER BY `{$args['orderby']}` {$args['order']} LIMIT %d, %d",
+            $offset,
+            $number
+        ),
+        OBJECT
+    );
📝 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
if ( ! in_array( $args['orderby'], $orderby, true ) ) {
$args['orderby'] = 'id';
}
if ( ! in_array( $args['order'], $order ) ) {
if ( ! in_array( $args['order'], $order, true ) ) {
$args['order'] = 'DESC';
}
if ( $args['count'] ) {
return $wpdb->get_var( "SELECT COUNT(*) FROM {$wpdb->prefix}wpuf_transaction" );
}
$result = $wpdb->get_results( "SELECT * FROM {$wpdb->prefix}wpuf_transaction ORDER BY `{$args['orderby']}` {$args['order']} LIMIT {$args['offset']}, {$args['number']}", OBJECT );
$offset = absint( $args['offset'] );
$number = absint( $args['number'] );
$result = $wpdb->get_results( "SELECT * FROM {$wpdb->prefix}wpuf_transaction ORDER BY `{$args['orderby']}` {$args['order']} LIMIT {$offset}, {$number}", OBJECT );
if ( ! in_array( $args['orderby'], $orderby, true ) ) {
$args['orderby'] = 'id';
}
if ( ! in_array( $args['order'], $order, true ) ) {
$args['order'] = 'DESC';
}
if ( $args['count'] ) {
return $wpdb->get_var( "SELECT COUNT(*) FROM {$wpdb->prefix}wpuf_transaction" );
}
$offset = absint( $args['offset'] );
$number = absint( $args['number'] );
$result = $wpdb->get_results(
$wpdb->prepare(
"SELECT * FROM {$wpdb->prefix}wpuf_transaction ORDER BY `{$args['orderby']}` {$args['order']} LIMIT %d, %d",
$offset,
$number
),
OBJECT
);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@wpuf-functions.php` around lines 2370 - 2385, The transaction query in
wpuf_get_all_transactions() is still interpolating the LIMIT values directly,
even though `$offset` and `$number` are sanitized. Update the
`$wpdb->get_results()` query to use `$wpdb->prepare()` with `%d` placeholders
for the LIMIT clause, matching the existing SQL-safety pattern already used
elsewhere in wpuf-functions.php. Keep the validation for `orderby` and `order`
as-is, but move the dynamic limit values into the prepared statement for
consistency and defense-in-depth.

Source: Coding guidelines


return $result;
}
Expand Down Expand Up @@ -2407,11 +2410,11 @@ function wpuf_get_pending_transactions( $args = [] ) {

$args = wp_parse_args( $args, $defaults );

if ( ! in_array( $args['orderby'], $orderby ) ) {
if ( ! in_array( $args['orderby'], $orderby, true ) ) {
$args['orderby'] = 'id';
}

if ( ! in_array( $args['order'], $order ) ) {
if ( ! in_array( $args['order'], $order, true ) ) {
$args['order'] = 'DESC';
}

Expand Down Expand Up @@ -2504,8 +2507,8 @@ function wpuf_get_all_transactions( $args = [] ) {
);
}

$orderby = in_array( $args['orderby'], $orderby_keys ) ? $args['orderby'] : 'id';
$sorting_order = in_array( $args['order'], $order_keys ) ? $args['order'] : 'DESC';
$orderby = in_array( $args['orderby'], $orderby_keys, true ) ? $args['orderby'] : 'id';
$sorting_order = in_array( $args['order'], $order_keys, true ) ? $args['order'] : 'DESC';
$offset = ( int ) sanitize_key( $args['offset'] );
$number = ( int ) sanitize_key( $args['number'] );

Expand Down Expand Up @@ -5415,7 +5418,14 @@ function wpuf_guess_username( $email ) {
function wpuf_clear_schedule_lock() {
check_ajax_referer( 'wpuf_nonce', 'nonce' );

$post_id = isset( $_POST['post_id'] ) ? intval( wp_unslash( $_POST['post_id'] ) ) : '';
// Clearing an edit-lock (potentially set by an admin) requires the ability
// to edit others' posts. This prevents any logged-in subscriber from wiping
// lock meta on arbitrary posts.
if ( ! current_user_can( 'edit_others_posts' ) ) {
wp_send_json_error( esc_html__( 'You are not allowed to clear this lock.', 'wp-user-frontend' ), 403 );
}

$post_id = isset( $_POST['post_id'] ) ? absint( wp_unslash( $_POST['post_id'] ) ) : 0;

if ( ! empty( $post_id ) ) {
update_post_meta( $post_id, '_wpuf_lock_user_editing_post_time', '' );
Expand Down
Loading