Esempio n. 1
0
def _CreateFilterFromImagesDescribeArgs(image, args):
    r"""Parses `docker images describe` arguments into a filter to send to containeranalysis API.

  The returned filter will combine the user-provided filter specified by
  the --metadata-filter flag and occurrence kind filters specified by flags
  such as --show-package-vulnerability.

  Returns None if there is no information to fetch from containeranalysis API.

  Args:
    image: the fully-qualified path of a docker image.
    args: user provided command line arguments.

  Returns:
    A filter string to send to the containeranalysis API.

  For example, given a user input:
  gcloud docker images describe \
    us-west1-docker.pkg.dev/my-project/my-repo/ubuntu@sha256:abc \
    --show-package-vulnerability \
    --show-image-basis \
    --metadata-filter='createTime>"2019-04-10T"'

  this method will create a filter:

  '''
  ((kind="VULNERABILITY") OR (kind="IMAGE")) AND
  (createTime>"2019-04-10T") AND
  (resourceUrl=us-west1-docker.pkg.dev/my-project/my-repo/ubuntu@sha256:abc'))
  '''
  """

    occ_filter = filter_util.ContainerAnalysisFilter()
    filter_kinds = []
    # We don't need to filter on kinds when showing all metadata
    if not args.show_all_metadata:
        if args.show_build_details:
            filter_kinds.append('BUILD')
        if args.show_package_vulnerability:
            filter_kinds.append('VULNERABILITY')
            filter_kinds.append('DISCOVERY')
        if args.show_image_basis:
            filter_kinds.append('IMAGE')
        if args.show_deployment:
            filter_kinds.append('DEPLOYMENT')
        if args.show_provenance:
            filter_kinds.append('DSSE_ATTESTATION')
            filter_kinds.append('BUILD')

        # args include none of the occurrence types, there's no need to call the
        # containeranalysis API.
        if not filter_kinds:
            return None

    occ_filter.WithKinds(filter_kinds)
    occ_filter.WithCustomFilter(args.metadata_filter)
    occ_filter.WithResources([image])
    return occ_filter.GetFilter()
Esempio n. 2
0
    def Run(self, args):
        """This is what gets called when the user runs this command.

    Args:
      args: an argparse namespace. All the arguments that were provided to this
        command invocation.

    Raises:
      ArgumentError: If the user provided the flag --show-occurrences-from but
        --show-occurrences=False.
      InvalidImageNameError: If the user specified an invalid image name.
    Returns:
      Some value that we want to have printed later.
    """
        # Verify that --show-occurrences-from is set iff --show-occurrences=True.
        if args.IsSpecified(
                'show_occurrences_from') and not args.show_occurrences:
            raise ArgumentError(
                '--show-occurrences-from may only be set if --show-occurrences=True'
            )

        repository = util.ValidateRepositoryPath(args.image_name)
        http_obj = http.Http()
        with util.WrapExpectedDockerlessErrors(repository):
            with docker_image.FromRegistry(
                    basic_creds=util.CredentialProvider(),
                    name=repository,
                    transport=http_obj) as image:
                manifests = image.manifests()
                # Only consider the top _DEFAULT_SHOW_OCCURRENCES_FROM images
                # to reduce computation time.
                most_recent_resource_urls = None
                occ_filter = filter_util.ContainerAnalysisFilter()
                occ_filter.WithCustomFilter(args.occurrence_filter)
                occ_filter.WithResourcePrefix('https://{}'.format(repository))
                if args.show_occurrences_from:
                    # This block is skipped when the user provided
                    # --show-occurrences-from=unlimited on the CLI.
                    most_recent_resource_urls = [
                        'https://%s@%s' % (args.image_name, k)
                        for k in heapq.nlargest(
                            args.show_occurrences_from,
                            manifests,
                            key=lambda k: manifests[k]['timeCreatedMs'])
                    ]
                    occ_filter.WithResources(most_recent_resource_urls)
                return util.TransformManifests(
                    manifests,
                    repository,
                    show_occurrences=args.show_occurrences,
                    occurrence_filter=occ_filter)
