Hey Devs!
Recently, some products in our company have started the process of becoming OSS (Open Source Software). In OSS projects, conflicts between the licenses of utilized packages and the project's license can pose a risk.
Today, I'd like to share how we began addressing this risk and monitoring it continuously by introducing a tool called License Finder.
- Introduction to License Finder
- Introducing License Finder to CI
- Conclusion
- Appendix: How ROUTE06, Inc. Handles It
Introduction to License Finder
License Finder is a Ruby-based CLI tool for license checking, developed by Pivotal (now VMware).
pivotal / LicenseFinder
Find licenses for your project's dependencies.
It integrates with package managers to discover dependencies and identify the licenses of each package. It supports bundler, npm, pnpm, and more.
The identified licenses are compared against a list of licenses approved by the user, and the results are outputted. It can also be integrated into CI pipelines.
Installation
Install License Finder with Ruby 2.6.0 or later:
$ gem install license_finder
Usage
Install dependency packages using tools like bundle install
or npm install
.
Then simply run License Finder.
💡 The package manager is automatically detected. If you want to configure it manually, you can do so by editing the config/license_finder.yml
file. For details, see Saving Configuration in the README.md.
When executed, all dependency packages are output with their license names. Initially, all packages are unapproved:
$ license_finder
LicenseFinder::Bundler: is active for '/Users/masutaka/work'
Dependencies that need approval:
actioncable, 7.2.1, MIT
actionmailbox, 7.2.1, MIT
(snip)
Approving Dependencies
The goal is to have zero unapproved dependencies.
Typically, you approve licenses as a whole, and if needed, approve or ignore individual packages. Here are some examples:
# Permit MIT and Apache-2.0 licenses
$ license_finder permitted_licenses add "MIT" "Apache 2.0"
# Specify "who" and "why" when permitting a license
$ license_finder permitted_licenses add "Simplified BSD" --who CTO --why "Go ahead"
# Approve the package awesome_gpl_gem
$ license_finder approvals add awesome_gpl_gem
# Ignore the package awesome_ignore_gem
$ license_finder ignored_dependencies add awesome_ignore_gem
By default, the decisions are saved in doc/dependency_decisions.yml
.
Introducing License Finder to CI
When there are no unapproved packages, License Finder returns an exit code of 0
. Using this feature, it can be incorporated into CI (GitHub Actions).
Example Integration with GitHub Actions
Here’s an example of integration in giselles-ai/giselle.
🔗 https://github.com/giselles-ai/giselle/blob/v0.3.1/.github/workflows/license.yml
The license_finder job (L18-L104) includes two main tasks:
- Execute License Finder (L80-L82). The CI fails if unapproved licenses are found, allowing us to detect licensing issues
- Update the license report docs/packages-license.md and commit it to the repository (L84-L104)
The license report is committed to the repository to meet requirements from licenses like CC BY 4.0 that mandate attribution. To reduce maintenance costs, the report is updated automatically using GitHub Actions.
Below are additional customizations:
- The license_finder job takes about 3 minutes, so it only executes when necessary:
- It runs only when files like package.json are updated (L48-L66)
- Since this job is a required check before merging pull requests, no filtering is done in the
on:
section
- For pull requests from forked repositories, the license report is not updated:
- This is because GitHub Actions cannot access Repository variables/secrets needed to generate a GitHub App Token for committing changes
- Although changing
on: pull_request:
toon: pull_request_target:
would allow access, we avoided this due to security risks
- The license report is not updated on the default branch
To update the license report, we created a GitHub App1 with the following permissions and installed it in the repository:
- Contents: Read and write
- Metadata: Read-only
Instead of the GITHUB_TOKEN
environment variable, we used a GitHub App Token for the following reasons:
- The license_finder job is a required check before merging pull requests
- Even after committing via GitHub Actions, the license_finder job should still execute for the newly created commit
- If
GITHUB_TOKEN
is used for commits, they are considered bot operations, and CI is not triggered - Personal Access Tokens have long lifetimes, so we avoid using them whenever possible
Conclusion
If you’re considering an in-house solution for monitoring OSS licenses, License Finder could be an option. However, without sufficient knowledge of licenses, there’s a risk of mistakenly approving dependencies (see appendix). Another concern is that development on License Finder is not very active.
For now, we plan to continue using License Finder while keeping SaaS options like FOSSA in mind.
Appendix: How ROUTE06, Inc. Handles It
Here's how we handle cases where License Finder fails in CI.
How to Handle License Finder Failures in CI
License Finder fails when a package with a license not permitted in the decisions file is added.
Below is an example of CI failing because the npm package mdn-data uses the CC0 1.0 Universal
license, which is not permitted.
Please follow the response flow outlined below.
Response Flow
- Check the license of the relevant package
- Confirm that the identified license is compatible with the license of the OSS repository in question
- Take action based on the following cases:
- Case 1: The licenses are compatible
- Example: The repository is licensed under Apache-2.0, and the package uses the Zlib license
- Case 2: The licenses are not compatible
- Example: The repository is licensed under Apache-2.0, and the package uses GPL-3.0-only
- Case 3: The license is unknown
- Case 1: The licenses are compatible
Case 1: The licenses are compatible
If the licenses are compatible, allow the license in License Finder.
## Example of permitting the Zlib license
$ license_finder permitted_licenses add 'Zlib' \
--why 'Compatible with Apache-2.0 license. See https://opensource.org/license/Zlib' \
--who 'OSPO @masutaka'
- [Recommended] Use the
--why
option to specify the reason for permitting the license. Including the URL of the license can be helpful - [Optional] Use the
--who
option to specify who granted the permission
Case 2: The licenses are not compatible
Copyleft licenses such as GPL, LGPL, AGPL, or EPL may not be compatible with permissive licenses like MIT or Apache-2.0.
- Compatible cases:
- Using an Apache-2.0 package in a GPLv3 product
- Incompatible cases:
- Using a GPLv3 package in an Apache-2.0 product
When licenses are incompatible, you must take appropriate actions:
- Exclude GPLv3 or other incompatible packages from the product
- Change the entire product license to GPLv3 or similar
- Separate the incompatible package and use it independently
- Seek legal advice
Case 3: The license is unknown
If the package registry or Git repository does not specify the license, contact the provider.
- Example: The license for the npm package @vercel/flags was unknown
Depending on the situation, if it seems acceptable, create an issue and approve the package temporarily.
$ license_finder approvals add '@vercel/flags' \
--why 'The license is none. Check https://github.com/giselles-ai/giselle/issues/12 later'
Once the license is identified, remove the approval and re-run License Finder. If it fails again, follow the previously mentioned response flow.
$ license_finder approvals remove '@vercel/flags' \
--why 'It was released as OSS under the MIT license.'
If the issue seems problematic, consider alternatives such as using a different package or temporarily holding off on merging into the main branch.
Supplement: License Finder Supports Composite Licenses with AND
or OR
There are cases of composite licenses like (MIT AND Zlib)
for the npm package pako.
License Finder supports AND
and OR
, so in such cases, you only need to permit MIT
and Zlib
individually. There is no need to permit (MIT AND Zlib)
explicitly.
Top comments (0)