Esempio n. 1
0
def _get_commits(repo, regress_commit_or_range, fix_commit_or_range):
    """Get commits for analysis."""
    regress_commits = _get_commit_range(repo, regress_commit_or_range)
    if len(regress_commits) > COMMIT_RANGE_LIMIT:
        raise osv.ImpactError('Too many commits in regression range.')

    fix_commits = _get_commit_range(repo, fix_commit_or_range)
    if len(fix_commits) > COMMIT_RANGE_LIMIT:
        logging.warning('Too many commits in fix range.')
        # Rather than bail out here and potentially leaving a Bug as "unfixed"
        # indefinitely, we continue.

    return regress_commits, fix_commits
Esempio n. 2
0
def process_impact_task(source_id, message):
  """Process an impact task."""
  logging.info('Processing impact task for %s', source_id)

  regress_result = ndb.Key(osv.RegressResult, source_id).get()
  if not regress_result:
    logging.error('Missing RegressResult for %s', source_id)
    return

  fix_result = ndb.Key(osv.FixResult, source_id).get()
  if not fix_result:
    logging.warning('Missing FixResult for %s', source_id)
    fix_result = osv.FixResult()

  # Check if there is an existing Bug for the same source, but with a different
  # allocated ID. This shouldn't happen.
  allocated_bug_id = message.attributes['allocated_id']

  existing_bug = osv.Bug.query(osv.Bug.source_id == source_id).get()
  if existing_bug and existing_bug.key.id() != allocated_bug_id:
    logging.error('Bug entry already exists for %s with a different ID %s',
                  source_id, existing_bug.key.id())
    return

  if existing_bug and existing_bug.status == osv.BugStatus.INVALID:
    logging.warning('Bug %s already marked as invalid.', existing_bug.key.id())
    return

  if existing_bug:
    public = existing_bug.public
  else:
    raise osv.ImpactError('Task requested without Bug allocated.')

  # TODO(ochang): Handle changing repo types? e.g. SVN -> Git.

  repo_url = regress_result.repo_url or fix_result.repo_url
  if not repo_url:
    raise osv.ImpactError('No repo_url set')

  issue_id = fix_result.issue_id or regress_result.issue_id
  fix_commit = fix_result.commit

  with tempfile.TemporaryDirectory() as tmp_dir:
    repo = osv.clone_with_retries(repo_url, tmp_dir)

    # If not a precise fix commit, try to find the exact one by going through
    # commit messages (oss-fuzz only).
    if source_id.startswith(SOURCE_PREFIX) and ':' in fix_commit:
      start_commit, end_commit = fix_commit.split(':')
      commit = find_oss_fuzz_fix_via_commit(repo, start_commit, end_commit,
                                            source_id, issue_id)
      if commit:
        logging.info('Found exact fix commit %s via commit message (oss-fuzz)',
                     commit)
        fix_commit = commit

    # Actually compute the affected commits/tags.
    result = osv.get_affected(repo, regress_result.commit, fix_commit)
    logging.info('Found affected %s', ', '.join(result.tags))

  # If the range resolved to a single commit, simplify it.
  if len(result.fix_commits) == 1:
    fix_commit = result.fix_commits[0]
  elif not result.fix_commits:
    # Not fixed.
    fix_commit = ''

  if len(result.regress_commits) == 1:
    regress_commit = result.regress_commits[0]
  else:
    regress_commit = regress_result.commit

  project = fix_result.project or regress_result.project
  ecosystem = fix_result.ecosystem or regress_result.ecosystem
  summary = fix_result.summary or regress_result.summary
  details = fix_result.details or regress_result.details
  severity = fix_result.severity or regress_result.severity
  reference_urls = fix_result.reference_urls or regress_result.reference_urls

  update_affected_commits(allocated_bug_id, result, project, ecosystem, public)

  existing_bug.repo_url = repo_url
  existing_bug.fixed = fix_commit
  existing_bug.regressed = regress_commit
  existing_bug.affected = result.tags
  existing_bug.affected_fuzzy = osv.normalize_tags(result.tags)
  existing_bug.confidence = result.confidence
  existing_bug.issue_id = issue_id
  existing_bug.project = project
  existing_bug.ecosystem = ecosystem
  existing_bug.summary = summary
  existing_bug.details = details
  existing_bug.status = osv.BugStatus.PROCESSED
  existing_bug.severity = severity
  existing_bug.reference_urls = reference_urls

  existing_bug.additional_commit_ranges = []
  # Don't display additional ranges for imprecise commits, as they can be
  # confusing.
  if ':' in existing_bug.fixed or ':' in existing_bug.regressed:
    existing_bug.put()
    return

  def _sort_key(value):
    # Allow sorting of None values.
    return (value[0] or '', value[1] or '')

  for introduced_in, fixed_in in sorted(result.affected_ranges, key=_sort_key):
    if (introduced_in == existing_bug.regressed and
        (fixed_in or '') == existing_bug.fixed):
      # Don't include the main range.
      continue

    existing_bug.additional_commit_ranges.append(
        osv.CommitRange(introduced_in=introduced_in, fixed_in=fixed_in))

  existing_bug.put()
