Skip to content

GitLab CI template for Dependency Track

This project implements a GitLab CI/CD template to collect and send your SBOM reports to a Dependency Track server.

Usage

This template can be used both as a CI/CD component or using the legacy include:project syntax.

Use as a CI/CD component

Add the following to your gitlab-ci.yml:

include:
  # 1: include the component
  - component: gitlab.com/to-be-continuous/dependency-track/gitlab-ci-dependency-track@1.3.0
    # 2: set/override component inputs
    inputs:
      # ⚠ this is only an example
      base-api-url: "https://dependency-track.my-company.org/api"

Use as a CI/CD template (legacy)

Add the following to your gitlab-ci.yml:

include:
  # 1: include the template
  - project: "to-be-continuous/dependency-track"
    ref: "1.1.0"
    file: "/templates/gitlab-ci-dependency-track.yml"

variables:
  # 2: set/override template variables
  # ⚠ this is only an example
  DEPTRACK_BASE_API_URL: "https://dependency-track.my-company.org/api"

Understanding the Dependency Track template

The template will be triggered at each pipeline execution on your production branch (main or master by default) and each release pipeline (upon semver tag creation).

It will scan for all SBOM file in your project structure and will upload them to the configured Dependency Track server.\ ℹ SBOM files should have been generated in the upstream pipeline with appropriate tools, and propagated as build artifacts. Most to-be-continuous templates already support - whenever possible - a job to produce a SBOM report.

API Key permissions

In order to operate, this template needs a Dependency Track API Key. Here are some details about mandatory and optional permissions used by the template:

Permission Required? Explaination
BOM_UPLOAD mandatory Required to publish SBOM files to the Dependency Track server
PROJECT_CREATION_UPLOAD optional This is required if you want to automatically create the project while uploading the SBOM files when the project does not exist (but the parent project must exist)
VIEW_VULNERABILITY and VIEW_PORTFOLIO optional Required if you want to display found vulnerabilities after SBOM analysis.
Granting those permissions without enabling Portfolio ACLs is not recommended in the general case as it gives a read access to all projects
VIEW_PORTFOLIO and PORTFOLIO_MANAGEMENT optional Required if you want to automatically create one or several project ancestors prior to uploading the SBOM files.
Granting those permissions is not recommended in the general case as they virtually give administration rights to the API Key owner

Multiple SBOM strategy

When collecting several SBOM files in a project, this template supports two strategies:

  • publish each SBOM independently in separate projects (default),
  • or merge all SBOM files and publish the result into a single project.

The merge strategy can be simply enabled by setting the merge / $DEPTRACK_MERGE configuration to true (see below).

Project Path

Whenever a SBOM file is published, the template uploads it to the Dependency Track server under a certain project.\ The target project is determined by evaluating the project-path input / $DEPTRACK_PROJECT_PATH variable (see configuration chapter).

The project path is a sequence of elements separated by double slashes // (the separator is also configurable with the path-separator input / $DEPTRACK_PATH_SEPARATOR variable).\ Each element is expected to be one of the following:

  1. #11111111-1111-1111-1111-111111111111: a project Universally Unique Identifier (UUID) (starting with a hash #)
  2. project-name@version: a project name and a version (separated with a @)
  3. project-name: a project name only (empty version)

Lastly, the project path supports some expressions, that will be dynamically replaced when being evaluated:

Expression Value for separate SBOM files Value for merged SBOM
{file_prefix} SBOM filename prefix (before the first dot).
Ex: when processing the file reports/docker-sbom.cyclonedx.json, {file_prefix} will be docker-sbom.
merged
{sbom_name} Metadata > Component > Name info extracted from the SBOM file (json or xml) unk
{sbom_version} Metadata > Component > Version info extracted from the SBOM file (json or xml) empty string
{sbom_type} Metadata > Component > Type info extracted from the SBOM file (json or xml) unk

Default project path

By default, the Dependency Track project path is set to $CI_PROJECT_NAMESPACE//$CI_PROJECT_PATH-{file_prefix}@$CI_COMMIT_REF_NAME (with path separator //).

