#!/usr/bin/env bash set -euo pipefail : "${CARGO:=cargo}" # A list of the licenses that we currently allow in our code. # # If a package supports multiple licenses (using OR), then we are okay # if it supports _any_ of these licenses. # # We don't currently do a good job of understanding AND and OR, so # interesting license combinations that involve AND may need to be given # in quotes. RECOGNIZED_LICENSES=( Apache-2.0 BSD-2-Clause BSD-3-Clause BSL-1.0 CC0-1.0 CDLA-Permissive-2.0 ISC MIT Unicode-DFS-2016 Unicode-3.0 Unlicense Zlib "BSD-3-Clause AND MIT" "MIT AND BSD-3-Clause" "(MIT OR Apache-2.0) AND Unicode-DFS-2016" "MIT AND (MIT OR Apache-2.0)" # Used by unicode-ident "(MIT OR Apache-2.0) AND Unicode-3.0" "(Apache-2.0 OR MIT) AND Unicode-3.0" # Used by ring >= v0.17.10 "Apache-2.0 AND ISC" # Used by aws-lc-rs "ISC AND (Apache-2.0 OR ISC)" "(Apache-2.0 OR MIT) AND BSD-3-Clause" ) # List of packages that don't list a license. NO_LICENSE=( ) # List of packages which we allow to use the MPL-2.0 license. # # We need to check these individually because, if the party says # "MPL-2.0" without actually including the text of exhibit A from the # MPL, it is not unambiguous that they have applied MPL-2.0 to their # code. # # To deal with a new MPL dependency, take a look at its repository. # Look for the Exhibit A text (grep for "Source Code Form"). # If it's present, fine, you can add it to this list. # If not, here is an example of how to proceed # https://github.com/soc/option-ext/pull/4 # After the crate upstream merges that MR, we can take that as a declaration # about the licence status, so you can include the crate in this list, # even if upstream haven't *released* the updated crate yet. # # For background, see # https://gitlab.torproject.org/tpo/core/arti/-/issues/845#note_2900025 # MPL_20_OK=( option-ext dynasm dynasmrt ) # List of packages allowed to use the LGPL-3.0-only or LGPL-3.0 license. # # We aren't including LGPL code in the general Arti dependency tree, this is # just meant to be a limited whitelist which allows some of our own crates # we are developing under the LGPL. LGPL_30_OK=( equix hashx ) ###### # Exceptions for specific crates only. # # We use these when a license would otherwise not be allowed, # but we want to allow it for just one crate. # # If you expect that a specific license will be allowed for _several_ crates, # make a new thing like LGPL_30_OK above. declare -A PERMIT_LICENSE # awc-ls-sys hasn't updated their license from OpenSSL to Apache; # their ticket is https://github.com/aws/aws-lc/issues/2203. # # The OpenSSL license is GPL-incompatible, but we tolerate it, # since never use aws-lc-sys by default. PERMIT_LICENSE["aws-lc-sys"]="ISC AND (Apache-2.0 OR ISC) AND OpenSSL" # priority-queue is MPL-2.0 with exhibit A text # # See MPL_20_OK above. PERMIT_LICENSE["priority-queue"]="LGPL-3.0-or-later OR MPL-2.0" containsElement () { local e match="$1" shift for e; do [[ "$e" == "$match" ]] && return 0; done return 1 } if ! $CARGO license --help >/dev/null; then echo "cargo-license is not installed!" echo echo "For reasonable results, run:" echo " cargo install cargo-license" exit 2 fi cd "$(dirname "$0")/.." # The caller might reasonably have set CARGO to something containing spaces. # So collect the output before we set IFS. output=$($CARGO license --all-features -t) problems=0 IFS=$'\n' for line in $output; do package=$(echo "$line" | cut -f1) licenses=$(echo "$line" | cut -f5) # skip the first line. if test "$package" = "name" && test "$licenses" = "license"; then continue; fi if test -z "$licenses"; then if ! containsElement "$package" "${NO_LICENSE[@]}"; then echo "$package has no license" problems=1 fi continue fi if test "$licenses" = "MPL-2.0"; then if ! containsElement "$package" "${MPL_20_OK[@]}"; then echo "$package uses MPL-2.0 but has not been allow-listed." problems=1 fi continue fi if test "$licenses" = "LGPL-3.0-only" || test "$licenses" = "LGPL-3.0"; then if ! containsElement "$package" "${LGPL_30_OK[@]}"; then echo "$package uses LGPL-3.0 but has not been allow-listed." problems=1 fi continue fi if [[ -v PERMIT_LICENSE[$package] ]]; then if test "$licenses" = "${PERMIT_LICENSE[$package]}"; then # We've made an exception for this package. : else echo "$package has an exception listed, but its license does not match that exception." problems=1 fi continue fi found_ok=0 if containsElement "$licenses" "${RECOGNIZED_LICENSES[@]}"; then found_ok=1 else # TODO: By Splitting on "OR" without parsing, this can give bogus # elements in the output if the license is something like "(A OR # B) AND C". Fortunately the parenthesis will save us from false # negatives here, but in the end we should probably switch to a # real parser. for lic in ${licenses// OR /$'\n'}; do if containsElement "$lic" "${RECOGNIZED_LICENSES[@]}"; then found_ok=1 break fi done fi if test $found_ok = "0"; then echo "$package does not advertise any supported license!" echo " ($package: $licenses)" problems=1 fi done if test "$problems" = 1; then echo "You can suppress the above warnings by editing $0..." echo "but only do so if we are actually okay with all the licenses!" fi exit "$problems"