Пример #1
0
def notify_uploader_when_testcase_is_processed(policy, testcase, issue):
  """Notify uploader by email when all the testcase tasks are finished."""
  testcase_id = testcase.key.id()

  # Check if this is a user upload. If not, bail out.
  upload_metadata = data_types.TestcaseUploadMetadata.query(
      data_types.TestcaseUploadMetadata.testcase_id == testcase_id).get()
  if not upload_metadata:
    return

  # Check that we have a valid email to send the notification. If not, bail out.
  to_email = upload_metadata.uploader_email
  if not to_email:
    return

  # If this is a bundled archive with multiple testcases, then don't send email
  # for individual testcases.
  if upload_metadata.bundled:
    return

  # Check if the notification is already sent once. If yes, bail out.
  if data_handler.is_notification_sent(testcase_id, to_email):
    return

  # Make sure all testcase taks are done (e.g. minimization, regression, etc).
  if not data_handler.critical_tasks_completed(testcase):
    return

  notify = not upload_metadata.quiet_flag
  if issue:
    issue_description = data_handler.get_issue_description(testcase)
    _update_issue_when_uploaded_testcase_is_processed(
        policy,
        testcase,
        issue,
        issue_description,
        upload_metadata.bug_summary_update_flag,
        notify,
    )

  if notify:
    issue_description_without_crash_state = data_handler.get_issue_description(
        testcase, hide_crash_state=True)
    _send_email_to_uploader(testcase_id, to_email,
                            issue_description_without_crash_state)

  # Make sure to create notification entry, as we use this to update bug.
  data_handler.create_notification_entry(testcase_id, to_email)
Пример #2
0
    def test_get_issue_description_timeout(self):
        """Test get_issue_description for a timeout testcase."""
        self.mock.get().name = 'chromium'

        self.testcase.crash_type = 'Timeout'
        self.testcase.crash_stacktrace = (
            'Line1\n'
            'Command: /fuzzer -rss_limit_mb=2048 -timeout=25 -max_len=10 /testcase'
        )
        self.testcase.put()

        description = data_handler.get_issue_description(self.testcase)
        self.assertEqual(
            description,
            'Detailed report: https://test-clusterfuzz.appspot.com/'
            'testcase?key=1\n\n'
            'Fuzzer: libfuzzer_binary_name\n'
            'Fuzz target binary: binary_name\n'
            'Job Type: linux_asan_chrome\n'
            'Crash Type: Timeout (exceeds 25 secs)\n'
            'Crash Address: 0x1337\n'
            'Crash State:\n  A\n  B\n  C\n  \n'
            'Sanitizer: address (ASAN)\n\n'
            'Reproducer Testcase: '
            'https://test-clusterfuzz.appspot.com/download?testcase_id=1\n\n'
            'See help_url for instructions to reproduce this bug locally.')
Пример #3
0
    def test_get_issue_description_oom(self):
        """Test get_issue_description for an oom testcase."""
        self.mock.get().name = 'chromium'

        self.testcase.crash_type = 'Out-of-memory'
        self.testcase.crash_stacktrace = (
            'Line1\n'
            'Command: /fuzzer -rss_limit_mb=2048 -timeout=25 -max_len=10 /testcase'
        )
        self.testcase.job_type = 'windows_asan_chrome'
        self.testcase.one_time_crasher_flag = True
        self.testcase.second_crash_stacktrace = 'No crash using abc job type.'
        self.testcase.put()

        description = data_handler.get_issue_description(self.testcase)
        self.assertEqual(
            description,
            'Detailed report: https://test-clusterfuzz.appspot.com/'
            'testcase?key=1\n\n'
            'Fuzzer: libfuzzer_binary_name\n'
            'Fuzz target binary: binary_name\n'
            'Job Type: windows_asan_chrome\n'
            'Crash Type: Out-of-memory (exceeds 2048 MB)\n'
            'Crash Address: 0x1337\n'
            'Crash State:\n  A\n  B\n  C\n  \n'
            'Sanitizer: address (ASAN)\n\n'
            'Reproducer Testcase: '
            'https://test-clusterfuzz.appspot.com/download?testcase_id=1\n\n'
            'No crash using abc job type.\n\n'
            'See help_url for instructions to reproduce this bug locally.\n\n'
            '%s' % data_handler.FILE_UNREPRODUCIBLE_TESTCASE_TEXT)
