| @@ -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 | |