That means:

  • The project structure in Dependency Track will always be two levels deep:
  • a root project bearing the name of the GitLab project namespace,
  • and leaf projects (hosting SBOM files) bearing the full path of the GitLab project as a name, suffixed with the SBOM file prefix and with project version matching either the Git branch name or the Git tag name (depending on the kind of pipeline that originated the SBOM file).
  • If the API key has only the BOM_UPLOAD permission, then all the projects hierarchy must pre-exist down to the leaf sub-projects.
  • If the API key has the extra PROJECT_CREATION_UPLOAD permission, then only the root project must pre-exist, the template will automatically create the leaf projects if not found.
  • If the API key has the extra VIEW_PORTFOLIO and PORTFOLIO_MANAGEMENT permissions (not recommanded), then the entire project hierarchy will be automatically created by the template if it doesn't exist.

Example: Let's imagine a GitLab project located in acme-program/acme-services/acme-user-api with 2 SBOM files generated by the pipeline:

  • py-sbom.cyclonedx.json: the SBOM of the Python application implementation, with version 1.1.0
  • docker-sbom.cyclonedx.json: the SBOM of the container image, with version main

Lastly, let's suppose the project production branch is main, and 2 software versions have been released so far: 1.0.0 and 1.1.0.

The corresponding Dependency Track project structure with separate SBOMs strategy will be:

| Project name                                              | Version |
| --------------------------------------------------------- | ------- |
| 📂 acme-program/acme-services                             |         |
| ├─📄 acme-program/acme-services/acme-user-api-py-sbom     | main    |
| ├─📄 acme-program/acme-services/acme-user-api-py-sbom     | 1.0.0   |
| ├─📄 acme-program/acme-services/acme-user-api-py-sbom     | 1.1.0   |
| ├─📄 acme-program/acme-services/acme-user-api-docker-sbom | main    |
| ├─📄 acme-program/acme-services/acme-user-api-docker-sbom | 1.0.0   |
| ├─📄 acme-program/acme-services/acme-user-api-docker-sbom | 1.1.0   |
| ...

The corresponding Dependency Track project structure with merged SBOMs strategy will be:

| Project name                                         | Version |
| ---------------------------------------------------- | ------- |
| 📂 acme-program/acme-services                        |         |
| ├─📄 acme-program/acme-services/acme-user-api-merged | main    |
| ├─📄 acme-program/acme-services/acme-user-api-merged | 1.0.0   |
| ├─📄 acme-program/acme-services/acme-user-api-merged | 1.1.0   |
| ...

💡 this default configuration handles the Dependency Track constraint that each project name has to be globally unique in the server.

Other use cases

The default template behavior can be changed by overridding the project-path input / $DEPTRACK_PROJECT_PATH variable.

Examples:

  • #550e8400-e29b-41d4-a716-446655440000: every SBOM found will be published to the project with UUID 550e8400-e29b-41d4-a716-446655440000
    ℹ as Dependency Track is only able to store one SBOM per project, this configuration is suitable only if exactly one SBOM file is found (otherwise each one will overwrite the previous one)
  • my-project@v1.1.0: every SBOM found will be published to the project with name my-project and version v1.1.0
    ℹ depending on your API key permissions, sbom-scanner might try to automatically create the project if it doesn't exist
    ℹ as in the previous example, this configuration is suitable only if exactly one SBOM file is found
  • #550e8400-e29b-41d4-a716-446655440000/my-project-{file_prefix}@{sbom_version}: every SBOM found will be published to a project named my-project-{file_prefix} and version {sbom_version} (extracted from the SBOM file), direct child of project with UUID 550e8400-e29b-41d4-a716-446655440000
    ℹ depending on your API key permissions, sbom-scanner might try to automatically create the project if it doesn't exist
  • acme-program@v2/acme-services@v1.3/acme-user-api@v1.3/acme-user-api-{file_prefix}: complete project path only defined by project names and versions
    ℹ depending on your API key permissions, sbom-scanner might try to automatically create the project and its ancestors if they don't exist

