(STO/CI licenses) Create a build-scan-push pipeline
Launch pipeline builds and scans automatically based on GitLab merge requests.
This topic describes how to create an end-to-end pipeline that uses STO and CI steps to build an image and pushes it to Docker Hub only if the codebase and image contain no critical vulnerabilities. This pipeline uses two popular open-source tools:
Bandit, for scanning Python code.
Aqua Trivy, for scanning container images.
You can copy/paste the YAML pipeline example below into Harness and update it with your own infrastructure, connectors, and access tokens.
The following steps describe the workflow:
An STO Bandit step scans the codebase and ingests the scan results.
If the code has no critical vulnerabilities, a CI Build and Push step builds a test image and pushes it to Docker Hub.
An Aqua Trivy step scans the image and ingests the results.
If the image has no critical vulnerabilities, another Build and Push step pushes a prod image to Docker Hub.
Set up your codebase
This workflow uses Bandit to scan the target repository https://github.com/williamwissemann/dvpwa (specified in the Codebase for this pipeline).
Fork the following example repository into your GitHub account. This is a Python repo with known vulnerabilities: https://github.com/williamwissemann/dvpwa.
If you don't have a GitHub connector, do the following:
In your Harness project, select Project Setup > Connectors.
Select New Connector, then select Code Repositories > GitHub.
Set the GitHub connector settings as appropriate.
Use Account for the URL type.
This workflow uses Harness Cloud, so select Connect through Harness Platform when prompted for the connectivity mode.
Set up your pipeline
Do the following:
Select Security Testing Orchestration (left menu, top) > Pipelines > Create a Pipeline. Enter a name and click Start.
In the new pipeline, select Add stage > Build.
Set up your stage as follows:
Enter a Stage Name.
Select Third-party Git provider and then select your GitHub connector.
For Repository Name, click the type selector ("tack" button on the right) and then select Runtime Expression.
Expand Overview > Advanced and add the following stage variables.
You'll be specifying runtime inputs for some of these variables. This enables you to specify the code repo, branch, image label, and image tag, and other variables at runtime.
GITHUB_USERNAME— Select Secret as the type and enter your GitHub login name.GITHUB_REPO— Select String for the type and Runtime Input for the value (click the "tack button" to the right of the value field).GITHUB_BRANCH— Select String and Runtime Input.DOCKERHUB_USERNAME— Select String as the type and enter your DockerHub login name.DOCKER_IMAGE_LABEL— Select String and Runtime Input.DOCKER_IMAGE_TAG— Select String and Runtime Input.
In the Pipeline Editor, go to Infrastructure and select Cloud, Linux, and AMD64 for the infrastructure, OS, and architecture.
You can also use a Kubernetes or Docker build infrastructure, but these require additional work to set up. For more information, go to Supported Infrastructures documentation.
Add a Docker-in-Docker background step
Scan the code
In the Pipeline Studio, go to Execution and add a Bandit step to your pipeline.
Configure the step as follows:
Scan Mode = Orchestration
Target name — Click the value-type selector (tack button to the right of the input field) and select Expression. Then enter the following expression:
<+stage.variables.GITHUB_REPO>Target variant — Select Expression for the value type and enter the following:
<+stage.variables.GITHUB_BRANCH>When scanning a code repo, you generally want to specify the repo name and branch for the target.
type:Banditname:A name for the step.identifier:A unique step ID.spec :mode :orchestrationIn orchestrated mode, the step runs the scan and ingests the results in one step.config: defaulttarget :name : <+stage.variables.GITHUB_REPO>type : repositoryvariant : <+stage.variables.GITHUB_BRANCH>You will specify the target name and variant when you run the pipeline. When scanning a repository, you will generally use the repository name and branch for these fields.
advanced :log :level : info
Here's a YAML example:
Analyze the results
At this point, you might want to run a scan and view the detected issues.
Select Save, and then select Run.
In Run Pipeline, enter the repository and target settings you want to use. If you're using the dvpwa repository, enter the following:
Under Codebase:
Repository name : dvpwa
Branch name : master
Under Stage:
Target name : dvpwa (= the repo name)
Target variant : master (= the branch name)
Run the pipeline. When the execution finishes, select Vulnerabilities tab to view the scan results.
Build and push a test image
Harness CI includes a set of Build and Push steps that take a code repo with a Dockerfile, build a container image, and push it to an artifact repository.
You'll now add one of these steps to build and push to your Docker Hub account.
Add a Build and Push to Docker Registry step after the Bandit step.
Configure the step as follows:
Name = build_push_test_image
Docker Connector — Select your Docker Hub connector.
Docker Repository — Select Expression for the value type, then enter the following:
<+stage.variables.DOCKERHUB_USERNAME>/<+stage.variables.DOCKER_IMAGE_LABEL>Tags— Select Expression for the value type, then enter the following:
<+stage.variables.DOCKER_IMAGE_TAG>-scantest-DONOTUSE
Add a Build and Push to Docker Registry step after the Bandit step and configure it as follows:
name: build_push_test_imageidentifier:A unique step ID.spec :connectorRef : YOUR_DOCKERHUB_CONNECTORrepo : <+stage.variables.DOCKERHUB_USERNAME>/<+stage.variables.DOCKER_IMAGE_LABEL>type : containervariant : <+stage.variables.DOCKER_IMAGE_TAG>-scantest-DONOTUSE
Here's an example:
Scan the test image
Add an Aqua Trivy step to your pipeline after the build step and configure it as follows:
Scan Mode = Orchestration In orchestrated mode, the step runs the scan and ingests the results in one step.
Target name — Click the "tack" button on the right side of the input field and select Expression. Then enter the following expression:
<+stage.variables.DOCKERHUB_USERNAME>/<+stage.variables.DOCKER_IMAGE_LABEL>Target variant — Select Expression for the value type, then enter the following expression:
<+stage.variables.DOCKER_IMAGE_TAG>-scantest-DONOTUSEContainer image Type = V2
Domain =
docker.ioContainer image name — Select Expression for the value type, then enter the following expression:
<+stage.variables.DOCKERHUB_USERNAME>/<+stage.variables.DOCKER_IMAGE_LABEL>Container image tag — Select Expression for the value type, then enter the following expression:
<+stage.variables.DOCKER_IMAGE_TAG>Fail on Severity = Critical
Add an Aqua Trivy step to your pipeline after the build step and configure it as follows:
type:AquaTrivyname:A name for the step.identifier:A unique step ID.spec :mode :orchestrationIn orchestrated mode, the step runs the scan and ingests the results in one step.config: defaulttarget :name : <+stage.variables.DOCKERHUB_USERNAME>/<+stage.variables.DOCKER_IMAGE_LABEL>type : containervariant : <+stage.variables.DOCKER_IMAGE_TAG>When scanning an image, you generally use the image label and tag for the target name and variant .advanced :log :level : infofail_on_severity: critical
privileged: trueimage:type: docker_v2name: <+stage.variables.DOCKERHUB_USERNAME>/<+stage.variables.DOCKER_IMAGE_LABEL>access_token: <+secrets.getValue("YOUR_DOCKERHUB_ACCESS_TOKEN")domain: docker.iotag: <+stage.variables.DOCKER_IMAGE_TAG>-scantest-DONOTUSE
Here's an example:
Run the pipeline and verify your results
This is a good time to run your pipeline and verify that it can scan the image.
Click Run and set the GitHub and Docker variables.
If you forked the dvpwa repository repo into your GitHub account and want to use that, set the fields like this:
GITHUB_REPO= dvpwaGITHUB_BRANCH= masterDOCKER_IMAGE_LABEL= dvpwaDOCKER_IMAGE_TAG= 1.
With this setup, you'll build and push an image with a tag that looks like this:
1.x-scantest-DONOTUSEClick Run Pipeline and view the results in Vulnerabilities tab.
Build and push the prod image
Assuming that the Trivy scan detected no critical vulnerabilities, you can now build and push a prod version of your image to Docker Hub. This step is identical to the previous test image step, except for the image tag: the test image tag is 1.x-scantest-DONOTUSE and the prod image tag is 1.x.
Add a Build and Push to Docker Registry step after the Semgrep ingest step.
Configure the step as follows:
Name = build_push_test_image
Docker Connector — Select your Docker Hub connector.
Container image name — Select Expression for the value type, then enter the following expression:
<+stage.variables.DOCKERHUB_USERNAME>/<+stage.variables.DOCKER_IMAGE_LABEL>Container image tag — Select Expression for the value type, then enter the following expression:
<+stage.variables.DOCKER_IMAGE_TAG><+pipeline.sequenceID>
Add a Build and Push to Docker Registry step after the Bandit step and configure it as follows:
name: build_push_test_imageidentifier:A unique step ID.spec :connectorRef : YOUR_DOCKERHUB_CONNECTORrepo : <+stage.variables.DOCKERHUB_USERNAME>/<+stage.variables.DOCKER_IMAGE_LABEL>type : containertags :<+stage.variables.DOCKER_IMAGE_TAG><+pipeline.sequenceID>
Here's an example:
YAML pipeline example
Here's an example of the pipeline you created in this workflow. If you copy this example, replace the placeholder values with appropriate values for your project, organization, connectors, and access token.
Last updated
Was this helpful?