From 2dbdf75df3bac07012fd997afda3d05059b87aab Mon Sep 17 00:00:00 2001 From: Maxim Konovalenko Date: Wed, 18 Feb 2026 10:38:12 +0300 Subject: [PATCH 1/2] new cve scan Signed-off-by: Maxim Konovalenko --- templates/CVE_Scan.gitlab-ci.yml | 357 ++++++------------------------- 1 file changed, 68 insertions(+), 289 deletions(-) diff --git a/templates/CVE_Scan.gitlab-ci.yml b/templates/CVE_Scan.gitlab-ci.yml index c5f0070..0da0ff9 100644 --- a/templates/CVE_Scan.gitlab-ci.yml +++ b/templates/CVE_Scan.gitlab-ci.yml @@ -1,301 +1,80 @@ -# variables: -# $TAG - module image tag -# $MODULE_NAME - module name -# $DD_URL - URL to defectDojo -# $DD_TOKEN - token of defectDojo to upload reports -# $PROD_REGISTRY - must be deckhouse prod read registry, used to get trivy databases and release images -# $PROD_REGISTRY_USER - username to log in to deckhouse prod read registry -# $PROD_REGISTRY_PASSWORD - password to log in to deckhouse prod read registry -# $DEV_REGISTRY - must be deckhouse dev registry, used to get dev images -# $DEV_REGISTRY_USER - username to log in to deckhouse dev registry -# $DEV_REGISTRY_PASSWORD - password to log in to deckhouse dev registry -# $TRIVY_REPORTS_LOG_OUTPUT - 0 - no output, 1 - only CVE, 2 - CVE & License. Output Trivy reports into CI job log, default - 2 -# $SCAN_SEVERAL_LASTEST_RELEASES - true/false. Whether to scan last several releases or not. For scheduled pipelines override will not work as value is always true. -# $LATEST_RELEASES_AMOUNT - Number of latest releases to scan. Default is: 3 -# $SEVERITY - Vulnerabilities severity to scan. Default is: UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL -# $MODULE_PROD_REGISTRY_CUSTOM_PATH - Module custom path in prod registry. Example: flant/modules -# $MODULE_DEV_REGISTRY_CUSTOM_PATH - Module custom path in dev registry. Example: flant/modules .cve_scan: variables: + PROD_REGISTRY: "" + PROD_REGISTRY_USER: "" + PROD_REGISTRY_PASSWORD: "" + DEV_REGISTRY: "" + DEV_REGISTRY_USER: "" + DEV_REGISTRY_PASSWORD: "" + DD_URL: "" + DD_TOKEN: "" + SOURCE_TAG: "" + CASE: "" + EXTERNAL_MODULE_NAME: "" + RELEASE_IN_DEV: "false" + SCAN_USERS: "false" + SCAN_SEVERAL_LATEST_RELEASES: "false" + LATEST_RELEASES_AMOUNT: "3" + MODULE_PROD_REGISTRY_CUSTOM_PATH: "deckhouse/fe/modules" + MODULE_DEV_REGISTRY_CUSTOM_PATH: "sys/deckhouse-oss/modules" + DIGEST_FROM_WERF: "images_tags_werf" + TRIVY_REPORTS_LOG_OUTPUT: "2" + WORKDIR: "cve-scan" + CVE_TEST_REPO_GIT: "" + CVE_SSH_PRIVATE_KEY: "" + CODEOWNERS_REPO_TOKEN: "" + DECKHOUSE_PRIVATE_REPO: "" + CONFIGMAP_PROJECT_ID: "4352" TRIVY_BIN_VERSION: "v0.67.2" TRIVY_REPO_ID: "2181" TRIVY_DB_URL: "${PROD_REGISTRY}/deckhouse/ee/security/trivy-db:2" TRIVY_JAVA_DB_URL: "${PROD_REGISTRY}/deckhouse/ee/security/trivy-java-db:1" TRIVY_POLICY_URL: "${PROD_REGISTRY}/deckhouse/ee/security/trivy-bdu:1" - IMAGES_DIGESTS_PATH: "/images_digests.json" - TRIVY_REPORTS_LOG_OUTPUT: "2" - script: - # Creating workdir - - | - echo "๐Ÿ—๏ธ Creating workdir" - workdir="trivy_scan" - # remove workdir in case it was not removed on previous run - rm -rf "${workdir}" - mkdir "${workdir}" - echo - echo "=======================================================" - echo - # Preparing DOCKER_CONFIG and login to registries - - | - echo "๐Ÿ” Preparing DOCKER_CONFIG and login to registries" - mkdir -p "${workdir}/docker" - export DOCKER_CONFIG="${workdir}/docker" - echo "๐Ÿ”‘ Logging to ${PROD_REGISTRY}" - echo ${PROD_REGISTRY_PASSWORD} | docker login --username="${PROD_REGISTRY_USER}" --password-stdin ${PROD_REGISTRY} - echo "๐Ÿ”‘ Logging to ${DEV_REGISTRY}" - echo ${DEV_REGISTRY_PASSWORD} | docker login --username="${DEV_REGISTRY_USER}" --password-stdin ${DEV_REGISTRY} - echo - echo "=======================================================" - echo - # Get Trivy - - | - echo "๐Ÿ“ฅ Get Trivy" - echo "๐Ÿ”– Trivy version: ${TRIVY_BIN_VERSION}" - mkdir -p "${workdir}/bin/trivy-${TRIVY_BIN_VERSION}" - curl -L -s --fail-with-body ${CI_API_V4_URL}/projects/${TRIVY_REPO_ID}/packages/generic/trivy-${TRIVY_BIN_VERSION}/${TRIVY_BIN_VERSION}/trivy -o ${workdir}/bin/trivy-${TRIVY_BIN_VERSION}/trivy - chmod u+x ${workdir}/bin/trivy-${TRIVY_BIN_VERSION}/trivy - ln -s ${PWD}/${workdir}/bin/trivy-${TRIVY_BIN_VERSION}/trivy ${workdir}/bin/trivy - echo "๐Ÿ”„ Updating Trivy Data Bases" - mkdir -p "${workdir}/bin/trivy_cache" - ${workdir}/bin/trivy image --username "${PROD_REGISTRY_USER}" --password "${PROD_REGISTRY_PASSWORD}" --download-db-only --db-repository "${TRIVY_DB_URL}" --cache-dir "${workdir}/bin/trivy_cache" - ${workdir}/bin/trivy image --username "${PROD_REGISTRY_USER}" --password "${PROD_REGISTRY_PASSWORD}" --download-java-db-only --java-db-repository "${TRIVY_JAVA_DB_URL}" --cache-dir "${workdir}/bin/trivy_cache" - echo - echo "=======================================================" - echo - - # Run Trivy scan + before_script: + - mkdir -p ~/.ssh + - chmod 700 ~/.ssh + - echo "$CVE_SSH_PRIVATE_KEY" > ~/.ssh/id_rsa + - chmod 600 ~/.ssh/id_rsa - | - echo "โš™๏ธ Setting up registry path for module" - PROD_REGISTRY_MODULE_BASEDIR="${PROD_REGISTRY}/${MODULE_PROD_REGISTRY_CUSTOM_PATH:-deckhouse/fe/modules}" - DEV_REGISTRY_MODULE_BASEDIR="${DEV_REGISTRY}/${MODULE_DEV_REGISTRY_CUSTOM_PATH:-sys/deckhouse-oss/modules}" - severity="${SEVERITY:-UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL}" - latest_releases_amount="${LATEST_RELEASES_AMOUNT:-3}" - - echo "๐Ÿ“ฆ Using prod registry module base dir: ${PROD_REGISTRY_MODULE_BASEDIR}" - echo "๐Ÿงช Using dev registry module base dir: ${DEV_REGISTRY_MODULE_BASEDIR}" - - # If input var TAG is empty - set to default branch - if [ -z "${TAG}" ]; then - echo "๐Ÿท๏ธ TAG is empty, setting to default branch: ${CI_DEFAULT_BRANCH}" - TAG="${CI_DEFAULT_BRANCH}" - fi - # prepare TAG if it was triggered with CI_COMMIT_TAG - if [ -n "${CI_COMMIT_TAG}" ]; then - TAG=$(echo "${TAG}"| sed 's/^v//' | cut -d '.' -f -2) - echo "๐Ÿท๏ธ Minor tag to scan: ${TAG}" - fi - module_tags=("${TAG}") - - if [ "${CI_PIPELINE_SOURCE}" == "schedule" ]; then - echo "โฐ Pipeline is scheduled, several latest releases will be scanned" - SCAN_SEVERAL_LASTEST_RELEASES="true" - fi - - echo "๐Ÿ” Getting tags to scan" - # Check if provided tag is a semver minor, and if so - get image from prod registry - if echo "${TAG}" | grep -qE "^[0-9]+\.[0-9]+$"; then - echo "๐Ÿ“Š TAG is a semver minor, image from prod registry will be used" - echo "๐Ÿ“ˆ Total images for module found: $(crane ls "${PROD_REGISTRY_MODULE_BASEDIR}/${MODULE_NAME}" | wc -l)" - - echo "๐Ÿ” Trying to find tags to scan..." - module_tags=($(crane ls "${PROD_REGISTRY_MODULE_BASEDIR}/${MODULE_NAME}" | grep "^v${TAG}\.[0-9]*" | sort -V -r | head -n 1)) - echo "โœ… Selected images: ${module_tags[@]}" - fi - if [ "${SCAN_SEVERAL_LASTEST_RELEASES}" == "true" ]; then - echo "๐Ÿ“š Several latest releases will be scanned: ${latest_releases_amount}" + HOST=$(echo "$CVE_TEST_REPO_GIT" | sed -E 's/.*@([^:]+).*/\1/') + ssh-keyscan -H "$HOST" >> ~/.ssh/known_hosts 2>/dev/null + ssh-keyscan -H github.com >> ~/.ssh/known_hosts 2>/dev/null || true - # Get release tags by regexp, sort by sevmer desc, cut to get minor version, uniq and get several latest - echo "๐Ÿ” Trying to find tags to scan for several latest releases..." - releases=($(crane ls "${PROD_REGISTRY_MODULE_BASEDIR}/${MODULE_NAME}" | grep "^v[0-9]*\.[0-9]*\.[0-9]*" | sort -V -r)) - latest_minor_releases=($(printf '%s\n' "${releases[@]}"| cut -d "." -f -2 | uniq | head -n ${latest_releases_amount})) + - git clone --depth 1 $CVE_TEST_REPO_GIT /tmp/cve-scripts + - cp /tmp/cve-scripts/*.sh /tmp/cve-scripts/*.py . + - chmod +x *.sh *.py - for r in "${latest_minor_releases[@]}"; do - echo "๐Ÿ“ฆ Adding image for minor release: ${r} to scan" - module_tags+=($(printf '%s\n' "${releases[@]}" | grep "${r}" | sort -V -r|head -n 1)) - done - fi - echo "๐ŸŽฏ CVE Scan will be applied to the following tags of ${MODULE_NAME} module:" - echo "${module_tags[@]}" - - # Functions - trivy_scan() { - ${workdir}/bin/trivy i --timeout 15m --vex oci --show-suppressed --config-check "${TRIVY_POLICY_URL}" --cache-dir "${workdir}/bin/trivy_cache" --skip-db-update --skip-java-db-update --exit-code 0 --severity "${severity}" --ignorefile "${module_workdir}/.trivyignore" --format ${1} ${2} ${3} --quiet ${4} --username "${trivy_registry_user}" --password "${trivy_registry_pass}" --image-src remote - } - - send_report() { - dd_scan_type="${1}" - dd_report_file_path="${2}" - dd_module_name="${3}" - dd_image_name="${4}" - dd_engagement_name="[$(echo "${dd_scan_type}" | tr '[:lower:]' '[:upper:]')] [IMAGES] [${dd_branch}]" - - tags_string="\"external_modules\",\"images\",\"${dd_scan_type}\",\"${dd_release_or_dev_tag}\",\"${dd_image_version}\"" - if [[ -n "${dd_short_release_tag}" && -n "${dd_full_release_tag}" ]]; then - tags_string+=",\"${dd_short_release_tag}\",\"${dd_full_release_tag}\"" - fi - echo "" - echo "๐Ÿ“ค Uploading trivy ${dd_branch} report for image \"${dd_image_name}\" of \"${dd_module_name}\" module" - echo "" - dd_upload_response=$(curl -sw "%{http_code}" -X POST \ - --retry 10 \ - --retry-delay 20 \ - --retry-all-errors \ - ${DD_URL}/api/v2/reimport-scan/ \ - -H "accept: application/json" \ - -H "Authorization: Token ${DD_TOKEN}" \ - -F "auto_create_context=True" \ - -F "minimum_severity=Info" \ - -F "active=true" \ - -F "verified=true" \ - -F "scan_type=Trivy Scan" \ - -F "close_old_findings=true" \ - -F "do_not_reactivate=false" \ - -F "push_to_jira=false" \ - -F "file=@${dd_report_file_path}" \ - -F "product_type_name=External Modules" \ - -F "product_name=${dd_module_name}" \ - -F "scan_date=${date_iso}" \ - -F "engagement_name=${dd_engagement_name}" \ - -F "service=${dd_module_name} / ${dd_image_name}" \ - -F "group_by=component_name+component_version" \ - -F "deduplication_on_engagement=false" \ - -F "tags=external_module,${dd_scan_type},module:${dd_module_name},image:${dd_image_name},branch:${dd_branch},${dd_short_release_tag},${dd_full_release_tag},${dd_default_branch_tag},${dd_release_or_dev_tag}" \ - -F "test_title=[${dd_module_name}]: ${dd_image_name}:${dd_image_version}" \ - -F "version=${dd_image_version}" \ - -F "build_id=${IMAGE_HASH}" \ - -F "commit_hash=${CI_COMMIT_SHA}" \ - -F "branch_tag=${module_tag}" \ - -F "apply_tags_to_findings=true") - - dd_return_code="${dd_upload_response: -3}" - dd_return_body="${dd_upload_response:0: -3}" - if [ ${dd_return_code} -eq 201 ]; then - dd_engagement_id=$(echo ${dd_return_body} | jq ".engagement_id" ) - echo "dd_engagement_id: ${dd_engagement_id}" - echo "Update with tags: ${tags_string}" - # Updating engagement - dd_eng_patch_response=$(curl -sw "%{http_code}" -X "PATCH" \ - --retry 10 \ - --retry-delay 20 \ - --retry-all-errors \ - "${DD_URL}/api/v2/engagements/${dd_engagement_id}/" \ - -H "accept: application/json" \ - -H "Authorization: Token ${DD_TOKEN}" \ - -H "Content-Type: application/json" \ - -d "{ - \"tags\": ["${tags_string}"], - \"version\": \"${dd_image_version}\", - \"branch_tag\": \"${dd_branch}\" - }") - if [ ${dd_eng_patch_response: -3} -eq 200 ]; then - echo "โœ… Engagemet \"${dd_engagement_name}\" updated successfully" - else - echo "!!!WARNING!!!" - echo "Engagemet \"${dd_engagement_name}\" WAS NOT UPDATED" - echo "HTTP_CODE: ${dd_eng_patch_response: -3}" - echo "DD_RESPONSE: ${dd_eng_patch_response:0: -3}" - fi - else - echo "!!!WARNING!!!" - echo "Report for image \"${dd_image_name}\" of \"${dd_module_name}\" module WAS NOT UPLOADED" - echo "HTTP_CODE: ${dd_return_code}" - echo "DD_RESPONSE: ${dd_return_body}" - fi - - } - - # Scan in loop for provided list of tags - for module_tag in ${module_tags[@]}; do - dd_default_branch_tag="" - dd_short_release_tag="" - dd_full_release_tag="" - dd_release_or_dev_tag="dev" - dd_image_version="${module_tag}" - dd_branch="${module_tag}" - date_iso=$(date -I) - module_image="${DEV_REGISTRY_MODULE_BASEDIR}/${MODULE_NAME}" - trivy_registry_user="${DEV_REGISTRY_USER}" - trivy_registry_pass="${DEV_REGISTRY_PASSWORD}" - if [ "${module_tag}" == "${CI_DEFAULT_BRANCH}" ]; then - dd_default_branch_tag="default_branch" - fi - # If we are scanning release images - we need to redefine image path to prod registry - if echo "${module_tag}" | grep -q "^v[0-9]*\.[0-9]*\.[0-9]*" && [[ "${CI_PIPELINE_SOURCE}" != "merge_request_event" ]]; then - module_image="${PROD_REGISTRY_MODULE_BASEDIR}/${MODULE_NAME}" - trivy_registry_user="${PROD_REGISTRY_USER}" - trivy_registry_pass="${PROD_REGISTRY_PASSWORD}" - dd_short_release_tag="release:$(echo ${module_tag} | cut -d '.' -f -2 | sed 's/^v//')" - dd_full_release_tag="image_release_tag:${module_tag}" - dd_release_or_dev_tag="release" - dd_image_version="$(echo ${dd_short_release_tag} | sed 's/^release\://')" - fi - module_workdir="${workdir}/${MODULE_NAME}_${module_tag}" - module_reports="${module_workdir}/reports" - mkdir -p "${module_reports}" - touch ${module_workdir}/.trivyignore - echo "๐Ÿ” Image to check: ${module_image}:${module_tag}" - echo "โš ๏ธ Severity: ${severity}" - echo "----------------------------------------------" - echo "" - echo "๐Ÿ“ฅ Getting module image" - crane export "${module_image}:${module_tag}" "${MODULE_NAME}.tar" - tar xf "${MODULE_NAME}.tar" -C "${module_workdir}/" - echo "๐Ÿ“‹ Preparing images list to scan" - digests=$(cat "${module_workdir}${IMAGES_DIGESTS_PATH}") - # Main module images to scan - digests=$(echo "${digests}"|jq --arg i "${MODULE_NAME}" --arg s "${module_tag}" '. += { ($i): ($s) }') - echo "Images to scan:" - echo "${digests}" - - while read -r line; do - IMAGE_NAME=$(jq -rc '.key' <<< "${line}") - if [[ "${IMAGE_NAME}" == "trivy" ]]; then - continue - fi - # Set flag if additional image to use tag instead of hash - additional_image_detected=false - if [ "${IMAGE_NAME}" == "${MODULE_NAME}" ]; then - additional_image_detected=true - fi - echo "" - echo "----------------------------------------------" - echo "๐Ÿ‘พ Scaning image \"${IMAGE_NAME}\" of module \"${MODULE_NAME}\" for tag \"${module_tag}\"" - echo "" - IMAGE_HASH="$(jq -rc '.value' <<< "${line}")" - - if [ "${additional_image_detected}" == true ]; then - if [ "${TRIVY_REPORTS_LOG_OUTPUT}" -ne 0 ]; then - # CVE Scan - trivy_scan "table" "--scanners vuln" "" "${module_image}:${module_tag}" - if [ "${TRIVY_REPORTS_LOG_OUTPUT}" -eq 2 ]; then - # License scan - trivy_scan "table" "--scanners license --license-full" "" "${module_image}:${module_tag}" - fi - fi - # CVE Scan - trivy_scan "json" "--scanners vuln" "--output ${module_reports}/ext_${MODULE_NAME}_${IMAGE_NAME}_report.json" "${module_image}:${module_tag}" - # License scan - trivy_scan "json" "--scanners license --license-full" "--output ${module_reports}/ext_${MODULE_NAME}_${IMAGE_NAME}_report_license.json" "${module_image}:${module_tag}" - else - if [ "${TRIVY_REPORTS_LOG_OUTPUT}" -ne 0 ]; then - # CVE Scan - trivy_scan "table" "--scanners vuln" "" "${module_image}@${IMAGE_HASH}" - if [ "${TRIVY_REPORTS_LOG_OUTPUT}" -eq 2 ]; then - # License scan - trivy_scan "table" "--scanners license --license-full" "" "${module_image}@${IMAGE_HASH}" - fi - fi - # CVE Scan - trivy_scan "json" "--scanners vuln" "--output ${module_reports}/ext_${MODULE_NAME}_${IMAGE_NAME}_report.json" "${module_image}@${IMAGE_HASH}" - # License scan - trivy_scan "json" "--scanners license --license-full" "--output ${module_reports}/ext_${MODULE_NAME}_${IMAGE_NAME}_report_license.json" "${module_image}@${IMAGE_HASH}" - fi - echo " โœ… Done" - - send_report "CVE" "${module_reports}/ext_${MODULE_NAME}_${IMAGE_NAME}_report.json" "${MODULE_NAME}" "${IMAGE_NAME}" - send_report "License" "${module_reports}/ext_${MODULE_NAME}_${IMAGE_NAME}_report_license.json" "${MODULE_NAME}" "${IMAGE_NAME}" - done < <(jq -rc 'to_entries[]' <<< "${digests}") - done - rm -rf ${workdir} + script: + - | + export TRIVY_BIN_VERSION + export TRIVY_REPO_ID + export TRIVY_DB_URL + export TRIVY_JAVA_DB_URL + export TRIVY_POLICY_URL + export TRIVY_REPORTS_LOG_OUTPUT + export PROD_REGISTRY + export PROD_REGISTRY_USER + export PROD_REGISTRY_PASSWORD + export DEV_REGISTRY + export DEV_REGISTRY_USER + export DEV_REGISTRY_PASSWORD + export SOURCE_TAG + export CASE + export EXTERNAL_MODULE_NAME + export RELEASE_IN_DEV + export SCAN_USERS + export SCAN_SEVERAL_LATEST_RELEASES + export LATEST_RELEASES_AMOUNT + export MODULE_PROD_REGISTRY_CUSTOM_PATH + export MODULE_DEV_REGISTRY_CUSTOM_PATH + export DIGEST_FROM_WERF + export DD_URL + export DD_TOKEN + export CODEOWNERS_REPO_TOKEN + export DECKHOUSE_PRIVATE_REPO + export CONFIGMAP_PROJECT_ID + export WORKDIR="${CI_PROJECT_DIR}/${WORKDIR}" + + ./cve_scan.sh \ No newline at end of file From 086cd68179cd58b907346822f5a70dc9b9bb0b4e Mon Sep 17 00:00:00 2001 From: Maxim Konovalenko Date: Thu, 19 Feb 2026 11:01:21 +0300 Subject: [PATCH 2/2] ci Signed-off-by: Maxim Konovalenko --- templates/CVE_Scan.gitlab-ci.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/templates/CVE_Scan.gitlab-ci.yml b/templates/CVE_Scan.gitlab-ci.yml index 0da0ff9..b159791 100644 --- a/templates/CVE_Scan.gitlab-ci.yml +++ b/templates/CVE_Scan.gitlab-ci.yml @@ -40,14 +40,21 @@ - | HOST=$(echo "$CVE_TEST_REPO_GIT" | sed -E 's/.*@([^:]+).*/\1/') ssh-keyscan -H "$HOST" >> ~/.ssh/known_hosts 2>/dev/null - ssh-keyscan -H github.com >> ~/.ssh/known_hosts 2>/dev/null || true + - rm -rf /tmp/cve-scripts - git clone --depth 1 $CVE_TEST_REPO_GIT /tmp/cve-scripts - cp /tmp/cve-scripts/*.sh /tmp/cve-scripts/*.py . - chmod +x *.sh *.py script: - | + export SOURCE_TAG + export CASE + export EXTERNAL_MODULE_NAME + export RELEASE_IN_DEV + export SCAN_USERS + export SCAN_SEVERAL_LATEST_RELEASES + export LATEST_RELEASES_AMOUNT export TRIVY_BIN_VERSION export TRIVY_REPO_ID export TRIVY_DB_URL @@ -60,13 +67,6 @@ export DEV_REGISTRY export DEV_REGISTRY_USER export DEV_REGISTRY_PASSWORD - export SOURCE_TAG - export CASE - export EXTERNAL_MODULE_NAME - export RELEASE_IN_DEV - export SCAN_USERS - export SCAN_SEVERAL_LATEST_RELEASES - export LATEST_RELEASES_AMOUNT export MODULE_PROD_REGISTRY_CUSTOM_PATH export MODULE_DEV_REGISTRY_CUSTOM_PATH export DIGEST_FROM_WERF