> For the complete documentation index, see [llms.txt](https://developer.harness.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://developer.harness.io/security-testing-orchestration/troubleshooting-and-resources/sto-use-cases/set-up-sto-pipelines/create-custom-scan-images.md).

# Create custom scanner images

Harness maintains a [set of public scanner images](https://console.cloud.google.com/artifacts/docker/gar-prod-setup/us/harness-public) for popular tools such as Semgrep, OWASP, SonarQube, Snyk, and Veracode. Harness seeks to keep these images as small and as lightweight as possible, and to minimize the number of vulnerabilities in each image. This means that you might want to extend an image with additional layers to scan a specific type of target. You can easily add packages such as Node, Ruby, and Maven to a scanner image and then run the custom container in STO.

#### Important notes <a href="#important-notes" id="important-notes"></a>

* This topic assumes that you are familiar with containerization, Dockerfiles, and [best practices](https://docs.docker.com/develop/develop-images/dockerfile_best-practices/) for building container images.
* Harness supports the CI and STO images in the [Harness project on GAR](https://console.cloud.google.com/artifacts/docker/gar-prod-setup/us/harness-public). You can extend these images to support your own uses cases, but custom images are not supported by Harness.
* Harness recommends that you add only the packages and files required for your specific use case, and that you thoroughly test and scan your custom images for vulnerabilities and other issues before you deploy them in your production environment.
* Harness [updates its public CI and STO images](/security-testing-orchestration/troubleshooting-and-resources/sto-use-cases/set-up-sto-pipelines/sto-images.md) every two weeks. It is good practice to rebuild your custom images every month to use the most recent base images.

### Workflow description <a href="#workflow-description" id="workflow-description"></a>

The following steps describe the general workflow.

1. Create a Dockerfile that specifies the base Harness image and the packages and files that you want to.
2. Build your custom image and push it to your image registry.
3. In your pipeline, add a Run step that uses your customized image to scan the target and save the results in a shared folder.
4. Add a Security or Security Test step to ingest the results.

### Hands-on example: add Yarn and PNPM to an OWASP image <a href="#hands-on-example-add-yarn-and-pnpm-to-an-owasp-image" id="hands-on-example-add-yarn-and-pnpm-to-an-owasp-image"></a>

Suppose you're a security engineer and you want to start using STO and [OWASP Dependency Check](https://owasp.org/www-project-dependency-check/) to scan and validate the code repositories maintained by your organization. One development team uses [Yarn](https://yarnpkg.com/) and another team uses [PNPM](https://pnpm.io/). Your initial scans are failing because the base image doesn't include these packages.

The following steps illustrate how to create an OWASP image that you can use to scan repositories that use Yarn AND repositories that use PNPM. (OWASP assumes that a scanned repo uses one package manager only. A scan will fail if the repo includes files associated with different managers.)

#### Extend the base image with Yarn and PNPM <a href="#extend-the-base-image-with-yarn-and-pnpm" id="extend-the-base-image-with-yarn-and-pnpm"></a>

The first step is to create a Dockerfile that adds these packages to the OWASP base image.

<details>

<summary>Dockerfile example</summary>

```
# STEP 1 <a href="#step-1" id="step-1"></a>
# Specify the base STO scanner image <a href="#specify-the-base-sto-scanner-image" id="specify-the-base-sto-scanner-image"></a>
# For a list of all images in the Harness Container Registry, run the following: <a href="#for-a-list-of-all-images-in-the-harness-container-registry-run-the-following" id="for-a-list-of-all-images-in-the-harness-container-registry-run-the-following"></a>
# curl -X  GET https://app.harness.io/registry/_catalog <a href="#curl-x-get-httpsappharnessioregistrycatalog" id="curl-x-get-httpsappharnessioregistrycatalog"></a>

FROM harness/owasp-dependency-check-job-runner:latest as scanner

RUN apt-get update && apt-get install -y \
  ca-certificates \
  curl


# STEP 2 <a href="#step-2" id="step-2"></a>
# Add the packages and files you need to the image <a href="#add-the-packages-and-files-you-need-to-the-image" id="add-the-packages-and-files-you-need-to-the-image"></a>

# Install sudo <a href="#install-sudo" id="install-sudo"></a>
RUN apt install sudo

# Install npm <a href="#install-npm" id="install-npm"></a>
# https://askubuntu.com/questions/720784/how-to-install-latest-node-inside-a-docker-container <a href="#httpsaskubuntucomquestions720784how-to-install-latest-node-inside-a-docker-container" id="httpsaskubuntucomquestions720784how-to-install-latest-node-inside-a-docker-container"></a>
RUN apt update
RUN printf 'y\n1\n\1n' | apt install nodejs
RUN apt install -y npm

# Make sure Node is up-to-date <a href="#make-sure-node-is-up-to-date" id="make-sure-node-is-up-to-date"></a>
# https://askubuntu.com/questions/426750/how-can-i-update-my-nodejs-to-the-latest-version <a href="#httpsaskubuntucomquestions426750how-can-i-update-my-nodejs-to-the-latest-version" id="httpsaskubuntucomquestions426750how-can-i-update-my-nodejs-to-the-latest-version"></a>
RUN sudo npm cache clean -f
RUN sudo npm install -y -g n
RUN sudo n stable


# Install yarn <a href="#install-yarn" id="install-yarn"></a>
# https://linuxize.com/post/how-to-install-yarn-on-ubuntu-20-04/ <a href="#httpslinuxizecomposthow-to-install-yarn-on-ubuntu-20-04" id="httpslinuxizecomposthow-to-install-yarn-on-ubuntu-20-04"></a>
RUN curl -o- -L https://yarnpkg.com/install.sh | bash
RUN sudo apt install yarn -y
ENV PATH="/root/.yarn/bin:$PATH"

# Install pnpm <a href="#install-pnpm" id="install-pnpm"></a>
# https://vsys.host/how-to/how-to-install-pnpm-on-ubuntu-22-04 <a href="#httpsvsyshosthow-tohow-to-install-pnpm-on-ubuntu-22-04" id="httpsvsyshosthow-tohow-to-install-pnpm-on-ubuntu-22-04"></a>
# https://github.com/pnpm/pnpm/issues/5103 <a href="#httpsgithubcompnpmpnpmissues5103" id="httpsgithubcompnpmpnpmissues5103"></a>
RUN SHELL="bash:$SHELL"
RUN wget -qO- https://get.pnpm.io/install.sh | ENV="$HOME/.bashrc" SHELL="$(which bash)" bash -
RUN npm install -g pnpm
ENV PATH="~/.local/share/pnpm:$PATH"

```

</details>

#### Build and push the customized image <a href="#build-and-push-the-customized-image" id="build-and-push-the-customized-image"></a>

Once you're satisfied with the customized image, you can push it to your image registry.

![Custom image in private registry](/files/eufySRH8nW1MAZYRwuLD)

#### Add a shared folder to the pipeline stage <a href="#add-a-shared-folder-to-the-pipeline-stage" id="add-a-shared-folder-to-the-pipeline-stage"></a>

Now you're ready to set up your pipeline. First, you add a shared path to the stage for the scan results. This is a standard good practice for [ingestion workflows](/security-testing-orchestration/use-sto/sto-scanner-configuration/snyk/snyk-open-source.md#snyk-open-source-scan---ingestion-mode).

{% tabs %}
{% tab title="Visual" %}

1. In your Harness pipeline, go to the stage where you want to run the scan.
2. Select **Overview** and then add a shared path such as `/shared/scan_results`.

   <figure><img src="/files/3O3Ne8t0NiorY3OUtaR5" alt=""><figcaption><p>Add shared path for scan results</p></figcaption></figure>

{% endtab %}

{% tab title="YAML" %}
Go to the stage where you want to run the scan. Then add a shared path as shown in the following YAML.

```yaml

- stage:
    name: owasp-scan
    identifier: owaspscanwithbinaries
    description: ""
    type: SecurityTests
    spec:
      sharedPaths:
        - /shared/scan_results

```

{% endtab %}
{% endtabs %}

#### Add a Run step to scan the target with the custom image <a href="#add-a-run-step-to-scan-the-target-with-the-custom-image" id="add-a-run-step-to-scan-the-target-with-the-custom-image"></a>

Now add a Run step that pulls your custom image, runs the `dependency-check` binary on the specified target, and saves the scan results to the shared folder you created.

{% tabs %}
{% tab title="Visual" %}
Add a **Run** step and configure it as follows:

1. **Optional Configuration** > **Container Registry**: A connector to the registry where you stored your custom OWASP image.
2. **Optional Configuration** > **Image**: Your custom image name and tag, such as `myimageregistry/owaspcustom:latest`
3. **Command**: The [`dependency-check`](https://jeremylong.github.io/DependencyCheck/dependency-check-cli/arguments.html) CLI command and arguments, as well as any other commands you want to run.

   The following `dependency-check` arguments are required in this case:

   * `--scan /harness`
   * `--format JSON`
   * `--out <scan_results_output_path_and_filename>`
   * `--yarn <path_to_yarn>`
   * `--pnpm <path_to_pnpm>`

   Here's an example:

   ```yaml

    command: |-
         yarn --version
         pnpm --version 
         /app/dependency-check/bin/dependency-check.sh \
            --yarn /root/.yarn/bin/yarn \
            --pnpm /usr/local/bin/pnpm \
            --scan /harness \
            --format JSON  \ 
            --out /shared/scan_results/owasp.json 

   ```

   <figure><img src="/files/55QK8zW5qnEGmVbLoye8" alt=""><figcaption><p>Add Run step for custom image</p></figcaption></figure>

{% endtab %}

{% tab title="YAML" %}
Add a `Run` step and configure it as follows:

1. `connectorRef:` A connector to the registry where you stored your custom OWASP image.
2. `image:` Your custom image name and tag, such as `myimageregistry/owaspcustom:latest`.
3. `command:` The [`dependency-check`](https://jeremylong.github.io/DependencyCheck/dependency-check-cli/arguments.html) CLI command and arguments, as well as any other commands you want to run.

   The following `dependency-check` arguments are required in this case:

   * `--scan /harness`
   * `--format JSON`
   * `--out <scan_results_output_path_and_filename>`
   * `--yarn <path_to_yarn>`
   * `--pnpm <path_to_pnpm>`

Here's an example:

```yaml

- step:
    type: Run
    name: owasp-scan-with-binaries
    identifier: owasp-scan-with-binaries
    spec:
    connectorRef: YOUR_IMAGE_REGISTRY_CONNECTOR_ID
    image: YOUR_IMAGE_REGISTRY_NAME/owaspandnpmandyarn:latest
    shell: Sh
    command: |-
        yarn --version
        pnpm --version
        /app/dependency-check/bin/dependency-check.sh \
            --yarn /root/.yarn/bin/yarn \
            --pnpm /usr/local/bin/pnpm \
            --scan /harness \
            --prettyPrint \
            --format JSON  \
            --out /shared/scan_results/output.json 
    imagePullPolicy: Always

```

{% endtab %}
{% endtabs %}

#### Add an OWASP ingest step <a href="#add-an-owasp-ingest-step" id="add-an-owasp-ingest-step"></a>

Finally, add an **OWASP** step to ingest the scan results from the shared folder.

{% tabs %}
{% tab title="Visual" %}
Set the **Scan Mode** to **Ingestion** and specify the **Ingestion File** in your shared folder.

<figure><img src="/files/nirmleHYT1vUBgbRzQjC" alt=""><figcaption><p>OWASP step using Ingestion mode</p></figcaption></figure>
{% endtab %}

{% tab title="YAML" %}
Set the `mode:` to `ingestion` and specify the `ingestion : file` in your shared folder.

```yaml

- step:
    type: Owasp
    name: owasp_ingest_results
    identifier: Owasp_2
    spec:
    mode: ingestion
    config: default
    target:
        type: repository
        detection: auto
    advanced:
        log:
        level: info
    ingestion:
        file: /shared/scan_results/output.json


```

{% endtab %}
{% endtabs %}

#### Example pipeline <a href="#example-pipeline" id="example-pipeline"></a>

This example pipeline has three steps.

* `owasp_scan_oob` and `owasp_scan_with_binaries` run in parallel:
  * `owasp_scan_oob` uses the [OWASP Dependency-Check step](/security-testing-orchestration/use-sto/sto-scanner-configuration/owasp-scanner-reference.md) out-of-the box in orchestration mode. The step fails when trying to scan a repository with Yarn or PNPM because it doesn't have the required binaries. This step has a failure strategy to ignore all errors so it doesn't stop the pipeline.
  * `owasp_scan_with_binaries` uses the [custom OWASP image](#extend-the-base-image-with-yarn-and-pnpm) that includes the binaries required to scan Yarn and PNPM repositories.
* `owasp_ingest_results` uses the [OWASP Dependency-Check step](/security-testing-orchestration/use-sto/sto-scanner-configuration/owasp-scanner-reference.md) to ingest the scan results that `owasp_scan_with_binaries` published to the shared folder.

  <figure><img src="/files/prxVwJAqoGqru0Z7GFaP" alt=""><figcaption><p>Example pipeline with out-of-the-box OWASP orchestration step (fails) and Run step with custom OWASP image (succeeds)</p></figcaption></figure>

If you copy this example, replace the placeholder values with appropriate values for your [code repo connector](/continuous-integration/use-harness-ci/use-harness-ci/codebase-configuration/create-and-configure-a-codebase.md#code-repo-connectors), [image registry connector](/harness-ai/use-harness-platform/connectors/cloud-providers/ref-cloud-providers.md), and other applicable values. Depending on your project and organization, you might also need to replace `projectIdentifier` and `orgIdentifier`.

<details>

<summary>Example pipeline YAML</summary>

```yaml
pipeline:
  projectIdentifier: default
  orgIdentifier: default
  identifier: owaspyarnpnpmtestFINAL
  name: owasp-yarn-pnpm-test-FINAL
  tags: {}
  stages:
    - stage:
        name: owasp-scan
        identifier: owaspscan
        description: ""
        type: SecurityTests
        spec:
          cloneCodebase: true
          caching:
            enabled: true
            paths: []
          platform:
            os: Linux
            arch: Amd64
          runtime:
            type: Cloud
            spec: {}
          execution:
            steps:
              - parallel:
                  - step:
                      type: Owasp
                      name: owasp_scan_oob
                      identifier: Owasp_1
                      spec:
                        mode: orchestration
                        config: default
                        target:
                          type: repository
                          detection: auto
                        advanced:
                          log:
                            level: info
                          args:
                            cli: "--yarn /yarndir/yarn-v1.22.19/bin/yarn --log /yarndir/log.log"
                      failureStrategies:
                        - onFailure:
                            errors:
                              - AllErrors
                            action:
                              type: Ignore
                  - step:
                      type: Run
                      name: owasp-scan-with-binaries
                      identifier: Run_1
                      spec:
                        connectorRef: YOUR_IMAGE_REGISTRY_CONNECTOR_ID
                        image: YOUR_IMAGE_REGISTRY_NAME/YOUR_IMAGE_NAME:YOUR_IMAGE_TAG
                        shell: Sh
                        command: |-
                          yarn --version
                          pnpm --version
                          /app/dependency-check/bin/dependency-check.sh \
                               --yarn /root/.yarn/bin/yarn \
                               --pnpm /usr/local/bin/pnpm \
                               --scan /harness \
                               --prettyPrint \
                               --format JSON  \
                               --out /shared/scan_results/output.json 
                               # --project owasp_scan_with_yarn_and_pnpm
                               # --disableNodeAudit \     
                               # --enableExperimental \
                               # --noupdate\
                          # echo "SCAN RESULTS FILE ============================"
                          # cat /shared/scan_results/output.json
                        imagePullPolicy: Always
              - step:
                  type: Owasp
                  name: owasp_ingest_results
                  identifier: owasp_ingest_results
                  spec:
                    mode: ingestion
                    config: default
                    target:
                      type: repository
                      detection: auto
                    advanced:
                      log:
                        level: info
                    ingestion:
                      file: /shared/scan_results/output.json
          sharedPaths:
            - /shared/scan_results
            - /yarndir
          slsa_provenance:
            enabled: false
        failureStrategies:
          - onFailure:
              errors:
                - AllErrors
              action:
                type: Ignore
  properties:
    ci:
      codebase:
        connectorRef: YOUR_CODE_REPOSITORY_CONNECTOR_ID
        repoName: <+input>
        build: <+input>

```

</details>
