Commit ad096f32 authored by Christian Ceelen's avatar Christian Ceelen Committed by Pierre Smeyers
Browse files

feat(sbom): add SBOM generation support

parent 95f5a560
Loading
Loading
Loading
Loading
+25 −0
Original line number Diff line number Diff line
@@ -259,6 +259,31 @@ More info:
* [test coverage](https://docs.sonarsource.com/sonarqube-server/analyzing-source-code/test-coverage/test-coverage-parameters/#csharp) & [test execution](https://docs.sonarsource.com/sonarqube-server/analyzing-source-code/test-coverage/test-execution-parameters/#csharp) parameters
* [external analyzer reports](https://docs.sonarsource.com/sonarqube-server/analyzing-source-code/importing-external-issues/external-analyzer-reports/#external-dotnet-issues)

### `dotnet-sbom` job

This job creates a Software Bill Of Materials (SBOM) for the project, libraries and executables using [CycloneDX cdxgen](https://github.com/CycloneDX/cdxgen).

**Execution rules:**

- Disabled if `sbom-disabled` is set to `true`
- Runs always if `TBC_SBOM_MODE` is set to `always`
- Runs on release, integration, or production branches when `TBC_SBOM_MODE` is set to `onrelease` (default)
- Skipped in all other cases


| Input / Variable      | Description                              | Default value     |
| --------------------- | ---------------------------------------- | ----------------- |
| `sbom-disabled` / `DOTNET_SBOM_DISABLED` | Set to true to disable SBOM generation | `false`|
| `sbom-image` / `DOTNET_SBOM_IMAGE` | The container image to use for SBOM generation using [CycloneDX cdxgen](https://github.com/CycloneDX/cdxgen) | `ghcr.io/cyclonedx/cdxgen:master` |
| `sbom-supplier` / `DOTNET_SBOM_SUPPLIER` | The package supplier name to use in the generated SBOMs | `{CI_PROJECT_NAMESPACE}` |
| `sbom-opts` / `DOTNET_SBOM_OPTS` | Additional options to pass to the SBOM generation tool | `--fail-on-error --evidence --deep` |

The following SBOM reports are generated:

| Report         | Format                                                                       | Usage             |
| -------------- | ---------------------------------------------------------------------------- | ----------------- |
| `reports/dotnet-sbom-*.cyclonedx.json` | [CycloneDX Json](https://cyclonedx.org/docs/1.7/json/) test report(s) | [GitLab integration](https://docs.gitlab.com/ci/yaml/artifacts_reports/#artifactsreportscyclonedx) |

### Secrets management

Here are some advices about your **secrets** (variables marked with a :lock:):
+25 −0
Original line number Diff line number Diff line
@@ -117,6 +117,31 @@
      "name": "dotnet format",
      "description": "Run a [dotnet format](https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-format) check",
      "disable_with": "DOTNET_FORMAT_DISABLED"
    },
    {
      "id": "sbom",
      "name": "Software Bill of Materials",
      "description": "Set to true to disable SBOM generation",
      "disable_with": "DOTNET_SBOM_DISABLED",
      "variables": [
        {
          "name": "DOTNET_SBOM_IMAGE",
          "description": "The container image to use for SBOM generation using [CycloneDX cdxgen](https://github.com/CycloneDX/cdxgen)",
          "default": "ghcr.io/cyclonedx/cdxgen:master",
          "advanced": true
        },
        {
          "name": "DOTNET_SBOM_SUPPLIER",
          "description": "The package supplier name to use in the generated SBOMs",
          "default": "${CI_PROJECT_ROOT_NAMESPACE}"
        },
        {
          "name": "DOTNET_SBOM_OPTS",
          "description": "Additional options to pass to the SBOM generation tool",
          "default": "--fail-on-error --evidence --deep",
          "advanced": true
        }
      ]
    }
  ]
}
 No newline at end of file
+114 −0
Original line number Diff line number Diff line
@@ -68,12 +68,41 @@ spec:
      description: Set to true to disable the [Dotnet Format](https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-format) code formatting check (enabled by default)
      type: boolean
      default: false
    sbom-disabled:
      description: Set to true to disable SBOM generation
      type: boolean
      default: false
    sbom-supplier:
      description: The package supplier name to use in the generated SBOMs
      default: '${CI_PROJECT_ROOT_NAMESPACE}'
    sbom-image:
      description: The container image to use for SBOM generation using [CycloneDX cdxgen](https://github.com/CycloneDX/cdxgen)
      default: 'ghcr.io/cyclonedx/cdxgen:master'
    sbom-opts:
      description: Additional options to pass to the SBOM generation tool
      default: '--fail-on-error --evidence --deep'
    package-configuration:
      description: The build configuration to use for packaging (Debug or Release).
      default: Release
    package-symbols-disabled:
      description: Disable creation of symbol packages (snupkg) for debugging
      default: false
    publish-enabled:
      description: Set to true to enable publishing of artifact to a [NuGet feed](https://www.nuget.org/).
      type: boolean
      default: false
    nuget-repo:
      description: |-
       Target NuGet package repository url to publish the packages to. (when overriding this, please set `DOTNET_NUGET_API_KEY` at project CI variable to set the [nuget api-key used by dotnet nuget push](https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-nuget-push)) 
       
       _defaults to [GitLab project's packages repository](https://docs.gitlab.com/user/packages/nuget_repository/)_
      default: ${CI_SERVER_URL}/api/v4/projects/${CI_PROJECT_ID}/packages/nuget/index.json
    nuget-symbol-repo:
      description: |-
       Target NuGet package symbol repository url to publish the symbol packages to. (when overriding this, please set `DOTNET_NUGET_SYMBOL_API_KEY` at project CI variable to set the [nuget symbol api-key used by dotnet nuget push](https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-nuget-push)) 
       
       _defaults to [GitLab project's packages repository](https://docs.gitlab.com/user/packages/nuget_repository/)_ 
      default: ${CI_SERVER_URL}/api/v4/projects/${CI_PROJECT_ID}/packages/nuget/index.json
---
# default workflow rules: Merge Request pipelines
.tbc-workflow-rules:
@@ -138,6 +167,9 @@ workflow:
    - when: on_success

variables:
  # Global TBC SBOM Mode (onrelease -> only generate SBOMs for releases, always -> generate SBOMs for all refs)
  TBC_SBOM_MODE: "onrelease"

  # .NET Base Project
  DOTNET_IMAGE: $[[ inputs.image ]]
  DOTNET_PROJECT_DIR: $[[ inputs.project-dir ]]
@@ -168,10 +200,28 @@ variables:
  # Format
  DOTNET_FORMAT_DISABLED: $[[ inputs.format-disabled ]]

  # SBOM
  DOTNET_SBOM_DISABLED: $[[ inputs.sbom-disabled ]]
  DOTNET_SBOM_SUPPLIER: $[[ inputs.sbom-supplier ]]
  DOTNET_SBOM_IMAGE: $[[ inputs.sbom-image ]]
  DOTNET_SBOM_OPTS: $[[ inputs.sbom-opts ]]

  DOTNET_PACKAGE_CONFIGURATION: $[[ inputs.package-configuration ]]
  DOTNET_PACKAGE_SYMBOLS_DISABLED: $[[ inputs.package-symbols-disabled ]]

  # Publish
  DOTNET_PUBLISH_ENABLED: $[[ inputs.publish-enabled ]]
  DOTNET_NUGET_REPO: $[[ inputs.nuget-repo ]]
  DOTNET_NUGET_API_KEY: $CI_JOB_TOKEN
  DOTNET_NUGET_SYMBOL_REPO: $[[ inputs.nuget-symbol-repo ]]
  DOTNET_NUGET_SYMBOL_API_KEY: $CI_JOB_TOKEN

  # default production ref name (pattern)
  PROD_REF: '/^(master|main)$/'
  # default integration ref name (pattern)
  INTEG_REF: '/^develop$/'
  # default release tag name (pattern)
  RELEASE_REF: '/^v?[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9\-\.]+)?(\+[a-zA-Z0-9\-\.]+)?$/'

stages:
  - build
@@ -1381,6 +1431,41 @@ stages:
    log_info "Code formatting check completed"
  }

  function _dotnet_generate_os_arch_template() {
    local template=${1}
    local separator=${2:-}
    local x64_mode=${3:0}
    local arch
    case "$(uname -m)" in
      x64|x86_64|amd64) arch="amd64" ;;
      armv7|armhf) arch="arm" ;;
      aarch64|arm64) arch="arm64" ;;
      *) arch="$(uname -m)" ;;
    esac
    if [[ "${arch}" == "amd64" ]] && [[ "${x64_mode}" == "1" ]]; then
      arch=x64
    fi
    local os_name
    case $(uname | tr '[:upper:]' '[:lower:]') in
      linux*) export os_name=linux ;;
      darwin*) export os_name=osx ;;
      msys*) export os_name=win ;;
      *) export os_name=notset ;;
    esac
    local libc_type
    if ldd --version 2>&1 | grep -q musl; then
      libc_type="${separator}musl"
    else
      libc_type=""
    fi
    local result
    result=${template//<ARCH>/"${arch}"}
    result=${result//<OS>/"${os_name}"}
    result=${result//<LIBC>/"${libc_type}"}
    echo "${result}"
  }


  unscope_variables
  eval_all_secrets

@@ -1474,3 +1559,32 @@ dotnet-format:
    - if: '$DOTNET_FORMAT_DISABLED == "true"'
      when: never
    - !reference [.test-policy, rules]

dotnet-sbom:
  extends: .dotnet-env-base
  stage: test
  image: 
    name: $DOTNET_SBOM_IMAGE
    entrypoint: [""]
  variables:
    DOTNET_REQUIRES_SDK_INSTALL: "false"
    CDXGEN_DEBUG_MODE: "debug"
  before_script:
    - !reference [.dotnet-scripts]
    - install_ca_certs "${CUSTOM_CA_CERTS:-$DEFAULT_CA_CERTS}"
  script:
    - log_info "cdxgen -r ${DOTNET_PROJECT_DIR} --project-group ${DOTNET_SBOM_SUPPLIER} --project-name ${DOTNET_PROJECT_NAME}-${proj_name} --project-version ${DOTNET_PROJECT_VERSION_FULL} -o ${DOTNET_PROJECT_DIR}/reports/dotnet-sbom-${DOTNET_PROJECT_NAME}.cyclonedx.json ${DOTNET_SBOM_OPTS}"
    - cdxgen -r ${DOTNET_PROJECT_DIR} --project-group "${DOTNET_SBOM_SUPPLIER}" --project-name "${DOTNET_PROJECT_NAME}-${proj_name}" --project-version "${DOTNET_PROJECT_VERSION_FULL}" -o "${DOTNET_PROJECT_DIR}/reports/dotnet-sbom-${DOTNET_PROJECT_NAME}.cyclonedx.json" ${DOTNET_SBOM_OPTS}
  artifacts:
    reports:
      cyclonedx: "${DOTNET_PROJECT_DIR}/reports/dotnet-sbom-${DOTNET_PROJECT_NAME}.cyclonedx.json"
    expire_in: 1 month
    when: always
    paths:
      - '${DOTNET_PROJECT_DIR}/reports/dotnet-sbom-*.cyclonedx.json'
  rules:
    - if: '$DOTNET_SBOM_DISABLED == "true"'
      when: never
    - if: '$TBC_SBOM_MODE == "always"'
    - if: '$TBC_SBOM_MODE == "onrelease" && ($CI_COMMIT_REF_NAME =~ $RELEASE_REF || $CI_COMMIT_REF_NAME =~ $INTEG_REF || $CI_COMMIT_REF_NAME =~ $PROD_REF)'
    - when: never