Пример #4
0
def _add_issue_comment(testcase, comment):
    """Helper function to add a comment to the bug associated with a test case."""
    if not testcase.bug_information:
        return

    # Populate the full message.
    report = data_handler.get_issue_description(testcase).rstrip('\n')
    full_comment = '%s\n\n%s\n\n%s' % (comment, report, FIXED_REPORT_FOOTER)

    # Update the issue.
    issue_tracker_manager = (
        issue_tracker_utils.get_issue_tracker_manager(testcase))
    issue = issue_tracker_manager.get_issue(int(testcase.bug_information))
    issue.comment = full_comment
    issue_tracker_manager.save(issue, send_email=True)
Пример #5
0
  def test_get_issue_description_different_project(self):
    """Test get_issue_description with a differing project name."""
    self.mock.default_project_name.return_value = 'oss-fuzz'
    self.mock.get().url = 'url'

    description = data_handler.get_issue_description(self.testcase)
    self.assertEqual(
        description, 'Detailed report: https://test-clusterfuzz.appspot.com/'
        'testcase?key=1\n\n'
        'Project: project\n'
        'Fuzzer: libfuzzer_binary_name\n'
        'Fuzz target binary: binary_name\n'
        'Job Type: linux_asan_chrome\n'
        'Crash Type: Crash-type\n'
        'Crash Address: 0x1337\n'
        'Crash State:\n  A\n  B\n  C\n  \n'
        'Sanitizer: address (ASAN)\n\n'
        'Reproducer Testcase: '
        'https://test-clusterfuzz.appspot.com/download?testcase_id=1\n\n'
        'See help_url for instructions to reproduce this bug locally.')
Пример #6
0
    def test_get_issue_description_blackbox_fuzzer_testcase(self):
        """Test get_issue_description with a blackbox fuzzer testcase."""
        self.mock.default_project_name.return_value = 'oss-fuzz'
        self.mock.get().url = 'url'

        description = data_handler.get_issue_description(self.testcase_null)
        self.assertEqual(
            description,
            'Detailed report: https://test-clusterfuzz.appspot.com/'
            'testcase?key=3\n\n'
            'Project: project\n'
            'Fuzzer: fuzzer1\n'
            'Job Type: linux_asan_chrome\n'
            'Crash Type: UNKNOWN\n'
            'Crash Address: 0x1337\n'
            'Crash State:\n  NULL\n'
            'Sanitizer: address (ASAN)\n\n'
            'Reproducer Testcase: https://test-clusterfuzz.appspot.com/'
            'download?testcase_id=3\n\n'
            'See help_url for instructions to reproduce this bug locally.')
Пример #7
0
    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)
Пример #8
0
    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)
        itm = helpers.get_issue_tracker_manager(testcase)

        issue = helpers.get_or_exit(
            lambda: itm.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.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)

        # Create issue parameters.
        issue.comment = data_handler.get_issue_description(
            testcase, helpers.get_user_email())
        issue_summary = data_handler.get_issue_summary(testcase)

        # NULL states leads to unhelpful summaries, so do not update in that case.
        if needs_summary_update and testcase.crash_state != 'NULL':
            issue.summary = issue_summary

        # Add label on memory tool used.
        issue_filer.add_memory_tool_label_if_needed(issue, testcase)

        # Add view restrictions for internal job types.
        issue_filer.add_view_restrictions_if_needed(issue, testcase)

        # Don't enforce security severity label on an existing issue.

        itm.save(issue)

        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)
Пример #9
0
    def test_get_issue_description_additional_issue_fields(self):
        """Test get_issue_description with additional fields set in metadata."""
        self.mock.get().name = 'chromium'

        self.testcase.crash_type = 'Out-of-memory'
        self.testcase.crash_stacktrace = (
            'Line1\n'
            'Command: /fuzzer -rss_limit_mb=2048 -timeout=25 -max_len=10 /testcase'
        )
        self.testcase.job_type = 'windows_asan_chrome'
        self.testcase.one_time_crasher_flag = True
        self.testcase.set_metadata(
            'issue_metadata', {
                'additional_fields': {
                    'Acknowledgements': ['Alice', 'Bob', 'Eve', 'Mallory'],
                    'Answer': 42,
                }
            })
        self.testcase.put()

        description = data_handler.get_issue_description(self.testcase)
        self.assertEqual(
            description,
            'Detailed Report: https://test-clusterfuzz.appspot.com/'
            'testcase?key=1\n\n'
            'Fuzzing Engine: libFuzzer\n'
            'Fuzz Target: binary_name\n'
            'Job Type: windows_asan_chrome\n'
            'Crash Type: Out-of-memory (exceeds 2048 MB)\n'
            'Crash Address: 0x1337\n'
            'Crash State:\n  A\n  B\n  C\n  \n'
            'Sanitizer: address (ASAN)\n\n'
            'Crash Revision: https://test-clusterfuzz.appspot.com/revisions?'
            'job=windows_asan_chrome&revision=1337\n\n'
            'Reproducer Testcase: '
            'https://test-clusterfuzz.appspot.com/download?testcase_id=1\n\n'
            'See help_url for instructions to reproduce this bug locally.\n\n'
            '%s\n\n'
            'Acknowledgements: [\'Alice\', \'Bob\', \'Eve\', \'Mallory\']\n'
            'Answer: 42' % data_handler.FILE_UNREPRODUCIBLE_TESTCASE_TEXT)
