diff --git a/Dockerfile b/Dockerfile index 8e455f319..a8403c503 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,196 +1,249 @@ -FROM registry.access.redhat.com/ubi8/ubi:8.4 as anchore-engine-builder +ARG BASE_REGISTRY=registry.access.redhat.com +ARG BASE_IMAGE=ubi8/ubi +ARG BASE_TAG=8.5 -######## This is stage1 where anchore wheels, binary deps, and any items from the source tree get staged to /build_output ######## +#### Start first stage +#### Anchore wheels, binary dependencies, etc. are staged to /build_output for second stage +FROM ${BASE_REGISTRY}/${BASE_IMAGE}:${BASE_TAG} as anchore-engine-builder ARG CLI_COMMIT -ENV LANG=en_US.UTF-8 LC_ALL=C.UTF-8 -ENV GOPATH=/go +ENV LANG=en_US.UTF-8 +ENV LC_ALL=C.UTF-8 + +# environment variables for dependent binary versions ENV SYFT_VERSION=v0.26.0 ENV GRYPE_VERSION=v0.22.0 ENV PIP_VERSION=21.0.1 -COPY . /buildsource -WORKDIR /buildsource - +# setup build artifact directory RUN set -ex && \ - mkdir -p /build_output /build_output/deps /build_output/configs /build_output/wheels /build_output/cli_wheels + mkdir -p \ + /build_output/configs \ + /build_output/cli_wheels \ + /build_output/deps \ + /build_output/wheels +# installing build dependencies RUN set -ex && \ - echo "installing OS dependencies" && \ + echo "installing build dependencies" && \ + # keepcache is used so that subsequent invocations of yum do not remove the cached RPMs in --downloaddir echo "keepcache = 1" >> /etc/yum.conf && \ yum update -y && \ - yum module disable -y python36 && yum module enable -y python38 && \ - yum install -y gcc make python38 git python38-wheel python38-devel python38-psycopg2 go && \ - pip3 install pip=="${PIP_VERSION}" && \ - pip3 download -d /build_output/wheels pip=="${PIP_VERSION}" && \ + yum module disable -y python36 && \ + yum module enable -y python38 && \ + yum install -y \ + gcc \ + git \ + go \ + make \ + python38 \ + python38-devel \ + python38-psycopg2 \ + python38-wheel && \ yum install -y https://dl.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rpm && \ - yum install -y --downloadonly --downloaddir=/build_output/build_deps/ dpkg clamav clamav-update + pip3 install pip=="${PIP_VERSION}" -# create anchore binaries +# stage dependent binaries into /build_output RUN set -ex && \ - echo "installing anchore" && \ - pip3 wheel --wheel-dir=/build_output/wheels . && \ - pip3 wheel --wheel-dir=/build_output/cli_wheels/ git+git://github.com/anchore/anchore-cli.git@"$CLI_COMMIT"\#egg=anchorecli && \ - cp ./LICENSE /build_output/ && \ - cp ./conf/default_config.yaml /build_output/configs/default_config.yaml && \ - cp ./docker-entrypoint.sh /build_output/configs/docker-entrypoint.sh && \ - cp -R ./anchore_engine/conf/clamav /build_output/configs/ + echo "downloading OS dependencies" && \ + pip3 download -d /build_output/wheels pip=="${PIP_VERSION}" && \ + yum install -y --downloadonly --downloaddir=/build_output/deps/ \ + clamav \ + clamav-update \ + dpkg -# stage anchore dependency binaries RUN set -ex && \ - echo "installing GO" && \ - mkdir -p /go + echo "downloading anchore-cli" && \ + pip3 wheel --wheel-dir=/build_output/cli_wheels/ git+git://github.com/anchore/anchore-cli.git@"${CLI_COMMIT}"\#egg=anchorecli -RUN set -ex && \ - echo "installing Syft" && \ - curl -sSfL https://raw.githubusercontent.com/anchore/syft/main/install.sh | sh -s -- -b /build_output/deps "$SYFT_VERSION" +RUN set -exo pipefail && \ + echo "downloading Syft" && \ + curl -sSfL https://raw.githubusercontent.com/anchore/syft/main/install.sh | sh -s -- -b /build_output/deps "${SYFT_VERSION}" +RUN set -exo pipefail && \ + echo "downloading Grype" && \ + curl -sSfL https://raw.githubusercontent.com/anchore/grype/main/install.sh | sh -s -- -b /build_output/deps "${GRYPE_VERSION}" + +COPY . /buildsource +WORKDIR /buildsource + +# stage anchore-engine wheels and default application configs into /build_output RUN set -ex && \ - echo "installing Grype" && \ - curl -sSfL https://raw.githubusercontent.com/anchore/grype/main/install.sh | sh -s -- -b /build_output/deps "$GRYPE_VERSION" + echo "creating anchore-engine wheels" && \ + pip3 wheel --wheel-dir=/build_output/wheels . && \ + cp ./LICENSE /build_output/ && \ + cp ./conf/default_config.yaml /build_output/configs/default_config.yaml && \ + cp ./docker-entrypoint.sh /build_output/configs/docker-entrypoint.sh # create p1 buildblob & checksum RUN set -ex && \ tar -z -c -v -C /build_output -f /anchore-buildblob.tgz . && \ sha256sum /anchore-buildblob.tgz > /buildblob.tgz.sha256sum -# Build setup section - -FROM registry.access.redhat.com/ubi8/ubi:8.4 as anchore-engine-final - -######## This is stage2 which does setup and install entirely from items from stage1's /build_output ######## +#### Start second stage +#### Setup and install using first stage artifacts in /build_output +FROM ${BASE_REGISTRY}/${BASE_IMAGE}:${BASE_TAG} as anchore-engine-final ARG CLI_COMMIT ARG ANCHORE_COMMIT ARG ANCHORE_ENGINE_VERSION="1.0.1" ARG ANCHORE_ENGINE_RELEASE="r0" -# Copy skopeo artifacts from build step -COPY --from=anchore-engine-builder /build_output /build_output - # Container metadata section - -MAINTAINER dev@anchore.com - -LABEL anchore_cli_commit="$CLI_COMMIT" \ - anchore_commit="$ANCHORE_COMMIT" \ +LABEL anchore_cli_commit="${CLI_COMMIT}" \ + anchore_commit="${ANCHORE_COMMIT}" \ name="anchore-engine" \ maintainer="dev@anchore.com" \ vendor="Anchore Inc." \ - version="$ANCHORE_ENGINE_VERSION" \ - release="$ANCHORE_ENGINE_RELEASE" \ + version="${ANCHORE_ENGINE_VERSION}" \ + release="${ANCHORE_ENGINE_RELEASE}" \ summary="Anchore Engine - container image scanning service for policy-based security, best-practice and compliance enforcement." \ description="Anchore is an open platform for container security and compliance that allows developers, operations, and security teams to discover, analyze, and certify container images on-premises or in the cloud. Anchore Engine is the on-prem, OSS, API accessible service that allows ops and developers to perform detailed analysis, run queries, produce reports and define policies on container images that can be used in CI/CD pipelines to ensure that only containers that meet your organization’s requirements are deployed into production." # Environment variables to be present in running environment -ENV LANG=en_US.UTF-8 LC_ALL=C.UTF-8 +ENV AUTHLIB_INSECURE_TRANSPORT=true +ENV LANG=en_US.UTF-8 +ENV LC_ALL=C.UTF-8 +ENV PATH="${PATH}:/anchore-cli/bin" +ENV SET_HOSTID_TO_HOSTNAME=false # Default values overrideable at runtime of the container -ENV ANCHORE_CONFIG_DIR=/config \ - ANCHORE_SERVICE_DIR=/anchore_service \ - ANCHORE_LOG_LEVEL=INFO \ - ANCHORE_ENABLE_METRICS=false \ +ENV ANCHORE_ADMIN_EMAIL=admin@myanchore \ + ANCHORE_ADMIN_PASSWORD=null \ + ANCHORE_AUTH_ENABLE_HASHED_PASSWORDS=false \ + ANCHORE_AUTH_PRIVKEY=null \ + ANCHORE_AUTH_PUBKEY=null \ + ANCHORE_AUTH_SECRET=null \ + ANCHORE_AUTHZ_HANDLER=native \ + ANCHORE_CATALOG_NOTIFICATION_INTERVAL_SEC=30 \ + ANCHORE_CLI_PASS=foobar \ + ANCHORE_CLI_USER=admin \ + ANCHORE_CLI_URL="http://localhost:8228" \ + ANCHORE_CONFIG_DIR=/config \ + ANCHORE_DB_NAME=postgres \ + ANCHORE_DB_PORT=5432 \ + ANCHORE_DB_USER=postgres \ ANCHORE_DISABLE_METRICS_AUTH=false \ - ANCHORE_INTERNAL_SSL_VERIFY=false \ - ANCHORE_WEBHOOK_DESTINATION_URL=null \ - ANCHORE_HINTS_ENABLED=false \ - ANCHORE_FEEDS_ENABLED=true \ - ANCHORE_FEEDS_SSL_VERIFY=true \ + ANCHORE_ENABLE_METRICS=false \ + ANCHORE_ENABLE_PACKAGE_FILTERING="true" \ ANCHORE_ENDPOINT_HOSTNAME=localhost \ ANCHORE_EVENTS_NOTIFICATIONS_ENABLED=false \ - ANCHORE_CATALOG_NOTIFICATION_INTERVAL_SEC=30 \ - ANCHORE_FEED_SYNC_INTERVAL_SEC=21600 \ + ANCHORE_EXTERNAL_AUTHZ_ENDPOINT=null \ ANCHORE_EXTERNAL_PORT=null \ ANCHORE_EXTERNAL_TLS=false \ - ANCHORE_AUTHZ_HANDLER=native \ - ANCHORE_EXTERNAL_AUTHZ_ENDPOINT=null \ - ANCHORE_ADMIN_PASSWORD=null \ - ANCHORE_ADMIN_EMAIL=admin@myanchore \ - ANCHORE_HOST_ID="anchore-quickstart" \ - ANCHORE_DB_PORT=5432 \ - ANCHORE_DB_NAME=postgres \ - ANCHORE_DB_USER=postgres \ - SET_HOSTID_TO_HOSTNAME=false \ - ANCHORE_CLI_USER=admin \ - ANCHORE_CLI_PASS=foobar \ - ANCHORE_SERVICE_PORT=8228 \ - ANCHORE_CLI_URL="http://localhost:8228" \ - ANCHORE_FEEDS_URL="https://ancho.re/v1/service/feeds" \ ANCHORE_FEEDS_CLIENT_URL="https://ancho.re/v1/account/users" \ + ANCHORE_FEEDS_ENABLED=true \ + ANCHORE_FEEDS_SSL_VERIFY=true \ + ANCHORE_FEED_SYNC_INTERVAL_SEC=21600 \ ANCHORE_FEEDS_TOKEN_URL="https://ancho.re/oauth/token" \ - ANCHORE_GLOBAL_CLIENT_READ_TIMEOUT=0 \ + ANCHORE_FEEDS_URL="https://ancho.re/v1/service/feeds" \ ANCHORE_GLOBAL_CLIENT_CONNECT_TIMEOUT=0 \ - ANCHORE_AUTH_PUBKEY=null \ - ANCHORE_AUTH_PRIVKEY=null \ - ANCHORE_AUTH_SECRET=null \ + ANCHORE_GLOBAL_CLIENT_READ_TIMEOUT=0 \ + ANCHORE_GLOBAL_SERVER_REQUEST_TIMEOUT_SEC=180 \ + ANCHORE_GRYPE_DB_URL="https://toolbox-data.anchore.io/grype/databases/listing.json" \ + ANCHORE_HINTS_ENABLED=false \ + ANCHORE_HOST_ID="anchore-quickstart" \ + ANCHORE_INTERNAL_SSL_VERIFY=false \ + ANCHORE_LOG_LEVEL=INFO \ + ANCHORE_MAX_COMPRESSED_IMAGE_SIZE_MB=-1 \ ANCHORE_OAUTH_ENABLED=false \ ANCHORE_OAUTH_TOKEN_EXPIRATION=3600 \ - ANCHORE_AUTH_ENABLE_HASHED_PASSWORDS=false \ - AUTHLIB_INSECURE_TRANSPORT=true \ - ANCHORE_MAX_COMPRESSED_IMAGE_SIZE_MB=-1 \ - ANCHORE_GLOBAL_SERVER_REQUEST_TIMEOUT_SEC=180 \ + ANCHORE_SERVICE_DIR=/anchore_service \ + ANCHORE_SERVICE_PORT=8228 \ ANCHORE_VULNERABILITIES_PROVIDER=null \ - ANCHORE_GRYPE_DB_URL="https://toolbox-data.anchore.io/grype/databases/listing.json" \ - ANCHORE_ENABLE_PACKAGE_FILTERING="true" - -ENV PATH "${PATH}:/anchore-cli/bin" - -# Insecure transport required in case for things like tls sidecars - -# Container run environment settings - -#VOLUME /analysis_scratch -EXPOSE "${ANCHORE_SERVICE_PORT}" - -# Build dependencies + ANCHORE_WEBHOOK_DESTINATION_URL=null -RUN set -ex && \ - yum update -y && \ - yum module disable -y python36 && yum module enable -y python38 && \ - yum install -y python38 python38-wheel procps psmisc python38-psycopg2 skopeo && \ - pip3 install --upgrade --no-index --find-links=/build_output/wheels/ pip - -# Setup container default configs and directories - -WORKDIR /anchore-engine - -# Perform OS setup +#### Perform OS setup +# Setup container user/group and required application directories RUN set -ex && \ groupadd --gid 1000 anchore && \ useradd --uid 1000 --gid anchore --shell /bin/bash --create-home anchore && \ - mkdir /config && \ - mkdir /licenses && \ - mkdir -p /workspace_preload /var/log/anchore /var/run/anchore /analysis_scratch /workspace /anchore_service/bundles "${ANCHORE_SERVICE_DIR}"/bundles /home/anchore/clamav/db && \ - cp /build_output/LICENSE /licenses/ && \ - cp /build_output/configs/default_config.yaml /config/config.yaml && \ - cp /build_output/configs/docker-entrypoint.sh /docker-entrypoint.sh && \ - cp /build_output/configs/clamav/freshclam.conf /home/anchore/clamav/ && \ - chown -R 1000:0 /workspace_preload /var/log/anchore /var/run/anchore /analysis_scratch /workspace /anchore_service "${ANCHORE_SERVICE_DIR}" /home/anchore && \ - chmod -R g+rwX /workspace_preload /var/log/anchore /var/run/anchore /analysis_scratch /workspace /anchore_service "${ANCHORE_SERVICE_DIR}" /home/anchore && \ - chmod -R ug+rw /home/anchore/clamav && \ - md5sum /config/config.yaml > /config/build_installed && \ - chmod +x /docker-entrypoint.sh - - -# Perform any base OS specific setup + mkdir -p \ + /analysis_scratch \ + "${ANCHORE_SERVICE_DIR}"/bundles \ + /config \ + /home/anchore/clamav/db \ + /licenses \ + /var/log/anchore \ + /var/run/anchore \ + /workspace \ + /workspace_preload && \ + chown -R 1000:0 \ + /analysis_scratch \ + "${ANCHORE_SERVICE_DIR}" \ + /config \ + /home/anchore \ + /licenses \ + /var/log/anchore \ + /var/run/anchore \ + /workspace \ + /workspace_preload && \ + chmod -R g+rwX \ + /analysis_scratch \ + "${ANCHORE_SERVICE_DIR}" \ + /config \ + /home/anchore \ + /licenses \ + /var/log/anchore \ + /var/run/anchore \ + /workspace \ + /workspace_preload + +# Install build dependencies +RUN set -ex && \ + yum update -y && \ + yum module disable -y python36 && \ + yum module enable -y python38 && \ + yum install -y \ + procps \ + psmisc \ + python38 \ + python38-psycopg2 \ + python38-wheel \ + skopeo && \ + yum clean all + +# Copy the installed artifacts from the first stage +COPY --from=anchore-engine-builder /build_output /build_output -# Perform the cli install into a virtual env +# Install anchore-cli into a virtual environment RUN set -ex && \ + echo "updating pip" && \ + pip3 install --upgrade --no-index --find-links=/build_output/wheels/ pip && \ + echo "installing anchore-cli into virtual environment" && \ python3 -m venv /anchore-cli && \ source /anchore-cli/bin/activate && \ pip3 install --no-index --find-links=/build_output/cli_wheels/ anchorecli && \ deactivate -# Perform the anchore-engine build and install +# Install required OS deps & application config files +RUN set -exo pipefail && \ + cp /build_output/deps/syft /usr/bin/syft && \ + cp /build_output/deps/grype /usr/bin/grype && \ + yum install -y /build_output/deps/*.rpm && \ + yum clean all +# Install anchore-engine & cleanup filesystem RUN set -ex && \ + echo "installing anchore-engine and required dependencies" && \ pip3 install --no-index --find-links=/build_output/wheels/ anchore-engine && \ - cp /build_output/deps/syft /usr/bin/syft && \ - cp /build_output/deps/grype /usr/bin/grype && \ - yum install -y /build_output/build_deps/*.rpm && \ - rm -rf /build_output /root/.cache + echo "copying default application config files" && \ + cp /build_output/LICENSE /licenses/ && \ + cp /build_output/configs/default_config.yaml /config/config.yaml && \ + md5sum /config/config.yaml > /config/build_installed && \ + cp /build_output/configs/docker-entrypoint.sh /docker-entrypoint.sh && \ + chmod +x /docker-entrypoint.sh && \ + cp -R $(pip3 show anchore-engine | grep Location: | cut -c 11-)/anchore_engine/conf/clamav/freshclam.conf /home/anchore/clamav/ && \ + chmod -R ug+rw /home/anchore/clamav && \ + echo "cleaning up unneccesary files used for testing/cache/build" && \ + rm -rf \ + /build_output \ + /root/.cache \ + /usr/local/lib64/python3.8/site-packages/twisted/test \ + /usr/local/lib64/python3.8/site-packages/Crypto/SelfTest \ + /usr/share/doc # Container runtime instructions @@ -199,5 +252,9 @@ HEALTHCHECK --start-period=20s \ USER 1000 +EXPOSE "${ANCHORE_SERVICE_PORT}" + +WORKDIR /anchore-engine + ENTRYPOINT ["/docker-entrypoint.sh"] CMD ["anchore-manager", "service", "start", "--all"] diff --git a/Makefile b/Makefile index 7acab3dd3..f476b0ea9 100644 --- a/Makefile +++ b/Makefile @@ -196,6 +196,7 @@ push-redhat: setup-test-infra ## (Not available outside of CI) Push prod Anchore push-rebuild: setup-test-infra ## Rebuild and push prod Anchore Engine docker image to Docker Hub (not available outside of CI) @$(CI_CMD) push-prod-image-rebuild "$(COMMIT_SHA)" "$(DEV_IMAGE_REPO)" "$(GIT_TAG)" +.PHONY: ironbank-artifacts ironbank-artifacts: anchore-ci ## (Not available outside of CI) Create and upload ironbank buildblob artifacts @$(CI_CMD) create-ironbank-artifacts anchore-engine "$(GIT_TAG)" diff --git a/anchore_engine/services/policy_engine/engine/vulns/mappers.py b/anchore_engine/services/policy_engine/engine/vulns/mappers.py index ebacd0230..f2862f93f 100644 --- a/anchore_engine/services/policy_engine/engine/vulns/mappers.py +++ b/anchore_engine/services/policy_engine/engine/vulns/mappers.py @@ -15,7 +15,7 @@ Vulnerability, VulnerabilityMatch, ) -from anchore_engine.db import Image, ImageCpe, ImagePackage +from anchore_engine.db import Image from anchore_engine.services.policy_engine.engine.vulns.utils import get_api_endpoint from anchore_engine.subsys import logger as log from anchore_engine.util.cpe_generators import ( @@ -51,26 +51,6 @@ def __init__(self, engine_type, grype_type, grype_language=None): # default language to blank string self.grype_language = grype_language if grype_language else "" - def to_grype( - self, - image_package: ImagePackage, - location_cpes_dict: Dict[str, List[str]] = None, - ): - artifact = { - "id": str(uuid.uuid4()), - "name": image_package.name, - "version": image_package.fullversion, - "type": self.grype_type, - "locations": [ - { - "path": image_package.pkg_path, - } - ], - # Package type specific mappers add metadata attribute - } - - return artifact - def image_content_to_grype_sbom(self, record: Dict): """ Creates a grype sbom formatted record from a single image content API response record @@ -98,25 +78,6 @@ class KBMapper(PackageMapper): def __init__(self): super(KBMapper, self).__init__(engine_type="kb", grype_type="msrc-kb") - def to_grype( - self, - image_package: ImagePackage, - location_cpes_dict: Dict[str, List[str]] = None, - ): - """ - Convert this image package to a sbom artifact suitable for Grype input. - The name is the Microsoft Product ID, which is the Windows OS version. - The version is the KB (Knowledge Base) ID, which is the identifier for the patch. - """ - artifact = { - "id": str(uuid.uuid4()), - "name": image_package.src_pkg, - "version": image_package.version, - "type": self.grype_type, - "locations": [{"path": image_package.pkg_path}], - } - return artifact - def image_content_to_grype_sbom(self, record: Dict): artifact = { "id": str(uuid.uuid4()), @@ -142,39 +103,6 @@ class RpmMapper(LinuxDistroPackageMapper): def __init__(self): super(RpmMapper, self).__init__(engine_type="rpm", grype_type="rpm") - def to_grype( - self, - image_package: ImagePackage, - location_cpes_dict: Dict[str, List[str]] = None, - ): - """ - Adds the source package information to grype sbom - - Source package has already been through 2 transformations before this point - 1. From syft sbom to analyzer manifest in anchore_engine/analyzers/syft/handlers/rpm.py - 2. From analyzer manifest to policy-engine ImagePackage in anchore_engine/services/policy_engine/engine/loaders.py - - After the above transformations ImagePackage.src_pkg is the equivalent of syft/grype sourceRpm - - """ - artifact = super().to_grype(image_package, location_cpes_dict) - if image_package.normalized_src_pkg != "N/A": - artifact["metadataType"] = "RpmdbMetadata" - artifact["metadata"] = {"sourceRpm": image_package.src_pkg} - - # This epoch handling is necessary because in RPMs the epoch of the binary package is often not part of the - # sourceRpm name, but Grype needs it to do the version comparison correctly. - # Without this step Grype will use the wrong version string for the vulnerability match - epoch_str, version, release = split_fullversion(image_package.fullversion) - if epoch_str: - try: - artifact["epoch"] = int(epoch_str) - except ValueError: - # not a valid epoch, something went wrong - pass - - return artifact - def image_content_to_grype_sbom(self, record: Dict): """ Adds the source package information to grype sbom @@ -221,48 +149,6 @@ class DpkgMapper(LinuxDistroPackageMapper): def __init__(self): super(DpkgMapper, self).__init__(engine_type="dpkg", grype_type="deb") - def to_grype( - self, - image_package: ImagePackage, - location_cpes_dict: Dict[str, List[str]] = None, - ): - """ - Adds the source package information to grype sbom - - Source package has already been through 2 transformations before this point. These transformations make the final result more error prone unlike rpm - 1. From syft sbom to analyzer manifest in anchore_engine/analyzers/syft/handlers/debian.py. combines source package and source/package version into one value - 2. From analyzer manifest to policy-engine ImagePackage in anchore_engine/services/policy_engine/engine/loaders.py. attempts to split the source package and version - - After the above transformations ImagePackage.normalized_src_pkg is the closest approximation to syft/grype source. - Closest because the corner cases are not handled correctly by the above transformations - - Example of not-working case - Syft output of bsdutils package - { - "name": "bsdutils", - "version": "1:2.20.1-5.3", - "type": "deb", - "foundBy": "dpkgdb-cataloger", - "metadataType": "DpkgMetadata", - "metadata": { - "package": "bsdutils", - "source": "util-linux", - "version": "1:2.20.1-5.3", - "sourceVersion": "2.20.1-5.3", - ... - - Notice version and sourceVersion are different because of the epoch - - Step 1 processes this into sourcepkg=util-linux-2.20.1-5.3 - - Step 2 falters in it's processing because of the epoch difference and saves util-linux-2.20.1-5.3 to both - ImagePackage.src_pkg and ImagePackage.normalized_src_pkg. The correct value for normalized_src_pkg is util-linux - - """ - artifact = super().to_grype(image_package, location_cpes_dict) - if image_package.normalized_src_pkg != "N/A": - artifact["metadataType"] = "DpkgMetadata" - artifact["metadata"] = {"source": image_package.normalized_src_pkg} - return artifact - def image_content_to_grype_sbom(self, record: Dict): """ Adds the source package information to grype sbom @@ -284,21 +170,6 @@ class ApkgMapper(LinuxDistroPackageMapper): def __init__(self): super(ApkgMapper, self).__init__(engine_type="APKG", grype_type="apk") - def to_grype( - self, - image_package: ImagePackage, - location_cpes_dict: Dict[str, List[str]] = None, - ): - artifact = super().to_grype(image_package, location_cpes_dict) - - # pkgdb/ prefix is added to all os package locations, it's the only way to associate a package with it's cpes - cpes = location_cpes_dict.get(f"pkgdb/{image_package.name}") - if cpes: - # populate cpes for os packages - artifact["cpes"] = [cpe.get_cpe23_fs_for_sbom() for cpe in cpes] - - return artifact - def image_content_to_grype_sbom(self, record: Dict): """ Adds the origin package information to grype sbom @@ -316,31 +187,6 @@ def image_content_to_grype_sbom(self, record: Dict): class CPEMapper(PackageMapper): - def to_grype( - self, - image_package: ImagePackage, - location_cpes_dict: Dict[str, List[ImageCpe]] = None, - ): - cpes = location_cpes_dict.get(image_package.pkg_path) - if cpes: - artifact = { - "id": str(uuid.uuid4()), - "name": image_package.name, - "type": self.grype_type, - "language": self.grype_language, - "locations": [ - { - "path": image_package.pkg_path, - } - ], - "cpes": [cpe.get_cpe23_fs_for_sbom() for cpe in cpes], - "version": cpes[0].version, # set the version explicitly for grype - } - - return artifact - else: - raise ValueError("No CPEs found for package={}".format(image_package.name)) - def fallback_cpe_generator(self, record: Dict) -> List[str]: return generate_fuzzy_cpes( record.get("package"), record.get("version"), self.engine_type diff --git a/scripts/ci/build b/scripts/ci/build index 00cd4510a..be10ceb92 100755 --- a/scripts/ci/build +++ b/scripts/ci/build @@ -20,10 +20,12 @@ fi print_colorized INFO "Building Anchore Engine image ${TEST_IMAGE_NAME}; installing anchore-cli from git@${anchore_cli_commit}."; echo -# A targeted build can be used to populate the cache in circleci if you're using the docker_layer_caching option in the job. -# Since we're not using docker_layer_caching, this build step can be ignored. -# docker build --target anchore-engine-builder -t anchore-engine:builder -f ./Dockerfile . +if [[ ${CI:-false} == "true" ]]; then + BUILD_CACHE_ARG="--no-cache" +else + BUILD_CACHE_ARG="" +fi -docker build --build-arg ANCHORE_COMMIT="${COMMIT_SHA}" --build-arg CLI_COMMIT="${anchore_cli_commit}" -t "${TEST_IMAGE_NAME}" -f ./Dockerfile . +docker build ${BUILD_CACHE_ARG} --build-arg ANCHORE_COMMIT="${COMMIT_SHA}" --build-arg CLI_COMMIT="${anchore_cli_commit}" -t "${TEST_IMAGE_NAME}" -f ./Dockerfile . print_colorized INFO "Built Anchore Engine image ${TEST_IMAGE_NAME}."; echo diff --git a/tests/unit/anchore_engine/common/test_helpers.py b/tests/unit/anchore_engine/common/test_helpers.py index df45e71b2..60b54914c 100644 --- a/tests/unit/anchore_engine/common/test_helpers.py +++ b/tests/unit/anchore_engine/common/test_helpers.py @@ -45,3 +45,164 @@ def test_valid_data(self): assert len(extracted_content[key]["licenses"]) == 1 assert extracted_content[key]["licenses"][0] == "MIT" assert len(extracted_content[key]["cpes"]) > 0 + + +class TestMakeResponseError: + class TestException(Exception): + def __init__(self, msg, anchore_error_json=None): + super().__init__(msg) + if anchore_error_json is not None: + self.anchore_error_json = anchore_error_json + + params = [ + pytest.param( + { + "errmsg": "basic-test-case", + "in_httpcode": None, + "details": None, + "expected": { + "message": "basic-test-case", + "httpcode": 500, + "detail": {"error_codes": []}, + }, + }, + id="basic", + ), + pytest.param( + { + "errmsg": "basic-test-case", + "in_httpcode": 400, + "details": None, + "expected": { + "message": "basic-test-case", + "httpcode": 400, + "detail": {"error_codes": []}, + }, + }, + id="basic-with-httpcode", + ), + pytest.param( + { + "errmsg": "basic-test-case", + "in_httpcode": None, + "details": {"test": "value"}, + "expected": { + "message": "basic-test-case", + "httpcode": 500, + "detail": { + "test": "value", + "error_codes": [], + }, + }, + }, + id="basic-with-details", + ), + pytest.param( + { + "errmsg": "basic-test-case", + "in_httpcode": None, + "details": {"error_codes": [500, 404]}, + "expected": { + "message": "basic-test-case", + "httpcode": 500, + "detail": {"error_codes": [500, 404]}, + }, + }, + id="basic-with-error-codes", + ), + pytest.param( + { + "errmsg": Exception("thisisatest"), + "in_httpcode": None, + "details": None, + "expected": { + "message": "thisisatest", + "httpcode": 500, + "detail": {"error_codes": []}, + }, + }, + id="basic-exception", + ), + pytest.param( + { + "errmsg": TestException( + "testexception", + anchore_error_json={ + "message": "test", + "httpcode": 500, + "detail": {"error_codes": [404]}, + }, + ), + "in_httpcode": 400, + "details": None, + "expected": { + "message": "test", + "httpcode": 500, + "detail": {"error_codes": [404]}, + }, + }, + id="basic-exception-with-anchore-error-json", + ), + pytest.param( + { + "errmsg": TestException( + "testexception", + anchore_error_json={ + "message": "test", + "httpcode": 500, + "detail": {"error_codes": [404]}, + "error_code": 401, + }, + ), + "in_httpcode": 400, + "details": None, + "expected": { + "message": "test", + "httpcode": 500, + "detail": {"error_codes": [404, 401]}, + }, + }, + id="basic-exception-with-anchore-error-json-and-error-code", + ), + pytest.param( + { + "errmsg": TestException( + "testexception", + anchore_error_json='{"message": "test", "httpcode": 500, "detail": {"error_codes": [404]}}', + ), + "in_httpcode": 400, + "details": None, + "expected": { + "message": "test", + "httpcode": 500, + "detail": {"error_codes": [404]}, + }, + }, + id="basic-exception-with-json-string", + ), + pytest.param( + { + "errmsg": TestException( + "testexception", + anchore_error_json='{"message" "test", "httpcode": 500, "detail": {"error_codes": [404]}}', + ), + "in_httpcode": 400, + "details": None, + "expected": { + "message": "testexception", + "httpcode": 400, + "detail": {"error_codes": []}, + }, + }, + id="basic-exception-with-bad-json-string", + ), + ] + + @pytest.mark.parametrize("param", params) + def test_make_response_error(self, param): + actual = helpers.make_response_error( + param["errmsg"], param["in_httpcode"], param["details"] + ) + assert actual["message"] == param["expected"]["message"] + assert actual["httpcode"] == param["expected"]["httpcode"] + assert actual["detail"] == param["expected"]["detail"]