Esempio n. 3
0
def process_impact_task(source_id, message):
    """Process an impact task."""
    logging.info('Processing impact task for %s', source_id)

    regress_result = ndb.Key(osv.RegressResult, source_id).get()
    if not regress_result:
        logging.error('Missing RegressResult for %s', source_id)
        return

    fix_result = ndb.Key(osv.FixResult, source_id).get()
    if not fix_result:
        logging.warning('Missing FixResult for %s', source_id)
        fix_result = osv.FixResult()

    # Check if there is an existing Bug for the same source, but with a different
    # allocated ID. This shouldn't happen.
    allocated_bug_id = message.attributes['allocated_id']

    existing_bug = osv.Bug.query(osv.Bug.source_id == source_id).get()
    if existing_bug and existing_bug.key.id() != allocated_bug_id:
        logging.error('Bug entry already exists for %s with a different ID %s',
                      source_id, existing_bug.key.id())
        return

    if existing_bug and existing_bug.status == osv.BugStatus.INVALID:
        logging.warning('Bug %s already marked as invalid.',
                        existing_bug.key.id())
        return

    if existing_bug:
        public = existing_bug.public
    else:
        raise osv.ImpactError('Task requested without Bug allocated.')

    repo_url = regress_result.repo_url or fix_result.repo_url
    if not repo_url:
        raise osv.ImpactError('No repo_url set')

    # Always populate Bug attributes, even if the remainder of the analysis fails.
    # This does not mark the Bug as being valid.
    set_bug_attributes(existing_bug, regress_result, fix_result)
    existing_bug.put()

    issue_id = fix_result.issue_id or regress_result.issue_id
    fix_commit = fix_result.commit

    with tempfile.TemporaryDirectory() as tmp_dir:
        repo = osv.clone_with_retries(repo_url, tmp_dir)

        # If not a precise fix commit, try to find the exact one by going through
        # commit messages (oss-fuzz only).
        if source_id.startswith(SOURCE_PREFIX) and ':' in fix_commit:
            start_commit, end_commit = fix_commit.split(':')
            commit = find_oss_fuzz_fix_via_commit(repo, start_commit,
                                                  end_commit, source_id,
                                                  issue_id)
            if commit:
                logging.info(
                    'Found exact fix commit %s via commit message (oss-fuzz)',
                    commit)
                fix_commit = commit

        # Actually compute the affected commits/tags.
        repo_analyzer = osv.RepoAnalyzer()
        result = repo_analyzer.get_affected(repo, regress_result.commit,
                                            fix_commit)
        affected_tags = sorted(list(result.tags))
        logging.info('Found affected %s', ', '.join(affected_tags))

    # If the range resolved to a single commit, simplify it.
    if len(result.fix_commits) == 1:
        fix_commit = result.fix_commits[0]
    elif not result.fix_commits:
        # Not fixed.
        fix_commit = ''

    if (len(result.regress_commits) == 1
            and osv.UNKNOWN_COMMIT not in regress_result.commit):
        regress_commit = result.regress_commits[0]
    else:
        regress_commit = regress_result.commit

    project = fix_result.project or regress_result.project
    ecosystem = fix_result.ecosystem or regress_result.ecosystem
    osv.update_affected_commits(allocated_bug_id, result.commits, project,
                                ecosystem, public)

    affected_tags = sorted(list(result.tags))
    existing_bug.fixed = fix_commit
    existing_bug.regressed = regress_commit
    existing_bug.affected = affected_tags
    existing_bug.affected_fuzzy = osv.normalize_tags(affected_tags)
    existing_bug.status = osv.BugStatus.PROCESSED

    # For the AffectedRange, use the first commit in the regress commit range, and
    # the last commit in the fix commit range.
    introduced = result.regress_commits[0] if result.regress_commits else ''
    fixed = result.fix_commits[-1] if result.fix_commits else ''
    existing_bug.affected_ranges = [
        osv.AffectedRange(type='GIT',
                          repo_url=repo_url,
                          introduced=introduced,
                          fixed=fixed),
    ]

    # Expose range data in `database_specific`.
    database_specific = {}
    if ':' in existing_bug.regressed:
        database_specific['introduced_range'] = existing_bug.regressed
    if ':' in existing_bug.fixed:
        database_specific['fixed_range'] = existing_bug.fixed

    if database_specific:
        existing_bug.database_specific = database_specific

    # Don't display additional ranges for imprecise commits, as they can be
    # confusing.
    if ':' in existing_bug.fixed or ':' in existing_bug.regressed:
        existing_bug.put()
        return

    def _sort_key(value):
        # Allow sorting of None values.
        return (value[0] or '', value[1] or '')

    for introduced_in, fixed_in in sorted(result.affected_ranges,
                                          key=_sort_key):
        if not fixed_in:
            fixed_in = ''

        if (introduced_in == existing_bug.regressed
                and fixed_in == existing_bug.fixed):
            # Don't repeat the main range.
            continue

        existing_bug.affected_ranges.append(
            osv.AffectedRange(type='GIT',
                              repo_url=repo_url,
                              introduced=introduced_in,
                              fixed=fixed_in))

    existing_bug.put()