Пример #10
0
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
Пример #11
0
def file_issue(testcase,
               itm,
               security_severity=None,
               user_email=None,
               additional_ccs=None):
  """File an issue for the given test case."""
  issue = Issue()
  issue.summary = data_handler.get_issue_summary(testcase)
  issue.body = data_handler.get_issue_description(
      testcase, reporter=user_email, show_reporter=True)

  # Labels applied by default across all issue trackers.
  issue.status = 'New'
  issue.add_label('ClusterFuzz')

  # Add label on memory tool used.
  add_memory_tool_label_if_needed(issue, testcase)

  # Add reproducibility flag label.
  if testcase.one_time_crasher_flag:
    issue.add_label('Unreproducible')
  else:
    issue.add_label('Reproducible')

  # Add security severity flag label.
  add_security_severity_label_if_needed(issue, testcase, security_severity)

  # 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))

  # Chromium-specific labels.
  if itm.project_name == 'chromium':
    # A different status system is used on the chromium tracker. Since we
    # have already reproduced the crash, we skip the Unconfirmed status.
    issue.status = 'Untriaged'

    # Add OS label.
    if environment.is_chromeos_job(testcase.job_type):
      # ChromeOS fuzzers run on Linux platform, so use correct OS-Chrome for
      # tracking.
      issue.add_label('OS-Chrome')
    elif testcase.platform_id:
      os_label = 'OS-%s' % ((testcase.platform_id.split(':')[0]).capitalize())
      issue.add_label(os_label)

    # Add view restrictions for internal job types.
    add_view_restrictions_if_needed(issue, testcase)

    if testcase.security_flag:
      # Apply labels specific to security bugs.
      issue.add_label('Restrict-View-SecurityTeam')
      issue.add_label('Type-Bug-Security')

      # 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.add_label('reward-topanel')
        issue.add_label('External-Fuzzer-Contribution')

      data_handler.update_issue_impact_labels(testcase, issue)
    else:
      # Apply labels for functional (non-security) bugs.
      if utils.sub_string_exists_in(NON_CRASH_TYPES, testcase.crash_type):
        # Non-crashing test cases shouldn't be assigned Pri-1.
        issue.add_label('Pri-2')
        issue.add_label('Type-Bug')
      else:
        # Default functional bug labels.
        issue.add_label('Pri-1')
        issue.add_label('Stability-Crash')
        issue.add_label('Type-Bug')

  # AOSP-specific labels.
  elif itm.project_name == 'android':
    if testcase.security_flag:
      # Security bug labels.
      issue.add_cc('*****@*****.**')
      issue.add_label('Type-Security')
      issue.add_label('Restrict-View-Commit')
    else:
      # Functional bug labels.
      issue.add_label('Type-Defect')

  # OSS-Fuzz specific labels.
  elif itm.project_name == 'oss-fuzz':
    if testcase.security_flag:
      # Security bug labels.
      issue.add_label('Type-Bug-Security')
    else:
      # Functional bug labels.
      issue.add_label('Type-Bug')

    if should_restrict_issue:
      issue.add_label('Restrict-View-Commit')

  # 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.add_label(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.add_component(component)

  # 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)

  if itm.project_name == 'oss-fuzz' and ccs:
    # Add a reported label for deadline tracking.
    issue.add_label(reported_label())

    if issue.has_label_matching('Restrict-View-Commit'):
      issue.body += '\n\n' + DEADLINE_NOTE

    issue.body += '\n\n' + FIX_NOTE
    issue.body += '\n\n' + QUESTIONS_NOTE

  for cc in ccs:
    issue.add_cc(cc)

  # Add additional labels from testcase metadata.
  metadata_labels = utils.parse_delimited(
      testcase.get_metadata('issue_labels', ''),
      delimiter=',',
      strip=True,
      remove_empty=True)
  for label in metadata_labels:
    issue.add_label(label)

  issue.itm = itm
  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