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
54 changes: 54 additions & 0 deletions src/bp-members/bp-members-functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -4099,6 +4099,24 @@ function bp_assign_default_member_type_to_activate_user( $user_id, $key, $user )
$get_selected_member_type_on_register = '';
}

/*
* Harden the self-submitted Profile Type before it is trusted below.
*
* The registration form fully controls this value, so a visitor can post
* the ID of a Profile Type that is not offered on the registration form
* (its "_bp_member_type_enable_profile_field" is off) and have it — and
* its mapped WP role — assigned at activation. Ignore it, falling back
* to the admin-configured default type, whenever it is not actually
* offered at registration. Admin-driven paths (send-invite type,
* default registration type) are unaffected.
*/
if (
'' !== $get_selected_member_type_on_register
&& ! bb_is_member_type_allowed_on_registration( $get_selected_member_type_on_register )
) {
$get_selected_member_type_on_register = '';
}

// return to user if default member type is not set.
$existing_selected = bp_member_type_default_on_registration();

Expand Down Expand Up @@ -5580,3 +5598,39 @@ function bb_remove_orphaned_profile_slug( $user_id ) {
bb_remove_orphaned_profile_slug( $user_id );
}
}

/**
* Check whether a profile type may be self-selected on the registration form.
*
* Mirrors the gate the registration Profile Type dropdown itself uses
* (see BP_XProfile_Field_Type_Member_Types::edit_field_options_html()): the
* type must be an active member-type post whose "_bp_member_type_enable_profile_field"
* meta is unset or '1'. Used to reject a Profile Type that was submitted at
* signup but is not actually offered on the registration form, closing the
* mass-assignment path where a hidden, role-mapped Profile Type could be
* self-assigned during registration.
*
* @since BuddyBoss [BBVERSION]
*
* @param int $member_type_id Member type post ID submitted at registration.
*
* @return bool True when the type is offered on the registration form.
*/
function bb_is_member_type_allowed_on_registration( $member_type_id ) {
$member_type_id = absint( $member_type_id );

if ( empty( $member_type_id ) ) {
return false;
}

// Must be one of the active member-type posts (same source the dropdown iterates).
$active_member_types = array_map( 'absint', (array) bp_get_active_member_types() );
if ( ! in_array( $member_type_id, $active_member_types, true ) ) {
return false;
}

// Must be enabled for the registration profile field (same gate as the dropdown).
$enabled = get_post_meta( $member_type_id, '_bp_member_type_enable_profile_field', true );

return ( '' === $enabled || '1' === $enabled );
}
32 changes: 32 additions & 0 deletions src/bp-members/screens/register.php
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,38 @@ function bp_core_screen_signup() {
);
}
}

/*
* Reject a Profile Type that is not actually offered on the registration
* form. The hidden signup_profile_field_ids and field_<id> values are
* attacker-controllable, so a Profile Type not shown in the registration
* dropdown could otherwise be submitted and, via its WP role mapping,
* assigned at activation. The activation-time gate in
* bp_assign_default_member_type_to_activate_user() remains as a backstop
* for any path that bypasses this validation.
*/
// phpcs:ignore WordPress.Security.NonceVerification.Missing -- Registration is a public form processed without a nonce by design; matches the surrounding field handling.
$bb_submitted_member_type = isset( $_POST[ 'field_' . $field_id ] ) ? wp_unslash( $_POST[ 'field_' . $field_id ] ) : '';
if (
(int) bp_get_xprofile_member_type_field_id() === (int) $field_id
&& ! empty( $bb_submitted_member_type )
&& ! bp_current_user_can( 'bp_moderate' )
&& (
// An array is never a valid single-select submission; reject it
// (rather than skip validation) and short-circuit absint() so it
// is not called on an array.
is_array( $bb_submitted_member_type )
|| ! bb_is_member_type_allowed_on_registration( absint( $bb_submitted_member_type ) )
)
) {
$bp->signup->errors[ 'field_' . $field_id ] = sprintf(
'<div class="bp-messages bp-feedback error">
<span class="bp-icon" aria-hidden="true"></span>
<p>%s</p>
</div>',
__( 'Please select a valid profile type.', 'buddyboss' )
);
}
}

// This situation doesn't naturally occur so bounce to website root.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -220,12 +220,26 @@ public function admin_field_html( array $raw_properties = array() ) {
/**
* Check if valid.
*
* A submitted value must reference an actual member-type post, not any
* arbitrary post ID. The previous check accepted any existing post, which
* let a crafted signup/profile payload store an unrelated post ID in this
* field. Whether that member type may be self-selected at registration is
* enforced separately at registration input (see the member-type check in
* bp_core_screen_signup(), src/bp-members/screens/register.php) and, as a
* backstop, at activation
* (see bp_assign_default_member_type_to_activate_user()), so this check
* stays context-free and does not break profile edits that keep a member's
* currently-assigned type.
*
* @since BuddyBoss 1.0.0
* @since BuddyBoss [BBVERSION] Restrict to member-type posts only.
*
* @param int $values post id.
*
* @return bool
*/
public function is_valid( $values ) {
return empty( $values ) || get_post( $values );
return empty( $values ) || ( bp_get_member_type_post_type() === get_post_type( $values ) );
}

/**
Expand Down
Loading