def set_bug_attributes(bug, regress_result, fix_result): """Set bug attributes from bisection results.""" repo_url = regress_result.repo_url or fix_result.repo_url issue_id = fix_result.issue_id or regress_result.issue_id 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 bug.issue_id = issue_id bug.project = project bug.ecosystem = ecosystem bug.summary = summary bug.details = details bug.severity = severity bug.reference_url_types = {} for reference_url in reference_urls: if OSS_FUZZ_ISSUE_URL in reference_url: link_type = 'REPORT' else: link_type = 'WEB' bug.reference_url_types[reference_url] = link_type bug.regressed = regress_result.commit or '' bug.fixed = fix_result.commit or '' bug.affected_ranges = [ osv.AffectedRange(type='GIT', repo_url=repo_url, introduced=bug.regressed, fixed=bug.fixed) ]
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()