Security Command Center customers can find it difficult to obtain a centralized, comprehensive view of scan results from their Continuous Integration/Continuous Delivery (CI/CD) environments. This lack of a unified view complicates vulnerability management. The CI/CD integration is a component of artifact guard (Preview) that lets you connect multiple CI/CD pipelines to help you detect vulnerabilities throughout the software development lifecycle.
The CI/CD integration supports GitHub Actions, Cloud Build, and Jenkins. Once connected, you can use any of these CI/CD platforms to configure artifact guard policies, providing visibility and proactive control over your security posture.
Overview
CI/CD integration offers the following:
- End-to-end policy enforcement: Assists artifact guard in applying consistent security policies from code to cloud.
- Comprehensive threat detection: Scans for vulnerabilities, exposed secrets, problematic licenses, and malicious packages.
- Broad CI/CD compatibility: Works with Jenkins and GitHub Actions.
- Optimized for CI/CD: A lightweight binary ensures efficient operation in local environments.
- Flexible output: Provides results in industry-standard JSON and SARIF formats.
- Centralized security insights: Provides clear policy conformance views directly in your security dashboard.
This scanner natively populates Security Command Center, providing a unified, asset-centric view of security findings from artifact creation.
The CI/CD integration helps you enforce artifact guard policies across the software lifecycle. This ensures that artifacts are validated for conformance from build through deployment.
Audience
CI/CD integration can help with the following stakeholder tasks:
- Primary users: Devops and platform engineering teams
- Manage and integrate: Responsible for managing the scanning tool and integrating it directly into CI/CD pipelines.
- Configure policies: Set up scanning steps.
- Monitor compliance: Track build compliance and work with security administrators to refine policies.
- Secondary users: Security administrators
- Policy authors: Define and enforce security policies based on business criticality.
- Automate enforcement: Automate security within CI/CD to reduce vulnerability noise and set gating criteria.
- Provide oversight: Collaborate with DevOps and offer a dashboard for management to track gated vulnerabilities.
- Secondary users: Application developers
- Review and remediate: Interact with policy evaluation results, review build outcomes, and fix flagged security issues.
- Evaluate: Initiate policy assessment indirectly through the CI pipeline's build process.
- Maintain compliance: Ensure compliance with security requirements without disrupting development workflows.
Key terms and concepts
- Common Vulnerabilities and Exposures (CVE): A publicly disclosed computer security vulnerability that is assigned a unique identifier. These identifiers help track vulnerabilities for remediation.
- Software Bill of Materials (SBOM): A machine-readable inventory of software components and dependencies. An SBOM includes information about each component's version, origin, and other relevant details. SBOMs can be used to identify CVEs and other security risks.
- Artifact: A versioned and validated output of software development, such as data or an item created during the build process.
- Connector: A tag for images. When passed from the CI pipeline to the artifact guard service, a connector determines which policies to run against the image being built.
- CI Policy: A vulnerability policy defining rules or criteria to control which vulnerabilities and packages are permitted in your environment.
High-level workflow
- Create CI connectors.
- Create artifact guard policies using the connectors configured in the preceding step to define the policy scope.
- Initiate artifact evaluations.
During an evaluation, an image is built and evaluated against predefined policies. If policies fail, the build fails. DevOps or application engineers must then examine the failure details to find the specific vulnerability, update the dependency per the CVE details, and rerun the pipeline.
Before you begin
To use the CI/CD integration, you must enable artifact guard. For instructions, see Before you begin in the artifact guard documentation.
Then, you can create connectors in the Google Cloud console or using Google Cloud CLI.
Create a connector in the Google Cloud console
To create a connector, follow these steps:
In the Google Cloud console, go to Security > Settings.
On the Artifact guard card, click Manage settings.
Click Create connector and enter the following details for the connector:
- Connector ID: Add an ID for the connector.
- Description: Enter a description of the connector.
- CI/CD platform: Select the corresponding CI/CD platform from the list. This connector should only be used in the pipelines built with the provided CI/CD platform.
Click Create.
A notification confirms successful connector creation. Available connectors are listed in the Connectors table.
To remove a connector, click next to the connector and select Delete connector, then follow the prompts. Click Cancel to abort.
To link a policy to a connector, click next to the connector and select Add policy. Proceed with the steps to create an artifact guard policy. For more information, see Create a policy.
Create a connector using Google Cloud CLI
This section outlines the gcloud CLI commands available for CI/CD Artifact Scanning and how to use them.
Google Cloud CLI prerequisites
- Ensure your gcloud CLI version is 559.0.0 or higher.
- Set your project as the config project.
To do this, run the following gcloud CLI commands:
gcloud components update --version=559.0.0
gcloud config set project PROJECT_ID
Google Cloud CLI commands
create
gcloud alpha scc artifact-guard connectors create CONNECTOR_ID \ --location=LOCATION \ (--organization=ORGANIZATION_ID | --project=PROJECT_NUMBER) \ --pipeline-type=PIPELINE_TYPE \ [--description=DESCRIPTION] \ [--display-name=DISPLAY_NAME]
- CONNECTOR_ID: The ID of the connector to be created.
- PIPELINE_TYPE: The type of CI/CD pipeline. Must be one of the
following:
GOOGLE_CLOUD_BUILDGITHUB_ACTIONSJENKINS_PIPELINE
- DESCRIPTION: A text description for the connector.
- DISPLAY_NAME: A user-friendly display name for the connector.
get
gcloud alpha scc artifact-guard connectors describe CONNECTOR_ID \ --location=LOCATION \ (--organization=ORGANIZATION_ID | --project=PROJECT_NUMBER)
- CONNECTOR_ID: The ID of the connector to be described.
list
gcloud alpha scc artifact-guard connectors list PARENT
- PARENT: An organization or project. Acceptable
formats for the parent resource include:
{organizations/ORGANIZATION_ID/locations/LOCATION}{projects/PROJECT_NUMBER/locations/LOCATION}
delete
gcloud alpha scc artifact-guard connectors delete CONNECTOR_ID \ --location=LOCATION \ (--organization=ORGANIZATION_ID | --project=PROJECT_NUMBER)
- CONNECTOR_ID: The ID of the connector to be deleted.
Run an evaluation
Vulnerability scanning is supported for GitHub Actions and Jenkins pipelines. To perform an evaluation, you must do the following:
- Create secrets for authentication.
- Create an integration template file specific to your pipeline.
- Initiate an evaluation.
Secret configuration
CI/CD pipelines running outside of Google Cloud can authenticate using either service account keys or Workload Identity Federation. For detailed instructions on creating secrets, see the following:
Service account keys
Workload Identity Federation (for GitHub Actions)
You must add secrets to your CI/CD environment using one of the following methods:
Service account key method
One secret:
GCP_CREDENTIALS: The contents of your downloaded service account JSON key file.
Workload Identity Federation method
Two secrets:
GCP_WORKLOAD_IDENTITY_PROVIDER: The full resource name of your Workload Identity Provider. For example,projects/12345/locations/global/workloadIdentityPools/my-pool/providers/my-provider.GCP_SERVICE_ACCOUNT: The email address of the service account to impersonate.
Pipeline integration templates
To trigger an evaluation, you must create a file specific to your pipeline (Cloud Build, GitHub Actions or Jenkins) using the following template examples:
Cloud Build
See Variable definitions for information about each field.
steps: # Step 1: Generate auth token - name: 'gcr.io/cloud-builders/gcloud' id: 'Generate Token' entrypoint: 'bash' args: - '-c' - | echo "Starting token generation..." gcloud auth print-access-token > /workspace/gcp_token.txt if [ $? -eq 0 ]; then echo "Token generated successfully." else echo "Failed to generate token." >&2 exit 1 fi # Step 2: Build the image locally - name: 'gcr.io/cloud-builders/docker' id: 'Build Image' entrypoint: 'bash' args: - '-c' - | echo "🚧 Building Docker image from source code..." docker build -t ${_IMAGE_NAME_TO_SCAN}:${_IMAGE_TAG} . if [ $? -ne 0 ]; then echo "❌ Docker build failed." exit 1 fi echo "✅ Docker image built successfully: ${_IMAGE_NAME_TO_SCAN}:${_IMAGE_TAG}" # Step 3: Image scan for vulnerabilities - id: 'Image-Analysis' name: '${_SCANNER_IMAGE}' entrypoint: 'bash' args: - '-c' - | echo "Starting image scan with scanner: ${_SCANNER_IMAGE}" exit_code=0 docker run --rm \ -v /var/run/docker.sock:/var/run/docker.sock \ -v /workspace:/workspace \ -e GCP_PROJECT_ID="${_PROJECT_ID}" \ -e ORGANIZATION_ID="${_ORGANIZATION_ID}" \ -e IMAGE_NAME="${_IMAGE_NAME_TO_SCAN}" \ -e IMAGE_TAG="${_IMAGE_TAG}" \ -e CONNECTOR_ID="${_CONNECTOR_ID}" \ -e TRIGGER_ID="${_TRIGGER_ID}" \ -e IGNORE_ERRORS="${_IGNORE_ERRORS}" \ -e GCP_ACCESS_TOKEN="$(cat /workspace/gcp_token.txt)" \ "${_SCANNER_IMAGE}" || exit_code=$? echo "Docker run finished with exit code: $exit_code" if [ $exit_code -eq 0 ]; then echo "✅ Evaluation succeeded: Conformant image." elif [ $exit_code -eq 1 ]; then echo "❌ Scan failed: Non-conformant image." exit 1 else if [ "${_IGNORE_ERRORS}" = "true" ]; then echo "⚠️ Server/internal error ignored. Continuing." else echo "❌ Server/internal error. Exiting." exit 1 fi fi # Step 4: Configure Docker authentication for Artifact Registry - name: 'gcr.io/cloud-builders/gcloud' id: 'Configure Docker Auth' entrypoint: 'bash' args: - '-c' - | echo "🔐 Configuring Docker authentication for Artifact Registry..." gcloud auth configure-docker us-east1-docker.pkg.dev -q echo "✅ Docker authentication configured." # Step 5: Push image to Artifact Registry - name: 'gcr.io/cloud-builders/docker' id: 'Push Image to Artifact Registry' entrypoint: 'bash' args: - '-c' - | docker tag "${_IMAGE_NAME_TO_SCAN}:${_IMAGE_TAG}" "us-east1-docker.pkg.dev/${_PROJECT_ID}/${_AR_REPOSITORY}/${_IMAGE_NAME_TO_SCAN}:${_IMAGE_TAG}" echo "🚀 Pushing $_FULL_AR_TAG..." docker push "us-east1-docker.pkg.dev/${_PROJECT_ID}/${_AR_REPOSITORY}/${_IMAGE_NAME_TO_SCAN}:${_IMAGE_TAG}" echo "✅ Image pushed successfully." substitutions: _IMAGE_NAME_TO_SCAN: 'checkout-image' _ORGANIZATION_ID: 'orgId' _CONNECTOR_ID: 'connectorId' _SCANNER_IMAGE: 'us-central1-docker.pkg.dev/ci-plugin/ci-images/scc-artifactguard-scan-image:latest' _IMAGE_TAG: 'latest' _TRIGGER_ID: 'cloud-build-job' _PROJECT_ID: 'projectId' _AR_REPOSITORY: 'images' _IGNORE_ERRORS: "false" serviceAccount: "projects/projectId/serviceAccounts/id-compute@developer.gserviceaccount.com" options: logging: CLOUD_LOGGING_ONLY
GitHub Actions (secret)
This template is for GitHub Actions using a secret key.
- Add your service account secret key (
GCP_CREDENTIALS). See Variable definitions for information about additional fields.
# A workflow to BUILD the app image, RUN the scanner, and PUSH to AR if scan passes name: Build, Scan and Push on: workflow_dispatch: inputs: IMAGE_NAME_TO_SCAN: description: 'The tag for your application image to be built (e.g., my-app:latest)' required: true default: 'checkout-image' GCP_PROJECT_ID: description: 'GCP Project ID for authentication' required: true default: 'projectId' AR_REPOSITORY: description: 'Artifact Registry repository name (e.g., app-repo)' required: false default: 'images' ORGANIZATION_ID: description: 'Your GCP Organization ID' required: true default: 'orgId' CONNECTOR_ID: description: 'The ID for your pipeline connector' required: true default: 'connectorId' SCANNER_IMAGE: description: 'The full registry path for your PRE-BUILT scanner tool' required: true default: 'us-central1-docker.pkg.dev/ci-plugin/ci-images/scc-artifactguard-scan-image:latest' IMAGE_TAG: description: 'The Docker image version (of the app image)' required: true default: 'latest' IGNORE_SERVER_ERRORS: description: 'Ignore server errors' required: false type: boolean default: false VERBOSITY: description: 'Verbosity flag' required: false default: 'HIGH' jobs: build-and-scan: runs-on: ubuntu-latest steps: # 1. Check out repository (for your app's Dockerfile) - name: Check out repository uses: actions/checkout@v4 # 2. Authenticate to Google Cloud - name: Authenticate to GCP id: auth uses: 'google-github-actions/auth@v2' with: credentials_json: '${{ secrets.GCP_CREDENTIALS }}' # 3. Set up the gcloud CLI - name: Set up Cloud SDK uses: 'google-github-actions/setup-gcloud@v2' with: project_id: ${{ inputs.GCP_PROJECT_ID }} # 4. Configure Docker (needed to pull SCANNER_IMAGE and push app image) - name: Configure Docker run: gcloud auth configure-docker us-central1-docker.pkg.dev --quiet # 5. Build Application Image Locally (IMAGE_NAME_TO_SCAN) - name: Build Application Image Locally uses: docker/build-push-action@v5 with: context: . file: ./Dockerfile push: false # <-- Do not push load: true # <-- Load image into the runner's local daemon # Tag the image with the name the scanner will look for tags: | ${{ inputs.IMAGE_NAME_TO_SCAN }}:${{ inputs.IMAGE_TAG }} # 6. Run Image Scan (Using the SCANNER_IMAGE) - name: 'Run Image Analysis Scan' if: steps.auth.outcome == 'success' run: | echo "📦 Pulling scanner image and running scan..." SCANNER_IMAGE="${{ github.event_name == 'workflow_dispatch' && github.event.inputs.SCANNER_IMAGE || env.SCANNER_IMAGE }}" GCP_PROJECT_ID="${{ github.event_name == 'workflow_dispatch' && github.event.inputs.GCP_PROJECT_ID || env.GCP_PROJECT_ID }}" ORGANIZATION_ID="${{ github.event_name == 'workflow_dispatch' && github.event.inputs.ORGANIZATION_ID || env.ORGANIZATION_ID }}" IMAGE_NAME="${{ github.event_name == 'workflow_dispatch' && github.event.inputs.IMAGE_NAME_TO_SCAN || env.IMAGE_NAME_TO_SCAN }}" IMAGE_TAG="${{ github.event_name == 'workflow_dispatch' && github.event.inputs.IMAGE_TAG || env.IMAGE_TAG }}" CONNECTOR_ID="${{ github.event_name == 'workflow_dispatch' && github.event.inputs.CONNECTOR_ID || env.CONNECTOR_ID }}" VERBOSITY="${{ github.event_name == 'workflow_dispatch' && github.event.inputs.VERBOSITY || env.VERBOSITY }}" IGNORE_ERRORS="${{ github.event_name == 'workflow_dispatch' && github.event.inputs.IGNORE_SERVER_ERRORS || (env.IGNORE_SERVER_ERRORS == 'true') }}" exit_code=0 # This 'docker run' pulls the SCANNER_IMAGE from the registry # and passes the name of the locally-built app image (IMAGE_NAME) docker run --rm \ -v /var/run/docker.sock:/var/run/docker.sock \ -v ${{ steps.auth.outputs.credentials_file_path }}:/tmp/scc-key.json \ -e GCLOUD_KEY_PATH=/tmp/scc-key.json \ -e GCP_PROJECT_ID="${GCP_PROJECT_ID}" \ -e ORGANIZATION_ID="${ORGANIZATION_ID}" \ -e IMAGE_NAME="${IMAGE_NAME}" \ -e IMAGE_TAG="${IMAGE_TAG}" \ -e CONNECTOR_ID="${CONNECTOR_ID}" \ -e BUILD_TAG="${{ github.workflow }}" \ -e BUILD_ID="${{ github.run_number }}" \ -e VERBOSITY="${VERBOSITY}" \ "${SCANNER_IMAGE}" \ || exit_code=$? echo "Docker run finished with exit code: $exit_code" # --- Replicate Jenkins Exit Code Logic --- if [ $exit_code -eq 0 ]; then echo "✅ Evaluation succeeded: Conformant image." elif [ $exit_code -eq 1 ]; then echo "❌ Scan failed: Non-conformant image (vulnerabilities found)." exit 1 # Fail the step else if [ "$IGNORE_ERRORS" = "true" ]; then echo "⚠️ Server/internal error occurred (Code: $exit_code), but IGNORE_SERVER_ERRORS=true. Proceeding." else echo "❌ Server/internal error occurred (Code: $exit_code) during evaluation. Set IGNORE_SERVER_ERRORS=true to override." exit 1 # Fail the step fi fi # 8. Push Application Image (ONLY if scan succeeded) # This step only runs if the 'Run Image Analysis Scan' step above exited with 0 - name: Push Application Image to Artifact Registry run: | # Define the local and remote tags LOCAL_IMAGE_NAME="${{ inputs.IMAGE_NAME_TO_SCAN }}:${{ inputs.IMAGE_TAG }}" # This path is based on your 'Configure Docker' step (us-central1) # and the new AR_REPOSITORY input. FULL_AR_TAG="us-central1-docker.pkg.dev/${{ inputs.GCP_PROJECT_ID }}/${{ inputs.AR_REPOSITORY }}/${{ inputs.IMAGE_NAME_TO_SCAN }}:${{ inputs.IMAGE_TAG }}" echo "Tagging local image ${LOCAL_IMAGE_NAME} as ${FULL_AR_TAG}" docker tag "${LOCAL_IMAGE_NAME}" "${FULL_AR_TAG}" echo "Pushing ${FULL_AR_TAG} to Artifact Registry..." docker push "${FULL_AR_TAG}"
GitHub Actions (WIF)
This template is for GitHub Actions using Workload Identity Federation.
- Add your Workload Identity Provider in GitHub secret (
GCP_WORKLOAD_IDENTITY_PROVIDER). - Add your Service Account in GitHub secret (
GCP_SERVICE_ACCOUNT). See Variable definitions for information about additional fields.
# A workflow to BUILD the app image, RUN the scanner, and PUSH to AR if scan passes name: Build, Scan and Push on: push: branches: - main workflow_dispatch: inputs: IMAGE_NAME_TO_SCAN: description: 'The tag for your application image to be built (e.g., my-app:latest)' required: true default: 'checkout-image' GCP_PROJECT_ID: description: 'GCP Project ID for authentication and configuration' required: true default: 'projectId' ORGANIZATION_ID: description: 'Your GCP Organization ID' required: true default: 'orgId' CONNECTOR_ID: description: 'The ID for your pipeline connector' required: true default: 'connectorId' SCANNER_IMAGE: description: 'The Docker image that contains your scanner script' required: true default: 'us-central1-docker.pkg.dev/ci-plugin/ci-images/scc-artifactguard-scan-image:latest' IMAGE_TAG: description: 'The Docker image version' required: true default: 'latest' IGNORE_SERVER_ERRORS: description: 'If true, the pipeline continues on server/internal scanner errors.' required: false type: boolean default: false jobs: image-analysis-job: runs-on: ubuntu-latest permissions: contents: 'read' id-token: 'write' env: IMAGE_NAME_TO_SCAN: 'webgoat/webgoat' GCP_PROJECT_ID: 'projectId' ORGANIZATION_ID: 'orgId' CONNECTOR_ID: 'connectorId' SCANNER_IMAGE: 'us-central1-docker.pkg.dev/ci-plugin/ci-images/scc-artifactguard-scan-image:latest' IMAGE_TAG: 'imageTag' IGNORE_SERVER_ERRORS: 'false' steps: # Step 1: Authenticate and create credential file - name: 'Authenticate to Google Cloud' id: 'auth' uses: 'google-github-actions/auth@v2' with: workload_identity_provider: ${{ secrets.GCP_WORKLOAD_IDENTITY_PROVIDER }} service_account: ${{ secrets.GCP_SERVICE_ACCOUNT }} create_credentials_file: true # Step 2: Set up gcloud SDK - name: 'Set up gcloud SDK' uses: 'google-github-actions/setup-gcloud@v2' # Step 3: Configure Docker for registries - name: 'Configure Docker for Artifact Registry' run: | gcloud auth configure-docker us-central1-docker.pkg.dev --quiet # Step 4: Run Image Analysis Scan and Handle Exit Codes - name: 'Run Image Analysis Scan' run: | echo "📦 Running container from scanner image..." # Determine values: Use manual inputs if available (event_name=workflow_dispatch), otherwise use env defaults SCANNER_IMAGE="${{ github.event_name == 'workflow_dispatch' && github.event.inputs.SCANNER_IMAGE || env.SCANNER_IMAGE }}" GCP_PROJECT_ID="${{ github.event_name == 'workflow_dispatch' && github.event.inputs.GCP_PROJECT_ID || env.GCP_PROJECT_ID }}" ORGANIZATION_ID="${{ github.event_name == 'workflow_dispatch' && github.event.inputs.ORGANIZATION_ID || env.ORGANIZATION_ID }}" IMAGE_NAME="${{ github.event_name == 'workflow_dispatch' && github.event.inputs.IMAGE_NAME_TO_SCAN || env.IMAGE_NAME_TO_SCAN }}" IMAGE_TAG="${{ github.event_name == 'workflow_dispatch' && github.event.inputs.IMAGE_TAG || env.IMAGE_TAG }}" CONNECTOR_ID="${{ github.event_name == 'workflow_dispatch' && github.event.inputs.CONNECTOR_ID || env.CONNECTOR_ID }}" IGNORE_ERRORS="${{ github.event_name == 'workflow_dispatch' && github.event.inputs.IGNORE_SERVER_ERRORS || (env.IGNORE_SERVER_ERRORS == 'true') }}" # Variable to store exit code exit_code=0 # Run docker and capture exit code using || trick docker run --rm \ -v /var/run/docker.sock:/var/run/docker.sock \ -v ${{ steps.auth.outputs.credentials_file_path }}:/gcp-creds.json \ -e GOOGLE_APPLICATION_CREDENTIALS=/gcp-creds.json \ -e GCP_PROJECT_ID="${GCP_PROJECT_ID}" \ -e ORGANIZATION_ID="${ORGANIZATION_ID}" \ -e IMAGE_NAME="${IMAGE_NAME}" \ -e IMAGE_TAG="${IMAGE_TAG}" \ -e CONNECTOR_ID="${CONNECTOR_ID}" \ -e RUN_ID="${{ github.run_number }}" \ "${SCANNER_IMAGE}" \ || exit_code=$? echo "Docker run finished with exit code: $exit_code" if [ $exit_code -eq 0 ]; then echo "✅ Evaluation succeeded: Conformant image." elif [ $exit_code -eq 1 ]; then echo "❌ Scan failed: Non-conformant image (vulnerabilities found)." exit 1 # Fail the step else if [ "$IGNORE_ERRORS" = "true" ]; then echo "⚠️ Server/internal error occurred (Code: $exit_code), but IGNORE_SERVER_ERRORS=true. Proceeding." # Do nothing, step passes else echo "❌ Server/internal error occurred (Code: $exit_code) during evaluation. Set IGNORE_SERVER_ERRORS=true to override." exit 1 # Fail the step fi fi
Jenkins (secret)
- Add service account secret key (
GCP_CREDENTIALS). See Variable definitions for information about additional fields.
pipeline { agent any parameters { string( name: 'IMAGE_NAME_TO_SCAN', defaultValue: 'checkout-image', description: 'The tag for your application image to be built (e.g., my-app:latest)' ) string( name: 'GCP_PROJECT_ID', defaultValue: 'projectId', description: 'GCP Project ID for authentication' ) string( name: 'AR_REPOSITORY', defaultValue: 'images', description: 'Artifact Registry repository name (e.g., app-repo)' ) string( name: 'ORGANIZATION_ID', defaultValue: 'orgId', description: 'Your GCP Organization ID' ) string( name: 'CONNECTOR_ID', defaultValue: 'connectorId', description: 'The ID for your pipeline connector' ) string( name: 'SCANNER_IMAGE', defaultValue: 'us-central1-docker.pkg.dev/ci-plugin/ci-images/scc-artifactguard-scan-image:latest', description: 'The full registry path for your PRE-BUILT scanner tool' ) string( name: 'IMAGE_TAG', defaultValue: 'latest', description: 'The Docker image version (of the app image)' ) booleanParam( name: 'IGNORE_SERVER_ERRORS', defaultValue: false, description: 'Ignore server errors' ) string( name: 'VERBOSITY', defaultValue: 'HIGH', description: 'Verbosity flag' ) } stages { // Stage 1: Check out the source code stage('Checkout') { steps { echo "Checking out source code..." checkout scm } } // Stage 2: Build application image stage('Build Application Image') { steps { echo "Building application image: ${params.IMAGE_NAME_TO_SCAN}:${params.IMAGE_TAG}" sh "docker build -t ${params.IMAGE_NAME_TO_SCAN}:${params.IMAGE_TAG} -f ./Dockerfile ." } } // Stage 3: Authenticate to Google Cloud and run scanner stage('Scan Image') { steps { script { withCredentials([file(credentialsId: 'GCP_CREDENTIALS', variable: 'GCP_KEY_FILE')]) { // Authenticate sh "gcloud auth activate-service-account --key-file=\"$GCP_KEY_FILE\"" sh 'gcloud auth list' sh 'gcloud auth configure-docker gcr.io --quiet' sh 'gcloud auth configure-docker us-central1-docker.pkg.dev --quiet' // Run scanner container def exitCode = sh( script: """ echo "📦 Running scanner container from image: ${params.SCANNER_IMAGE}" docker run --rm \\ -v /var/run/docker.sock:/var/run/docker.sock \\ -v "$GCP_KEY_FILE":/tmp/scc-key.json \\ -e GCLOUD_KEY_PATH=/tmp/scc-key.json \\ -e GCP_PROJECT_ID="${params.GCP_PROJECT_ID}" \\ -e ORGANIZATION_ID="${params.ORGANIZATION_ID}" \\ -e IMAGE_NAME="${params.IMAGE_NAME_TO_SCAN}" \\ -e IMAGE_TAG="${params.IMAGE_TAG}" \\ -e CONNECTOR_ID="${params.CONNECTOR_ID}" \\ -e BUILD_TAG="${env.JOB_NAME}" \\ -e BUILD_ID="${env.BUILD_NUMBER}" \\ "${params.SCANNER_IMAGE}" """, returnStatus: true ) if (exitCode == 0) { echo "✅ Evaluation succeeded: Conformant image." } else if (exitCode == 1) { error("❌ Scan failed: Non-conformant image (vulnerabilities found).") } else { if (params.IGNORE_SERVER_ERRORS) { echo "⚠️ Server/internal error occurred, but IGNORE_SERVER_ERRORS=true. Proceeding with pipeline." } else { error("❌ Server/internal error occurred during evaluation. Set IGNORE_SERVER_ERRORS=true to override.") } } } } } } // Stage 4: Push Application Image stage('Push Application Image') { steps { script { def localImage = "${params.IMAGE_NAME_TO_SCAN}:${params.IMAGE_TAG}" def remoteTag = "us-central1-docker.pkg.dev/${params.GCP_PROJECT_ID}/${params.AR_REPOSITORY}/${params.IMAGE_NAME_TO_SCAN}:${params.IMAGE_TAG}" echo "Tagging local image ${localImage} as ${remoteTag}" sh "docker tag ${localImage} ${remoteTag}" echo "Pushing ${remoteTag} to Artifact Registry..." sh "docker push ${remoteTag}" } } } }
Variable definitions
This section provides information about the variable fields used in the Pipeline integration templates.
IMAGE_NAME_TO_SCAN (Required)
- Specifies the tag of the application image to be built.
GCP_PROJECT_ID (Required)
- Specifies the Google Cloud Project ID used for authentication and configuration.
AR_REPOSITORY (Optional)
- Specifies the name of the Artifact Registry repository where the image will be published if the build succeeds.
ORGANIZATION_ID (Required)
- The Google Cloud organization ID.
CONNECTOR_ID (Required)
- Specifies the connector ID of the pipeline to be used.
SCANNER_IMAGE (Required)
The prebuilt scanner image analyzes code, identifies vulnerabilities by evaluating images against policies during the build, and produces a conformance result to determine CI/CD pipeline passes or fails.
Image details:
us-central1-docker.pkg.dev/ci-plugin/ci-images/scc-artifactguard-scan-image:latest
VERBOSITY (Optional)
- The scanner supports an optional
VERBOSITYflag that controls the level of detail displayed in the scan output. The scanner output varies based on the verbosity level and conformance result (Pass or Fail). The verbosity flag can be set to
LOWorHIGH. It defaults toLOWif not provided.
Low verbosity (concise)
ArtifactGuard Conformance : False
- Details the reason for failure.
- Lists only the specific CVEs that caused the failure.
- Provides a summarized count of CVEs categorized by severity.
ArtifactGuard Conformance : Pass
- Details the policy names.
- Provides only the summarized count of CVEs by severity.
High verbosity (detailed)
Provides a comprehensive list of all vulnerabilities found.
ArtifactGuard Conformance : False
- Includes the reason for failure.
- Lists the CVEs causing the failure.
- Provides a list of all CVEs detected for the policy.
- Provides a summary count of CVEs broken down by severity.
- Provides a comprehensive list of all detected vulnerabilities.
ArtifactGuard Conformance : Pass
- Provides a list of all CVEs detected for the policy.
- Provides a summary count of CVEs broken down by severity.
- Provides a comprehensive list of all detected vulnerabilities.
IGNORE_SERVER_ERRORS (Optional)
- An optional boolean flag. If
true, the pipeline continues despite server errors. Defaults tofalse.
Initiate an evaluation
During the build process, a Docker-based strategy evaluates the image against
predefined policies. The vulnerability scanning logic is contained within the
us-central1-docker.pkg.dev/ci-plugin/ci-images/scc-artifactguard-scan-image:latest
image.
To initiate a vulnerability scan in your CI/CD pipeline, run the following command:
docker run --rm \
-v /var/run/docker.sock:/var/run/docker.sock \
-v ${{ steps.auth.outputs.credentials_file_path }}:/gcp-creds.json \
-e GOOGLE_APPLICATION_CREDENTIALS=/gcp-creds.json \
-e GCP_PROJECT_ID="${GCP_PROJECT_ID}" \
-e ORGANIZATION_ID="${ORGANIZATION_ID}" \
-e IMAGE_NAME="${IMAGE_NAME}" \
-e IMAGE_TAG="${IMAGE_TAG}" \
-e CONNECTOR_ID="${CONNECTOR_ID}" \
-e RUN_ID="${{ github.run_number }}" \
"${SCANNER_IMAGE}" \
|| exit_code=$?
The CI/CD integration propagates the Docker container's exit codes to the Jenkins or GitHub Actions runtime, which then determines the pipeline's pass or fail state.
Performance and limitations
- Artifact evaluation: An image is evaluated only if it meets the
following criteria:
- The Package Uniform Resource Locator (pURL) object size cannot exceed 100 MB.
- The image must contain 500 or fewer pURLs.
- Up to 1,200 API requests per minute (20 QPS) per consumer project for all methods within the artifact guard service.
- SLO: Artifact evaluation takes about two minutes.
Troubleshooting
This section outlines common errors along with how to resolve them.
CreateConnector failures
| Field | Required/Optional | Constraints |
|---|---|---|
name |
Required | Format: Must match the regular expression
[a-zA-Z0-9\\-\\s_]+$ Match one or more of the following:
Max Length: 64 characters. |
pipeline_type |
Required | Must be one of the following enum values:
|
description |
Optional | Must not exceed 256 characters. |
display_name |
Optional | Must not exceed 256 characters. |
Other errors
The following table outlines some common errors and how to resolve them.
| Error Message | Cause | Action/Resolution |
|---|---|---|
Permission artifactscanguard.connectors.create
denied on resource |
The user or service account lacks the artifactscanguard.connectors.create
IAM permission on the resource (project, folder, or
organization). |
Grant the caller an IAM role that includes the
artifactscanguard.connectors.create permission. |
status.ErrFailedPrecondition |
Onboarding might be in progress even if the Google Cloud console shows the service as enabled. | Report an issue to the support team. |
status.ErrInvalidArgument |
Field validation failures | Ensure the CreateConnector request meets the specified
constraints. |