Esempio n. 3
0
def TransformManifests(
    manifests,
    repository,
    show_occurrences=False,
    occurrence_filter=filter_util.ContainerAnalysisFilter()):
    """Transforms the manifests returned from the server."""
    if not manifests:
        return []

    # Map from resource url to the occurrence.
    occurrences = {}
    if show_occurrences:
        occurrences = FetchOccurrences(repository,
                                       occurrence_filter=occurrence_filter)

    # Attach each occurrence to the resource to which it applies.
    results = []
    for k, v in six.iteritems(manifests):
        result = {
            'digest': k,
            'tags': v.get('tag', []),
            'timestamp': _TimeCreatedToDateTime(v.get('timeCreatedMs'))
        }

        # Partition the (non-PACKAGE_VULNERABILITY) occurrences into different
        # columns by kind.
        for occ in occurrences.get(_ResourceUrl(repository, k), []):
            if occ.kind not in result:
                result[occ.kind] = []
            result[occ.kind].append(occ)

        if show_occurrences and occurrence_filter.resources:
            result['vuln_counts'] = {}
            # If this manifest is in the list of resource urls for which to show
            # summaries, query the API for the summary.
            resource_url = _ResourceUrl(repository, k)
            if resource_url not in occurrence_filter.resources:
                continue

            summary = FetchSummary(repository, resource_url)
            for severity_count in summary.counts:
                if severity_count.severity:
                    result['vuln_counts'][str(
                        severity_count.severity)] = (severity_count.totalCount)

        results.append(result)
    return results
Esempio n. 4
0
def _CreateFilterForImages(prefix, custom_filter, images):
    """Creates a list of filters from a docker image prefix, a custom filter and fully-qualified image URLs.

  Args:
    prefix: an URL prefix. Only metadata of images with this prefix will be
      retrieved.
    custom_filter: user provided filter string.
    images: fully-qualified docker image URLs. Only metadata of these images
      will be retrieved.

  Returns:
    A filter string to send to the containeranalysis API.
  """
    occ_filter = filter_util.ContainerAnalysisFilter()
    occ_filter.WithResourcePrefix(prefix)
    occ_filter.WithResources(images)
    occ_filter.WithCustomFilter(custom_filter)
    return occ_filter.GetChunkifiedFilters()
Esempio n. 5
0
def GetContainerAnalysisMetadata(docker_version, args):
    """Retrieves metadata for a docker image."""
    metadata = ContainerAnalysisMetadata()
    docker_url = 'https://{}'.format(docker_version.GetDockerString())
    occ_filter = _CreateFilterFromImagesDescribeArgs(docker_url, args)
    if occ_filter is None:
        return metadata
    occurrences = ca_requests.ListOccurrences(docker_version.project,
                                              occ_filter)
    include_build = args.show_build_details or args.show_all_metadata
    for occ in occurrences:
        metadata.AddOccurrence(occ, include_build)

    if metadata.vulnerability.vulnerabilities:
        vuln_summary = ca_requests.GetVulnerabilitySummary(
            docker_version.project,
            filter_util.ContainerAnalysisFilter().WithResources(
                [docker_url]).GetFilter())
        metadata.vulnerability.AddSummary(vuln_summary)
    return metadata
Esempio n. 6
0
def TransformContainerAnalysisData(
    image_name, occurrence_filter=filter_util.ContainerAnalysisFilter()):
    """Transforms the occurrence data from Container Analysis API."""
    analysis_obj = container_analysis_data_util.ContainerAndAnalysisData(
        image_name)
    project_id = RecoverProjectId(image_name)
    occs = requests.ListOccurrences(project_id, occurrence_filter.GetFilter())
    for occ in occs:
        analysis_obj.add_record(occ)

    if 'DEPLOYMENT' in occurrence_filter.kinds:
        dep_filter = occurrence_filter.WithKinds(['DEPLOYMENT'
                                                  ]).WithResources([])
        dep_occs = requests.ListOccurrences(project_id, dep_filter.GetFilter())
        image_string = six.text_type(image_name)
        for occ in dep_occs:
            if not occ.deployment:
                continue
            if image_string in occ.deployment.resourceUri:
                analysis_obj.add_record(occ)

    analysis_obj.resolveSummaries()
    return analysis_obj