Configuration

The Dependency Track template uses the following configuration.

Input / Variable Description Default value
sbom-scanner-image / DEPTRACK_SBOM_SCANNER_IMAGE The container image with Dependency Track SBOM Scanner tool registry.gitlab.com/to-be-continuous/tools/dt-sbom-scanner:latest
base-api-url / DEPTRACK_BASE_API_URL Dependency Track server base API url (includes /api) none (required)
🔒 DEPTRACK_API_KEY Dependency Track API key none (required)
project-path / DEPTRACK_PROJECT_PATH Dependency Track target project path to publish SBOM files to $CI_PROJECT_NAMESPACE//$CI_PROJECT_PATH-{file_prefix}@$CI_COMMIT_REF_NAME
path-separator / DEPTRACK_PATH_SEPARATOR Separator to use in project path //
sbom-patterns / DEPTRACK_SBOM_PATTERNS SBOM file patterns to publish (supports glob patterns) **/*.cyclonedx.json **/*.cyclonedx.xml
merge / DEPTRACK_MERGE Merge all SBOM files into one (default false) none (disabled)
merge-output / DEPTRACK_MERGE_OUTPUT Output merged SBOM file (only used with merge enabled) - for debugging purpose reports/deptrack-merged.cyclonedx.json
purl-max-len / DEPTRACK_PURL_MAX_LEN PURLs max length (-1: auto, 0: no trim, >0: trim to size - default: -1) -1 (auto)
show-findings / DEPTRACK_SHOW_FINDINGS Wait for analysis and display found vulnerabilities in logs none (disabled)

Secrets management

Here are some advices about your secrets (variables marked with a 🔒):

  1. Manage them as project or group CI/CD variables:
  2. masked to prevent them from being inadvertently displayed in your job logs,
  3. protected if you want to secure some secrets you don't want everyone in the project to have access to (for instance production secrets).
  4. Manage them using the Vault variant
  5. In case a secret contains characters that prevent it from being masked, simply define its value as the Base64 encoded value prefixed with @b64@: it will then be possible to mask it and the template will automatically decode it prior to using it.
  6. Don't forget to escape special characters (ex: $ -> $$).

Variants

Vault variant

This variant allows delegating your secrets management to a Vault server.

Configuration

In order to be able to communicate with the Vault server, the variant requires the additional configuration parameters:

Name Description Default value
TBC_VAULT_IMAGE The Vault Secrets Provider image to use (can be overridden) registry.gitlab.com/to-be-continuous/tools/vault-secrets-provider:master
VAULT_BASE_URL The Vault server base API url none
VAULT_OIDC_AUD The aud claim for the JWT $CI_SERVER_URL
🔒 VAULT_ROLE_ID The AppRole RoleID must be defined
🔒 VAULT_SECRET_ID The AppRole SecretID must be defined

Usage

Then you may retrieve any of your secret(s) from Vault using the following syntax:

@url@http://vault-secrets-provider/api/secrets/{secret_path}?field={field}

With:

Name Description
secret_path (path parameter) this is your secret location in the Vault server
field (query parameter) parameter to access a single basic field from the secret JSON payload

Example

include:
  # main template
  - project: 'to-be-continuous/dependency-track'
    ref: '1.3.0'
    file: '/templates/gitlab-ci-dependency-track.yml'
  # Vault variant
  - project: 'to-be-continuous/dependency-track'
    ref: '1.3.0'
    file: '/templates/gitlab-ci-dependency-track-vault.yml'

variables:
    # audience claim for JWT
    VAULT_OIDC_AUD: "https://vault.acme.host"
    # Secret managed by Vault
    DEPTRACK_API_KEY: "@url@http://vault-secrets-provider/api/secrets/b7ecb6ebabc231/runner/prod/deptrack?field=api-key"
    VAULT_BASE_URL: "https://vault.acme.host/v1"
    # $VAULT_ROLE_ID and $VAULT_SECRET_ID defined as a secret CI/CD variable