bug_update.php: Rework access checking so that the privilege to reopen or close a bug cannot be abused to make arbitrary updates. While I believe the approach is basically right, this patch is likely to break some customized setups. diff --git a/bug_update.php b/bug_update.php index 1f02d5d..0880570 100644 --- a/bug_update.php +++ b/bug_update.php @@ -44,15 +44,8 @@ $g_project_override = $t_bug_data->project_id; } - if ( !( - ( access_has_bug_level( access_get_status_threshold( $f_new_status, bug_get_field( $f_bug_id, 'project_id' ) ), $f_bug_id ) ) || - ( access_has_bug_level( config_get( 'update_bug_threshold' ) , $f_bug_id ) ) || - ( ( bug_get_field( $f_bug_id, 'reporter_id' ) == auth_get_current_user_id() ) && - ( ( ON == config_get( 'allow_reporter_reopen' ) ) || - ( ON == config_get( 'allow_reporter_close' ) ) ) ) - ) ) { - access_denied(); - } + # The primary access check is now done below so it can be more closely coupled + # to the actual operation being performed. # extract current extended information $t_old_bug_status = $t_bug_data->status; @@ -149,26 +142,38 @@ continue; } + # It's OK to do this before the primary access check, since we did an + # access check specifically for this custom field. if ( !custom_field_set_value( $t_id, $f_bug_id, gpc_get_custom_field( "custom_field_$t_id", $t_def['type'], NULL ) ) ) { error_parameters( lang_get_defaulted( custom_field_get_field( $t_id, 'name' ) ) ); trigger_error( ERROR_CUSTOM_FIELD_INVALID_VALUE, ERROR ); } } - $t_notify = true; - $t_bug_note_set = false; - if ( ( $t_old_bug_status != $t_bug_data->status ) && ( FALSE == $f_update_mode ) ) { + # Each of the alternatives here does a primary access check. + if ( $f_update_mode ) { + if ( ! access_has_bug_level( config_get( 'update_bug_threshold' ) , $f_bug_id ) ) { + access_denied(); + } + + $t_bug_data->update( true ); + + # Add a bugnote if there is one + bugnote_add( $f_bug_id, $f_bugnote_text, $f_time_tracking, $f_private, 0, '', NULL, FALSE ); + } else { # handle status transitions that come from pages other than bug_*update_page.php # this does the minimum to act on the bug and sends a specific message if ( $t_bug_data->status >= $t_resolved && $t_bug_data->status < $t_closed && $t_old_bug_status < $t_resolved ) { + if ( ! access_has_bug_level( access_get_status_threshold( $t_resolved, bug_get_field( $f_bug_id, 'project_id' ) ), $f_bug_id ) ) { + access_denied(); + } + # bug_resolve updates the status, fixed_in_version, resolution, handler_id and bugnote and sends message bug_resolve( $f_bug_id, $t_bug_data->resolution, $t_bug_data->fixed_in_version, $f_bugnote_text, $t_bug_data->duplicate_id, $t_bug_data->handler_id, $f_private, $f_time_tracking ); - $t_notify = false; - $t_bug_note_set = true; if ( $f_close_now ) { bug_set_field( $f_bug_id, 'status', $t_closed ); @@ -180,22 +185,17 @@ $t_bug_data->status = bug_get_field( $f_bug_id, 'status' ); } else if ( $t_bug_data->status >= $t_closed && $t_old_bug_status < $t_closed ) { + access_ensure_can_close_bug( $f_bug_id ); + # bug_close updates the status and bugnote and sends message bug_close( $f_bug_id, $f_bugnote_text, $f_private, $f_time_tracking ); - $t_notify = false; - $t_bug_note_set = true; } else if ( $t_bug_data->status == config_get( 'bug_reopen_status' ) && $t_old_bug_status >= $t_resolved ) { + access_ensure_can_reopen_bug( $f_bug_id ); + bug_set_field( $f_bug_id, 'handler_id', $t_bug_data->handler_id ); # fix: update handler_id before calling bug_reopen # bug_reopen updates the status and bugnote and sends message bug_reopen( $f_bug_id, $f_bugnote_text, $f_time_tracking, $f_private ); - $t_notify = false; - $t_bug_note_set = true; - - // update bug data with fields that may be updated inside bug_resolve(), otherwise changes will be overwritten - // in bug_update() call below. - $t_bug_data->status = bug_get_field( $f_bug_id, 'status' ); - $t_bug_data->resolution = bug_get_field( $f_bug_id, 'resolution' ); } } @@ -205,14 +205,6 @@ $t_bug_data = $t_new_bug_data; } - # Add a bugnote if there is one - if ( false == $t_bug_note_set ) { - bugnote_add( $f_bug_id, $f_bugnote_text, $f_time_tracking, $f_private, 0, '', NULL, FALSE ); - } - - # Update the bug entry, notify if we haven't done so already - $t_bug_data->update( true, ( false == $t_notify ) ); - form_security_purge( 'bug_update' ); helper_call_custom_function( 'issue_update_notify', array( $f_bug_id ) );