Skip to content

Dependency-Track: Update portfolio

The Dependency-Track portfolio page provides a comprehensive overview of all projects, primarily applications, that are being tracked for security vulnerabilities and outdated dependencies. This page serves as a crucial tool for identifying which projects contain vulnerabilities that need to be addressed and for monitoring dependencies that require updates to maintain application security and compliance. There is this page in Dependency Track where you can see all projects ( by projects we mean mostly applications ) which have vulnerabilities and needs to be fixed, dependency track is to follow which dependencies are out of track and needs to be updated

[

dependency-track.attracs.com

https://dependency-track.attracs.com/projects

](https://dependency-track.attracs.com/projects)

The image below is a visual aid showing the Dependency-Track portfolio page and how projects are listed for tracking and management: To streamline the process of updating dependencies and managing vulnerabilities across these projects, we have developed automation scripts. These scripts help automate the update process, ensuring consistency and saving time. The following script automates the process of updating project dependencies, generating Software Bill of Materials (SBOMs), and activating the new versions in Dependency-Track: This are the following scripts:

[

github.com

https://github.com/Attracs/docker-devops-dependency-track/blob/main/manual-runs/update_dtrack_portfolio.sh

](https://github.com/Attracs/docker-devops-dependency-track/blob/main/manual-runs/update_dtrack_portfolio.sh)

#!/usr/bin/env bash
set -euo pipefail

# ============================================================
# LOAD API KEY
# ============================================================
SCRIPT_DIR="$(cd \"$(dirname \"${BASH_SOURCE[0]}\")\" 2626 pwd)"

if [[ -z "${API_KEY:-}" ]]; then
  if [[ -f "$SCRIPT_DIR/vars.conf" ]]; then
    # shellcheck source=/dev/null
    source "$SCRIPT_DIR/vars.conf"
  else
    echo "ERROR: API_KEY not set and vars.conf not found"
    exit 1
  fi
fi

API_BASE="https://dependency-track-api.attracs.com/api/v1"

# ============================================================
# LOOP THROUGH ALL PROJECTS
# ============================================================
for PROJECT_PATH in "$SCRIPT_DIR"/projects/*; do
    [[ -d "$PROJECT_PATH/.git" ]] || continue

    PROJECT_NAME="$(basename "$PROJECT_PATH")"

    echo
    echo "=========================================="
    echo "Processing repository: $PROJECT_NAME"
    echo "=========================================="

    cd "$PROJECT_PATH"

    # ============================================================
    # GIT FETCH (SAFE)
    # ============================================================
    echo "Fetching branches and tags..."
    git fetch --all --tags --prune || {
        echo "WARNING: git fetch failed 2014 skipping $PROJECT_NAME"
        continue
    }

    # Fix shallow repo (CI-safe)
    if git rev-parse --is-shallow-repository | grep -q true; then
        echo "Repository is shallow 2014 unshallowing..."
        git fetch --unshallow --tags || true
    fi

    # ============================================================
    # DETERMINE NEWEST: TAG OR COMMIT
    # ============================================================
    HEAD_COMMIT="$(git rev-parse --short HEAD)"
    HEAD_TS="$(git log -1 --format=%ct HEAD)"

    LATEST_TAG="$(git tag --sort=-creatordate | head -n 1 || true)"
    TAG_TS=0

    if [[ -n "$LATEST_TAG" ]]; then
        TAG_TS="$(git log -1 --format=%ct "$LATEST_TAG")"
    fi

    if [[ "$TAG_TS" -gt "$HEAD_TS" ]]; then
        PROJECT_VERSION="${LATEST_TAG#v}"
        VERSION_SOURCE="tag ($LATEST_TAG)"
    else
        PROJECT_VERSION="$HEAD_COMMIT"
        VERSION_SOURCE="commit ($HEAD_COMMIT)"
    fi

    OUTPUT_FILE="$SCRIPT_DIR/${PROJECT_NAME}-${PROJECT_VERSION}-sbom.xml"

    echo "Version        : $PROJECT_VERSION"
    echo "Version source : $VERSION_SOURCE"
    echo "SBOM file      : $OUTPUT_FILE"

    # ============================================================
    # GENERATE SBOM
    # ============================================================
    syft dir:. \
      --source-name "$PROJECT_NAME" \
      --source-version "$PROJECT_VERSION" \
      -o cyclonedx-xml@1.5="$OUTPUT_FILE"

    cyclonedx validate \
      --fail-on-errors \
      --input-file "$OUTPUT_FILE" \
      --input-format xml

    # ============================================================
    # UPLOAD SBOM
    # ============================================================
    echo "Uploading SBOM..."
    curl -sS -X POST "$API_BASE/bom" \
      -H "X-Api-Key: $API_KEY" \
      -H "Content-Type: multipart/form-data" \
      -H "X-Alto-Is-Latest-Version: true" \
      -F "autoCreate=true" \
      -F "projectName=$PROJECT_NAME" \
      -F "projectVersion=$PROJECT_VERSION" \
      -F "projectClassifier=APPLICATION" \
      -F "bom=@$OUTPUT_FILE" \
      >/dev/null

    # ============================================================
    # ACTIVATE NEW VERSION / DEACTIVATE OLD
    # ============================================================
    PROJECTS_JSON="$(curl -sS -H "X-Api-Key: $API_KEY" "$API_BASE/project?name=$PROJECT_NAME")"

    NEW_UUID="$(echo "$PROJECTS_JSON" | jq -r --arg V "$PROJECT_VERSION" '.[] | select(.version==$V) | .uuid')"

    if [[ -z "$NEW_UUID" || "$NEW_UUID" == "null" ]]; then
        echo "ERROR: New version not found 2014 skipping activation"
        continue
    fi

    echo "$PROJECTS_JSON" | jq -r '.[] | "\(.uuid) \(.version)"' | while read -r UUID VERSION; do
        if [[ "$UUID" == "$NEW_UUID" ]]; then
            ACTIVE=true
            IS_LATEST=true
        else
            ACTIVE=false
            IS_LATEST=false
        fi
        curl -sS -X PATCH \
          -H "X-Api-Key: $API_KEY" \
          -H "Content-Type: application/json" \
          "$API_BASE/project/$UUID" \
          -d "{\"active\": $ACTIVE, \"isLatest\": $IS_LATEST}" \
          >/dev/null
    done

    echo "271 Finished $PROJECT_NAME"
done

echo
echo "271 All repositories processed successfully."

This script performs several key actions for each project:

- Determines whether the latest tag or the latest commit is newer, and uses that as the project version.

- Generates a Software Bill of Materials (SBOM) for the project.

- Uploads the SBOM to Dependency-Track.

- Deactivates all old versions in Dependency-Track, keeping only the latest version as active.

Note: Even though only the latest version is marked as active, you can still view older versions in Dependency-Track for reference and historical tracking.

In summary, this automation ensures that all repositories are processed successfully, keeping dependencies up to date and vulnerabilities under control.

Automating with GitHub Actions

After local testing and verifying that everything works as expected, this automation is set up to run automatically using GitHub Actions. The workflow is scheduled to execute every Friday at 00:00, ensuring all projects are updated regularly. Below is the GitHub Actions workflow script used to automate this process:

[

github.com

https://github.com/Attracs/docker-devops-dependency-track/blob/main/.github/workflows/update-dtrack.yml

](https://github.com/Attracs/docker-devops-dependency-track/blob/main/.github/workflows/update-dtrack.yml)

name: Dependency-Track

on:
  schedule:
    - cron: '0 0 * * 5'   # Every Friday at midnight UTC
  workflow_dispatch:
  push:
    tags:
      - 'v*'

jobs:
  dtrack:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout repository
        uses: actions/checkout@v4
        with:
          fetch-depth: 0
          fetch-tags: true

      - name: Install system dependencies
        run: |
          sudo apt-get update
          sudo apt-get install -y jq curl unzip git

      - name: Install Syft
        uses: anchore/sbom-action/download-syft@v0

      - name: Install CycloneDX CLI
        run: |
          curl -Lo cyclonedx-cli \
            https://github.com/CycloneDX/cyclonedx-cli/releases/download/v0.29.1/cyclonedx-linux-x64
          chmod +x cyclonedx-cli
          sudo mv cyclonedx-cli /usr/local/bin/cyclonedx

      - name: Make scripts executable
        working-directory: manual-runs
        run: chmod +x *.sh

      - name: Clone / update repositories
        working-directory: manual-runs
        env:
          GH_TOKEN: ${{ secrets.GH_TOKEN }}
        run: ./clone_repos.sh

      - name: Run Dependency-Track portfolio update
        working-directory: manual-runs
        env:
          API_KEY: ${{ secrets.DTRACK_API_KEY }}
          GH_TOKEN: ${{ secrets.GH_TOKEN }}
        run: ./update_dtrack_portfolio.sh

This workflow ensures that the update process is performed automatically on a weekly basis, keeping all tracked projects up to date in Dependency-Track. This automation is executed every Friday, so that by Monday meetings, developers can review and address the latest vulnerabilities that need to be fixed.