Exemple #1
0
    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,
                                       })
Exemple #2
0
    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)
Exemple #3
0
    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,
                                       })
Exemple #4
0
    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))
Exemple #5
0
    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)