def _push_new_ranges_and_versions(self, source_repo, repo, vulnerability, yaml_path, original_sha256, range_collectors, versions): """Pushes new ranges and versions.""" old_ranges = list(vulnerability.affects.ranges) del vulnerability.affects.ranges[:] for repo_url, range_collector in range_collectors.items(): for introduced, fixed in range_collector.ranges(): vulnerability.affects.ranges.add( type=vulnerability_pb2.AffectedRange.Type.GIT, repo=repo_url, introduced=introduced, fixed=fixed) has_changes = old_ranges != list(vulnerability.affects.ranges) for version in sorted(versions): if version not in vulnerability.affects.versions: has_changes = True vulnerability.affects.versions.append(version) if not has_changes: return True # Write updates, and push. vulnerability.modified.FromDatetime(osv.utcnow()) osv.vulnerability_to_yaml(vulnerability, yaml_path) repo.index.add_all() return osv.push_source_changes(repo, f'Update {vulnerability.id}', self._git_callbacks(source_repo), expected_hashes={ yaml_path: original_sha256, })
def _do_update(self, source_repo, repo, vulnerability, relative_path, original_sha256): """Process updates on a vulnerability.""" logging.info('Processing update for vulnerability %s', vulnerability.id) try: result = self._analyze_vulnerability(source_repo, repo, vulnerability, relative_path, original_sha256) except UpdateConflictError: # Discard changes due to conflict. return # Update datastore with new information. bug = osv.Bug.get_by_id(vulnerability.id) if not bug: if source_repo.name == 'oss-fuzz': logging.warning('%s not found for OSS-Fuzz source.', vulnerability.id) return bug = osv.Bug(db_id=vulnerability.id, source_id=f'{source_repo.name}:{relative_path}', timestamp=osv.utcnow(), status=osv.BugStatus.PROCESSED, source_of_truth=osv.SourceOfTruth.SOURCE_REPO) bug.update_from_vulnerability(vulnerability) bug.public = True bug.put() osv.update_affected_commits(bug.key.id(), result.commits, bug.project, bug.ecosystem, bug.public)
def _push_new_ranges_and_versions(self, source_repo, repo, vulnerability, yaml_path, original_sha256, range_collectors, versions): """Pushes new ranges and versions.""" has_changes = False for repo_url, range_collector in range_collectors.items(): for introduced, fixed in range_collector.ranges(): if any( # Range collectors use None, while the proto uses '' for empty # values. (affected_range.introduced or None) == introduced and ( affected_range.fixed or None) == fixed for affected_range in vulnerability.affects.ranges): # Range already exists. continue has_changes = True vulnerability.affects.ranges.add( type=vulnerability_pb2.AffectedRange.Type.GIT, repo=repo_url, introduced=introduced, fixed=fixed) for version in sorted(versions): if version not in vulnerability.affects.versions: has_changes = True vulnerability.affects.versions.append(version) if not has_changes: return True # Write updates, and push. vulnerability.modified.FromDatetime(osv.utcnow()) osv.vulnerability_to_yaml(vulnerability, yaml_path) repo.index.add_all() return osv.push_source_changes(repo, f'Update {vulnerability.id}', self._git_callbacks(source_repo), expected_hashes={ yaml_path: original_sha256, })
def _push_new_ranges_and_versions(self, source_repo, repo, vulnerability, yaml_path, added_ranges, added_versions): # Add new ranges and versions (sorted for determinism). for repo_url, introduced, fixed in sorted(added_ranges): vulnerability.affects.ranges.add( type=vulnerability_pb2.AffectedRangeNew.Type.GIT, repo=repo_url, introduced=introduced, fixed=fixed) for version in sorted(added_versions): vulnerability.affects.versions.append(version) # Write updates, and push. vulnerability.last_modified.FromDatetime(osv.utcnow()) osv.vulnerability_to_yaml(vulnerability, yaml_path) # TODO(ochang): Hash check for conflicts. repo.index.add_all() return osv.push_source_changes(repo, f'Update {vulnerability.id}', self._git_callbacks(source_repo))
def _do_update(self, source_repo, repo, vulnerability, yaml_path, relative_path, original_sha256): """Process updates on a vulnerability.""" logging.info('Processing update for vulnerability %s', vulnerability.id) package_repo_dir = tempfile.TemporaryDirectory() package_repo_url = None package_repo = None bug = osv.Bug.get_by_id(vulnerability.id) if bug: fix_result = osv.FixResult.get_by_id(bug.source_id) if fix_result: add_fix_information(vulnerability, bug, fix_result) # Repo -> Git range collectors range_collectors = collections.defaultdict(osv.RangeCollector) versions_with_bug = set() versions_with_fix = set() commits = set() try: for affected_range in vulnerability.affects.ranges: if affected_range.type != vulnerability_pb2.AffectedRange.GIT: continue # Convert empty values ('') to None. introduced = affected_range.introduced or None fixed = affected_range.fixed or None range_collectors[affected_range.repo].add(introduced, fixed) for affected_range in vulnerability.affects.ranges: # Go through existing provided ranges to find additional ranges (via # cherrypicks and branches). if affected_range.type != vulnerability_pb2.AffectedRange.GIT: continue current_repo_url = affected_range.repo if current_repo_url != package_repo_url: # Different repo from previous one. package_repo_dir.cleanup() package_repo_dir = tempfile.TemporaryDirectory() package_repo_url = current_repo_url package_repo = osv.clone_with_retries( package_repo_url, package_repo_dir.name) result = osv.get_affected(package_repo, affected_range.introduced, affected_range.fixed) for introduced, fixed in result.affected_ranges: range_collectors[current_repo_url].add(introduced, fixed) versions_with_fix.update(result.tags_with_fix) versions_with_bug.update(result.tags_with_bug) commits.update(result.commits) finally: package_repo_dir.cleanup() # Enumerate ECOSYSTEM and SEMVER ranges. versions = self._enumerate_versions(vulnerability.package.name, vulnerability.package.ecosystem, vulnerability.affects.ranges) # Add additional versions derived from tags. versions.extend(versions_with_bug - versions_with_fix) if self._push_new_ranges_and_versions(source_repo, repo, vulnerability, yaml_path, original_sha256, range_collectors, versions): logging.info('Updated range/versions for vulnerability %s.', vulnerability.id) else: logging.warning('Discarding changes for %s due to conflicts.', vulnerability.id) return # Update datastore with new information. bug = osv.Bug.get_by_id(vulnerability.id) if not bug: if source_repo.name == 'oss-fuzz': logging.warning('%s not found for OSS-Fuzz source.', vulnerability.id) return bug = osv.Bug(id=vulnerability.id, source_id=f'{source_repo.name}:{relative_path}', timestamp=osv.utcnow(), status=osv.BugStatus.PROCESSED, source_of_truth=osv.SourceOfTruth.SOURCE_REPO) bug.update_from_vulnerability(vulnerability) bug.put() osv.update_affected_commits(bug.key.id(), commits, bug.project, bug.ecosystem, bug.public)