Beispiel #1
0
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)
Beispiel #2
0
    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)
Beispiel #4
0
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