Esempio n. 7
0
def GetContainerAnalysisMetadataForImages(repo_or_image, occurrence_filter,
                                          images):
    """Retrieves metadata for all images with a given path prefix."""
    metadata = collections.defaultdict(ContainerAnalysisMetadata)
    prefix = 'https://{}'.format(repo_or_image.GetDockerString())
    occ_filters = _CreateFilterForImages(prefix, occurrence_filter, images)
    occurrences = ca_requests.ListOccurrencesWithFilters(
        repo_or_image.project, occ_filters)
    for occ in occurrences:
        metadata.setdefault(occ.resourceUri,
                            ContainerAnalysisMetadata()).AddOccurrence(occ)

    summary_filters = filter_util.ContainerAnalysisFilter().WithResourcePrefix(
        prefix).WithResources(images).GetChunkifiedFilters()
    summaries = ca_requests.GetVulnerabilitySummaryWithFilters(
        repo_or_image.project, summary_filters)
    for summary in summaries:
        for count in summary.counts:
            metadata.setdefault(
                count.resourceUri,
                ContainerAnalysisMetadata()).vulnerability.AddCount(count)

    return metadata
Esempio n. 8
0
    def Run(self, args):
        """This is what gets called when the user runs this command.

    Args:
      args: an argparse namespace. All the arguments that were provided to this
        command invocation.

    Raises:
      InvalidImageNameError: If the user specified an invalid image name.
    Returns:
      Some value that we want to have printed later.
    """

        filter_kinds = []
        if args.show_build_details:
            filter_kinds.append('BUILD')
        if args.show_package_vulnerability:
            filter_kinds.append('VULNERABILITY')
            filter_kinds.append('DISCOVERY')
        if args.show_image_basis:
            filter_kinds.append('IMAGE')
        if args.show_deployment:
            filter_kinds.append('DEPLOYMENT')

        if args.show_all_metadata:
            filter_kinds = _DEFAULT_KINDS

        if filter_kinds or args.metadata_filter:
            f = filter_util.ContainerAnalysisFilter()
            f.WithKinds(filter_kinds)
            f.WithCustomFilter(args.metadata_filter)
            f.WithResources(['https://{}'.format(args.image_name)])

            with util.WrapExpectedDockerlessErrors(args.image_name):
                img_name = util.GetDigestFromName(args.image_name)
                data = util.TransformContainerAnalysisData(img_name, f)
                # Clear out fields that weren't asked for and have no data.
                if (not data.build_details_summary.build_details
                        and not args.show_build_details
                        and not args.show_all_metadata):
                    del data.build_details_summary
                if (not data.package_vulnerability_summary.vulnerabilities
                        and not args.show_package_vulnerability
                        and not args.show_all_metadata):
                    del data.package_vulnerability_summary
                if (not data.discovery_summary.discovery
                        and not args.show_package_vulnerability
                        and not args.show_all_metadata):
                    del data.discovery_summary
                if (not data.image_basis_summary.base_images
                        and not args.show_image_basis
                        and not args.show_all_metadata):
                    del data.image_basis_summary
                if (not data.deployment_summary.deployments
                        and not args.show_deployment
                        and not args.show_all_metadata):
                    del data.deployment_summary
                return data
        else:
            with util.WrapExpectedDockerlessErrors(args.image_name):
                img_name = util.GetDigestFromName(args.image_name)
                return container_data_util.ContainerData(
                    registry=img_name.registry,
                    repository=img_name.repository,
                    digest=img_name.digest)