iTranslated by AI
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.
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.
License Scanning
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.
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.
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.
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