PluginProbe
wpForo Forum / 3.1.6
wpForo Forum v3.1.6
3.1.6 3.1.5 3.1.4 3.1.2 3.1.1 3.1.0 3.0.9 3.0.8 3.0.7 trunk 1.0.0 1.0.1 1.0.2 1.1.0 1.1.1 1.1.2 1.2.0 1.3.0 1.3.1 1.4.0 1.4.1 1.4.10 1.4.11 1.4.12 1.4.13 All 138 releases
← All changes | classes/Members.php +54 -6 3.1.13.1.6 View file →
@@ -710,8 +710,17 @@
710 710
711 711 //Define $user
712 712 $user = $data[ $form ];
713 713
714 + // SECURITY: Strip admin-only profile fields from form input unless
715 + // user has edit members permission. Prevents mass assignment attacks
716 + // where users manipulate status, reputation, or email confirmation.
717 + if( ! WPF()->usergroup->can( 'em' ) ) {
718 + unset( $user['custom_points'] );
719 + unset( $user['status'] );
720 + unset( $user['is_email_confirmed'] );
721 + }
722 +
714 723 //Define $userid
715 724 $userid = intval( $data[ $form ]['userid'] );
716 725
717 726 //Check profile editor permissions
@@ -892,8 +901,10 @@
892 901 $this->upload_avatar( $userid );
893 902 }
894 903
895 904 //Update Custom Fields
905 + // $custom_fields is still the raw $_POST['data']. The same values were merged into $user, where validate() has since dropped every field the current user may not edit and sanitize() has cleaned the rest, so read them back from $user: a key that is still there is one this user was allowed to submit, and its value is the sanitized one.
906 + $custom_fields = array_intersect_key( $user, $custom_fields );
896 907 if( ! empty( $custom_fields ) && ( in_array( 'full', $type ) || in_array( 'custom_fields', $type ) ) ) {
897 908 $result_fields = $this->update_custom_fields( $userid, $custom_fields, false );
898 909 }
899 910
@@ -997,8 +1008,17 @@
997 1008 $result_profile = true;
998 1009
999 1010 if( $check_permissions ) {
1000 1011 WPF()->perm->can_edit_user( $userid );
1012 +
1013 + // SECURITY: Strip admin-only fields from user input unless user has
1014 + // edit members permission. Prevents mass assignment attacks where
1015 + // users manipulate their own status, reputation, or email confirmation.
1016 + if( ! WPF()->usergroup->can( 'em' ) ) {
1017 + unset( $data['custom_points'] );
1018 + unset( $data['status'] );
1019 + unset( $data['is_email_confirmed'] );
1020 + }
1001 1021 }
1002 1022
1003 1023 $member = $this->encode( $data );
1004 1024
@@ -2290,15 +2310,43 @@
2290 2310 }
2291 2311
2292 2312 public function blog_comments( $userid, $user_email ) {
2293 2313 global $wpdb;
2294 - if( ! $userid || ! $user_email ) return 0;
2295 2314
2296 - return (int) $wpdb->get_var(
2297 - "SELECT COUNT(*) FROM " . $wpdb->comments . " WHERE `user_id` = " . intval(
2298 - $userid
2299 - ) . " OR `comment_author_email` = '" . esc_sql( $user_email ) . "'"
2300 - );
2315 + $userid = intval( $userid );
2316 +
2317 + // Match the author by WP user id and/or by email (for guest/imported comments).
2318 + // A comment made while logged in has both columns set, but COUNT(*) still counts
2319 + // that single row once, so the OR never double-counts.
2320 + $author_conditions = [];
2321 + $params = [];
2322 + if( $userid ) {
2323 + $author_conditions[] = 'c.user_id = %d';
2324 + $params[] = $userid;
2325 + }
2326 + if( $user_email && is_email( $user_email ) ) {
2327 + $author_conditions[] = 'c.comment_author_email = %s';
2328 + $params[] = $user_email;
2329 + }
2330 + if( empty( $author_conditions ) ) return 0;
2331 +
2332 + // WordPress stores every kind of comment in the same table (pingbacks, trackbacks,
2333 + // reviews, comments on pages/CPTs, etc.). Restrict to real, approved, public blog
2334 + // post comments only:
2335 + // - comment_approved = '1' -> approved only (not spam/trash/pending)
2336 + // - comment_type IN ('','comment') -> regular comments (not pingback/trackback/review)
2337 + // - p.post_type = 'post' -> blog posts, not pages/products/other CPTs
2338 + // - p.post_status = 'publish' -> public posts only
2339 + $sql = "SELECT COUNT(*)
2340 + FROM {$wpdb->comments} AS c
2341 + INNER JOIN {$wpdb->posts} AS p ON p.ID = c.comment_post_ID
2342 + WHERE ( " . implode( ' OR ', $author_conditions ) . " )
2343 + AND c.comment_approved = '1'
2344 + AND c.comment_type IN ( '', 'comment' )
2345 + AND p.post_type = 'post'
2346 + AND p.post_status = 'publish'";
2347 +
2348 + return (int) $wpdb->get_var( $wpdb->prepare( $sql, $params ) );
2301 2349 }
2302 2350
2303 2351 public function show_delete_form( $current_user, $userids ) {
2304 2352 if( empty( $current_user ) || empty( $userids ) ) return;