In this blog post, Andrew Lock demonstrates how to create SBOM attestations for your .NET applications or NuGet packages using GitHub Actions, enhancing supply chain security.

Creating SBOM Attestations for NuGet Packages Using GitHub Actions

By Andrew Lock

Introduction

In this post, Andrew Lock provides a detailed guide on creating attestations for Software Bill of Materials (SBOM) documents for .NET applications and NuGet packages. The goal is to enhance confidence and transparency in the software supply chain by leveraging GitHub Actions for automated provenance and attestation generation.

Supply Chain Security and Attestations

Supply chain security involves ensuring the integrity and provenance of software artifacts. Andrew discusses the role of provenance attestations, which serve as standardized documents that describe how, where, and by whom an artifact was built (SLSA specification).

While providing an attestation doesn’t guarantee downstream security, it facilitates the verification process for package consumers, forming a key building block in supply chain security.

What is an SBOM?

A Software Bill of Materials (SBOM) enumerates the packages and dependencies used to create a software artifact. SBOMs enable:

  • Visibility into included components
  • Identification of known vulnerabilities
  • Awareness of compliance/licensing issues
  • Insight into supply chain risks

Combining Provenance and SBOM Attestations

Attestations can apply both to the artifact and to its associated SBOM, allowing consumers to verify that the SBOM is authentic and untampered, and was generated as claimed during a specific build run.

Generating SBOM Attestations in GitHub Actions

Andrew demonstrates how to use the actions/attest-sbom GitHub Action to generate signed attestations for SBOMs. This action supports both SPDX and CycloneDX JSON formats. These formats are supported by multiple open-source tools and are standardized (SPDX: ISO/IEC 5692:2021, CycloneDX: ECMA-424).

Example Workflow: Building a .NET Project with SBOM Attestation

Below is a sample workflow that:

  • Builds a .NET NuGet package
  • Creates a CycloneDX SBOM
  • Generates an SBOM attestation
name: BuildAndPack

on:
  push:
    branches: ["main" ]
    tags: ['*']
  pull_request:
    branches: ['*']

jobs:
  build-and-test:
    # Add necessary permissions for attestation
    permissions:
      id-token: write
      attestations: write
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4.2.2
    - uses: actions/setup-dotnet@v4.3.1
      with:
        dotnet-version: "9.0.x"

    - name: Build and pack
      run: dotnet pack -c Release

    - name: Push to NuGet
      run: dotnet nuget push artifacts/packages/NetEscapades.AspNetCore.SecurityHeaders.nupkg
      env:
        NuGetToken: $

    - name: Generate JSON SBOM
      uses: CycloneDX/gh-dotnet-generate-sbom@v1.0.1
      with:
        path: ./src/NetEscapades.AspNetCore.SecurityHeaders/NetEscapades.AspNetCore.SecurityHeaders.csproj
        out: ./artifacts/sboms
        json: true
        github-bearer-token: $

    # Add this attestation step
    - name: Attest package
      uses: actions/attest-sbom@v2.2.0
      with:
        subject-path: artifacts/packages/NetEscapades.AspNetCore.SecurityHeaders.nupkg
        sbom-path: artifacts/sboms/bom.json

Key Points:

  • The workflow is set to run on every PR, branch, and tag by default, but can be restricted as needed (e.g., only on releases).
  • The SBOM is generated in CycloneDX format, but SPDX is also supported.
  • Attestation steps require additional permissions (id-token: write and attestations: write).

Resulting Attestation Artifacts and Output

The actions/attest-sbom action creates a Sigstore bundle (JSON document) representing the attestation, which includes information about:

  • The artifact
  • The SBOM
  • The verification material (digital signatures, logs)

The run summary in GitHub Actions will also display attestation links for easy access.

Attestation Workflow Summary Example

If you click these links, you’re taken to a display page where you can view or download the attestation in a user-friendly format.

Attestation Display Page Example

Verifying SBOM Attestations

Attestations add value only if they are verified by consumers. Andrew shows how verification can be performed using the GitHub CLI:

gh attestation verify \
  --owner andrewlock \
  --predicate-type https://cyclonedx.org/bom \
  <filename-or-url>
  • For SPDX SBOMs, use --predicate-type https://spdx.dev/Document/v2.3
  • If omitted, the CLI verifies the package’s provenance attestation by default

Example Output

gh attestation verify --owner andrewlock --predicate-type https://cyclonedx.org/bom "NetEscapades.AspNetCore.SecurityHeaders.1.0.0-preview.4.nupkg"

# ... output indicating policy, verification succeeded, and matching attestation information

Limitations for NuGet Packages

Verifying attestations for NuGet packages from nuget.org presents challenges:

  • nuget.org modifies uploaded .nupkg files by adding its own signature file, invalidating earlier attestations
  • Removing the added signature is only effective if the package isn’t author-signed
  • Author-signed packages have their .signature.p7s file altered/countersigned, which can’t be “cleaned”
  • Thus, provenance/SBOM attestations can’t always be reliably verified for packages retrieved from nuget.org
  • However, NuGet native signatures provide some, but not equivalent, assurance

Summary

This guide builds on previous posts about provenance and SBOMs, describing how to automate SBOM attestation as part of the CI/CD workflow in GitHub Actions. While generating attestations is straightforward with available Actions, consuming/verifying attestations from nuget.org packages remains hindered by the repository’s modifications. Still, these practices illustrate good supply chain hygiene and point the way toward better security practices in the .NET ecosystem.


Author: Andrew Lock
.NET Escapades

This post appeared first on “Andrew Lock’s Blog”. Read the entire article here