def get_issue_tracker_policy_for_testcase(testcase): """Get the issue tracker with the given type and name.""" issue_tracker_project_name = _get_issue_tracker_project_name(testcase) if not issue_tracker_project_name or issue_tracker_project_name == 'disabled': return None return issue_tracker_policy.get(issue_tracker_project_name)
def get(self): """Handle a cron job.""" @memoize.wrap(memoize.FifoInMemory(256)) def cc_users_for_job(job_type, security_flag): """Return users to CC for a job.""" # Memoized per cron run. return external_users.cc_users_for_job(job_type, security_flag) for testcase in get_open_testcases_with_bugs(): issue_tracker = issue_tracker_utils.get_issue_tracker_for_testcase( testcase) if not issue_tracker: logging.error('Failed to get issue tracker manager for %s', testcase.key.id()) continue policy = issue_tracker_policy.get(issue_tracker.project) reported_label = policy.label('reported') if not reported_label: return reported_pattern = issue_filer.get_label_pattern(reported_label) try: issue = issue_tracker.get_original_issue( testcase.bug_information) except: logging.error('Error occurred when fetching issue %s.', testcase.bug_information) continue if not issue or not issue.is_open: continue ccs = cc_users_for_job(testcase.job_type, testcase.security_flag) new_ccs = [cc for cc in ccs if cc not in issue.ccs] if not new_ccs: # Nothing to do. continue for cc in new_ccs: logging.info('CCing %s on %s', cc, issue.id) issue.ccs.add(cc) comment = None if (not issue.labels.has_with_pattern(reported_pattern) and not data_handler.get_value_from_job_definition( testcase.job_type, 'DISABLE_DISCLOSURE', False)): # Add reported label and deadline comment if necessary. for result in issue_filer.apply_substitutions( policy, reported_label, testcase): issue.labels.add(result) if policy.label('restrict_view') in issue.labels: logging.info('Adding deadline comment on %s', issue.id) comment = policy.deadline_policy_message issue.save(new_comment=comment, notify=True)
def update_issue(testcase, issue_id, needs_summary_update): """Associate (or update) an existing issue with the testcase.""" issue_id = helpers.cast(issue_id, int, 'Issue ID (%s) is not a number!' % issue_id) issue_tracker = helpers.get_issue_tracker_for_testcase(testcase) issue = helpers.get_or_exit( lambda: issue_tracker.get_issue(issue_id), 'Issue (id=%d) is not found!' % issue_id, 'Failed to get the issue (id=%s).' % issue_id, Exception) if not issue.is_open: raise helpers.EarlyExitException( ('The issue (%d) is already closed and further updates are not' ' allowed. Please file a new issue instead!') % issue_id, 400) if not testcase.is_crash(): raise helpers.EarlyExitException( 'This is not a crash testcase, so issue update is not applicable.', 400) issue_comment = data_handler.get_issue_description( testcase, helpers.get_user_email()) if needs_summary_update: issue.title = data_handler.get_issue_summary(testcase) policy = issue_tracker_policy.get(issue_tracker.project) properties = policy.get_existing_issue_properties() for label in properties.labels: for result in issue_filer.apply_substitutions( policy, label, testcase): issue.labels.add(result) issue.save(new_comment=issue_comment) testcase.bug_information = str(issue_id) testcase.put() data_handler.update_group_bug(testcase.group_id) helpers.log('Updated issue %sd' % issue_id, helpers.MODIFY_OPERATION)
def _check_and_update_similar_bug(testcase, issue_tracker): """Get list of similar open issues and ones that were recently closed.""" # Get similar testcases from the same group. similar_testcases_from_group = [] if testcase.group_id: group_query = data_types.Testcase.query( data_types.Testcase.group_id == testcase.group_id) similar_testcases_from_group = ndb_utils.get_all_from_query( group_query, batch_size=data_types.TESTCASE_ENTITY_QUERY_LIMIT // 2) # Get testcases with the same crash params. These might not be in the a group # if they were just fixed. same_crash_params_query = data_types.Testcase.query( data_types.Testcase.crash_type == testcase.crash_type, data_types.Testcase.crash_state == testcase.crash_state, data_types.Testcase.security_flag == testcase.security_flag, data_types.Testcase.project_name == testcase.project_name, data_types.Testcase.status == 'Processed') similar_testcases_from_query = ndb_utils.get_all_from_query( same_crash_params_query, batch_size=data_types.TESTCASE_ENTITY_QUERY_LIMIT // 2) for similar_testcase in itertools.chain(similar_testcases_from_group, similar_testcases_from_query): # Exclude ourself from comparison. if similar_testcase.key.id() == testcase.key.id(): continue # Exclude similar testcases without bug information. if not similar_testcase.bug_information: continue # Get the issue object given its ID. issue = issue_tracker.get_issue(similar_testcase.bug_information) if not issue: continue # If the reproducible issue is not verified yet, bug is still valid and # might be caused by non-availability of latest builds. In that case, # don't file a new bug yet. if similar_testcase.open and not similar_testcase.one_time_crasher_flag: return True # If the issue is still open, no need to file a duplicate bug. if issue.is_open: return True # If the issue indicates that this crash needs to be ignored, no need to # file another one. policy = issue_tracker_policy.get(issue_tracker.project) ignore_label = policy.label('ignore') if ignore_label in issue.labels: _add_triage_message(testcase, ( 'Skipping filing a bug since similar testcase ({testcase_id}) in ' 'issue ({issue_id}) is blacklisted with {ignore_label} label.' ).format(testcase_id=similar_testcase.key.id(), issue_id=issue.id, ignore_label=ignore_label)) return True # If the issue is recently closed, wait certain time period to make sure # our fixed verification has completed. if (issue.closed_time and not dates.time_has_expired( issue.closed_time, hours=data_types.MIN_ELAPSED_TIME_SINCE_FIXED)): _add_triage_message( testcase, ('Delaying filing a bug since similar testcase ' '({testcase_id}) in issue ({issue_id}) was just fixed.' ).format(testcase_id=similar_testcase.key.id(), issue_id=issue.id)) return True return False
def file_issue(testcase, issue_tracker, security_severity=None, user_email=None, additional_ccs=None): """File an issue for the given test case.""" logs.log('Filing new issue for testcase: %d' % testcase.key.id()) policy = issue_tracker_policy.get(issue_tracker.project) is_crash = not utils.sub_string_exists_in(NON_CRASH_TYPES, testcase.crash_type) properties = policy.get_new_issue_properties( is_security=testcase.security_flag, is_crash=is_crash) issue = issue_tracker.new_issue() issue.title = data_handler.get_issue_summary(testcase) issue.body = data_handler.get_issue_description(testcase, reporter=user_email, show_reporter=True) # Add reproducibility flag label. if testcase.one_time_crasher_flag: issue.labels.add(policy.label('unreproducible')) else: issue.labels.add(policy.label('reproducible')) # Chromium-specific labels. if issue_tracker.project == 'chromium' and testcase.security_flag: # Add reward labels if this is from an external fuzzer contribution. fuzzer = data_types.Fuzzer.query( data_types.Fuzzer.name == testcase.fuzzer_name).get() if fuzzer and fuzzer.external_contribution: issue.labels.add('reward-topanel') issue.labels.add('External-Fuzzer-Contribution') update_issue_impact_labels(testcase, issue) # Add additional labels from the job definition and fuzzer. additional_labels = data_handler.get_additional_values_for_variable( 'AUTOMATIC_LABELS', testcase.job_type, testcase.fuzzer_name) for label in additional_labels: issue.labels.add(label) # Add additional components from the job definition and fuzzer. automatic_components = data_handler.get_additional_values_for_variable( 'AUTOMATIC_COMPONENTS', testcase.job_type, testcase.fuzzer_name) for component in automatic_components: issue.components.add(component) # Add issue assignee from the job definition and fuzzer. automatic_assignee = data_handler.get_additional_values_for_variable( 'AUTOMATIC_ASSIGNEE', testcase.job_type, testcase.fuzzer_name) if automatic_assignee: issue.status = policy.status('assigned') issue.assignee = automatic_assignee[0] else: issue.status = properties.status # Add additional ccs from the job definition and fuzzer. ccs = data_handler.get_additional_values_for_variable( 'AUTOMATIC_CCS', testcase.job_type, testcase.fuzzer_name) # For externally contributed fuzzers, potentially cc the author. # Use fully qualified fuzzer name if one is available. fully_qualified_fuzzer_name = (testcase.overridden_fuzzer_name or testcase.fuzzer_name) ccs += external_users.cc_users_for_fuzzer(fully_qualified_fuzzer_name, testcase.security_flag) ccs += external_users.cc_users_for_job(testcase.job_type, testcase.security_flag) # Add the user as a cc if requested, and any default ccs for this job. # Check for additional ccs or labels from the job definition. if additional_ccs: ccs += [cc for cc in additional_ccs if cc not in ccs] # For user uploads, we assume the uploader is interested in the issue. if testcase.uploader_email and testcase.uploader_email not in ccs: ccs.append(testcase.uploader_email) ccs.extend(properties.ccs) # Get view restriction rules for the job. issue_restrictions = data_handler.get_value_from_job_definition( testcase.job_type, 'ISSUE_VIEW_RESTRICTIONS', 'security') should_restrict_issue = (issue_restrictions == 'all' or (issue_restrictions == 'security' and testcase.security_flag)) has_accountable_people = bool(ccs) # Check for labels with special logic. additional_labels = [] if should_restrict_issue: additional_labels.append(policy.label('restrict_view')) if has_accountable_people: additional_labels.append(policy.label('reported')) if testcase.security_flag: additional_labels.append(policy.label('security_severity')) additional_labels.append(policy.label('os')) # Apply label substitutions. for label in itertools.chain(properties.labels, additional_labels): for result in apply_substitutions(policy, label, testcase, security_severity): issue.labels.add(result) issue.body += data_handler.format_issue_information( testcase, properties.issue_body_footer) if (should_restrict_issue and has_accountable_people and policy.deadline_policy_message): issue.body += '\n\n' + policy.deadline_policy_message for cc in ccs: issue.ccs.add(cc) # Add additional labels and components from testcase metadata. metadata_labels = _get_from_metadata(testcase, 'issue_labels') for label in metadata_labels: issue.labels.add(label) metadata_components = _get_from_metadata(testcase, 'issue_components') for component in metadata_components: issue.components.add(component) issue.reporter = user_email issue.save() # Update the testcase with this newly created issue. testcase.bug_information = str(issue.id) testcase.put() data_handler.update_group_bug(testcase.group_id) return issue.id