iTranslated by AI

The content below is an AI-generated translation. This is an experimental feature, and may contain errors. View original article
🔍

Automating Dependency License Checks in GitHub Actions with Trivy (GPL Prevention)

に公開

Overview

When developing software, it is a situation you want to avoid having dependencies with restrictive licenses like GPL inadvertently mixed in.
This is often referred to as "GPL contamination" or "license infection."

Previously, I used a tool called LicenseFinder to scan for these, but since it is written in Ruby, it can be difficult to set up, and it appears it is no longer being actively maintained. Therefore, we decided to switch to Trivy, a highly functional tool.

https://github.com/pivotal/LicenseFinder

https://github.com/pivotal/LicenseFinder/issues/1059#issuecomment-3139284915

About Trivy

Trivy (pronounced roughly "tree-vee") is called an SBOM tool. While we are using it for license scanning this time, it is primarily used for scanning containers and dependencies for vulnerabilities.
We already handle vulnerability scanning using Dependabot Alerts, so I will skip that here, but you can use them together if necessary.

https://trivy.dev

License Scanning

https://trivy.dev/docs/latest/scanner/license/

Unlike LicenseFinder, Trivy's license scanner includes a classification of Severity for each open-source license based on the Google License Classification.
For example, licenses you may want to avoid, such as AGPL or GPL, are ranked as CRITICAL or HIGH, while licenses that are easy to use as dependencies, such as the MIT license, are treated as LOW.

https://opensource.google/documentation/reference/thirdparty/licenses

By leveraging this, you don't need to manually define every major license; you can simply write rules to allow or deny based on risk, and it will only notify you when necessary.

Implementation

Creating a rego file

In this setup, I will allow licenses with a LOW risk level, while permitting specific packages with stricter licenses (that only run on the server-side) on a case-by-case basis.

/.github/license.rego
package user.license_check

allowed_licenses := {
    # Add additional licenses to allow here
    # Always verify before adding
    "APSL-2.0"
}

allowed_packages := {
    # These are LGPL, but we allow them as they only run server-side
    "@img/sharp-libvips-linux-x64",
    "@img/sharp-libvips-linuxmusl-x64",
    # These are MPL, but used only during the build process, so we allow them
    "lightningcss",
    "lightningcss-linux-x64-gnu",
    "lightningcss-linux-x64-musl"
}

deny contains msg if {
    license := input.Results[_].Licenses[_]
    
    license.Severity != "LOW"
    not allowed_licenses[license.Name]
    not allowed_packages[license.PkgName]

    msg := sprintf("NOT ALLOWED LICENSE: '%s' (package: %s)", [license.Name, license.PkgName])
}

GitHub Actions

Trivy only generates the SBOM; you need to use another tool like OPA to verify the scan results. In this example, I use OPA and the jq command to check the results and fail the build if any unpermitted dependencies are found.

.github/workflows/trivy.yml
name: Trivy CI
permissions: {}
on:
    pull_request:
        types:
            - opened
            - synchronize
    push:
        branches:
            - "**"

jobs:
    eslint:
        name: 🔍️ Check license of packages with Trivy
        runs-on: ubuntu-latest
        permissions:
            contents: read

        steps:
            - uses: actions/checkout@v6

            - name: Use Node.js ${{ matrix.node-version }}
              uses: actions/setup-node@v4
              with:
                node-version: 24
            
            - name: Install pnpm
              run: corepack disable && npm install -g pnpm

            - name: Install dependencies
              run: pnpm install

            - name: Run Trivy License Scan
              uses: aquasecurity/trivy-action@master
              with:
                scan-type: 'fs'
                scanners: 'license'
                format: 'json'
                output: 'trivy-result.json'
                version: latest
            
            - name: Log trivy-result.json
              run: cat trivy-result.json

            - name: Setup OPA
              uses: open-policy-agent/setup-opa@v2
              with:
                version: latest

            - name: Evaluate Policy
              run: |
                opa eval -i trivy-result.json -d .github/license.rego "data.user.license_check.deny" --format json > opa-result.json
                cat opa-result.json
                jq -e '.result[0].expressions[0].value | length == 0' opa-result.json > /dev/null

Points to Note

When performing the scan, ensure you have installed the packages (e.g., using pnpm install), otherwise you will not get accurate results.

Discussion