(STO license) 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 builds an image and pushes it to Docker Hub only if the codebase and image contain no critical vulnerabilities. This pipeline uses two free tools:
Semgrep, a popular SAST tool for detecting vulnerabilities in application code. Semgrep can scan a wide variety of languages and includes a free version for individuals who want to scan files locally.
Aqua Trivy, a popular open-source tool for scanning container images.
Once you complete this workflow, you'll have a complete end-to-end pipeline that you can easily adapt to a wide variety of use cases. You can also 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:
A Run step scans the codebase using Semgrep and saves the results to a SARIF file.
A Semgrep step ingests the scan results (ingestion-only workflow).
If the code has no critical vulnerabilities, another Run steps builds the image.
An Aqua Trivy step scans the image and ingests the results (orchestration workflow).
If the image has no critical vulnerabilities, another Run step pushes the image to Docker Hub.

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 > Security.
Set up your stage as follows:
Enter a Stage Name.
Disable Clone Codebase. You will add a Run step to clone the codebase later.
In the Pipeline Editor, go to Overview and add the following shared path:
/shared/customer-artifactsYou'll use this shared folder to store the code repo so that all steps can access it.
Go to Overview and add the following Shared Path:
/shared/scan_resultsExpand 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_PAT— Select Secret as the type and then select the Harness secret for your GitHub access token.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.DOCKERHUB_PAT— Select Secret as the type and then select the Harness secret for your Docker Hub access token.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
The following use cases require a Docker-in-Docker background step in your pipeline:
Container image scans on Kubernetes and Docker build infrastructures
Required for Orchestration and Dataload scan modes
Custom Scan steps on Kubernetes and Docker build infrastructures
Required for all target types and Orchestration/DataLoad modes
The following use cases do not require a Docker-in-Docker background step:
Harness Cloud AMD64 build infrastructures
SAST/DAST/configuration scans that use a scanner-specific step and not a Custom Scan step.
Ingestion scans where the data file has already been generated
Add the codebase scan step
Now you will add a step that runs a scan using the local Semgrep container image maintained by Harness.
Go to Execution and add a Run step.
Configure the step as follows:
Name = run_semgrep_scan
Command =
Open Optional Configuration and set the following options:
Container Registry — When prompted, select Account and then
Harness Docker Connector. The step uses this connector to download the scanner image.Image = returntocorp/semgrep
Add the following environment variable:
Key :
SEMGREP_APP_TOKENValue : Click the type selector (right), set the value type to Expression, and enter the value
<+secrets.getValue("YOUR_SEMGREP_TOKEN_SECRET")>.
set the value type
Add a Run step to your SecurityTests stage and configure it as follows:
type:Runname:A name for the step.identifier:A unique step ID.spec :connectorRef : account.HarnessImageThis is a connector to the Harness image registry. The step uses this connector to download the scanner image.
image : returntocorp/semgrepshell : Shcommand : |-envVariables:SEMGREP_APP_TOKEN: <+secrets.getValue("YOUR_SEMGREP_TOKEN_SECRET")>
Here's an example:
Add the Semgrep ingest step
Now that you've added a step to run the scan, it's a simple matter to ingest it into your pipeline. Harness provides a set of customized steps for popular scanners such as Semgrep.
In Execution, add a Semgrep step after your Run step.
Configure the step as follows:
Name =
ingest_semgrep_dataType = Repository
Under Target:
Name = Select Runtime Input as the value type.
Variant = Select Runtime Input as the value type.
Ingestion File =
/shared/scan_results/semgrep.sarif
Add a step after the Run step and configure it as follows:
type:Semgrepname:A name for the step.identifier:A unique step ID.spec :mode :ingestionconfig: defaulttarget :name : <+stage.variables.GITHUB_REPO>type : repositoryvariant : <+stage.variables.GITHUB_BRANCH>When scanning a repository, you will generally use the repository name and branch for the target name and variant.
advanced :log :level : infofail_on_severity: none
ingestion :file : /shared/scan_results/semgrep.sarif
Here's a YAML example:
Run the pipeline and verify your results
This is a good time to run your pipeline and verify that it can scan the repo and ingest the results correctly.
Click Run and set the
GITHUB_REPOandGITHUB_BRANCHvariables. (You don't need to set the image 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= master
Click Run Pipeline and wait for the execution to finish. You can then view your scan results in Vulnerabilities tab.
Add the image build step
Assuming that the Semgrep scanner detected no critical vulnerabilities, the next step is to build a local image using the Dockerfile in your codebase.
Add a Run step after the Semgrep ingest step.
Configure the step as follows:
Name = build_local_image
Command =
Open Optional Configuration and set the following options:
Container Registry — When prompted, select Account and then your Docker Hub connector.
Image = docker
Add a Run step and configure it as follows:
type:Runname:A name for the step.identifier:A unique step ID.spec :connectorRef : YOUR_DOCKERHUB_CONNECTORimage : returntocorp/semgrepshell : Shcommand : |-envVariables:SEMGREP_APP_TOKEN: <+secrets.getValue("YOUR_SEMGREP_TOKEN_SECRET")>
Here's an example:
Add the Aqua-Trivy scan/ingest step
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>Container image Type = Local Image
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>Fail on Severity = None
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: local_imagename: <+stage.variables.DOCKERHUB_USERNAME>/<+stage.variables.DOCKER_IMAGE_LABEL>tag: <+stage.variables.DOCKER_IMAGE_TAG>
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 repo and ingest the results correctly.
Click Run and set the GitHub and Docker variables. (You don't need to set the image 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= master-scantest-DONOTUSE
Click Run Pipeline and wait for the execution to finish. You can then view your scan results, for both the repo and the image, in Vulnerabilities tab.
Add the image push step
Assuming that the Aqua Trivy scanner detected no critical vulnerabilities, you can now push your new image to Docker Hub.
Add a Run step after the Aqua Trivy scan/ingest step.
Configure the step as follows:
Name = push_image
Command =
Open Optional Configuration and set the following options:
Container Registry — Select your Docker Hub connector.
Image = docker
Add a Run step after the Bandit scan step and configure it as follows:
type:Runname:A name for the step.identifier:A unique step ID.spec :connectorRef : MY_DOCKERHUB_CONNECTORimage : dockershell : Shcommand : |-
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, and connectors.
Last updated
Was this helpful?