/ maint / check_licenses
check_licenses
  1  #!/usr/bin/env bash
  2  
  3  set -euo pipefail
  4  
  5  : "${CARGO:=cargo}"
  6  
  7  # A list of the licenses that we currently allow in our code.
  8  #
  9  # If a package supports multiple licenses (using OR), then we are okay
 10  # if it supports _any_ of these licenses.
 11  #
 12  # We don't currently do a good job of understanding AND, so
 13  # interesting license combinations that involve AND may need to be given
 14  # in quotes.
 15  RECOGNIZED_LICENSES=(
 16      Apache-2.0
 17      BSD-2-Clause
 18      BSD-3-Clause
 19      BSL-1.0
 20      CC0-1.0
 21      ISC
 22      MIT
 23      Unicode-DFS-2016
 24      Unicode-3.0
 25      Unlicense
 26      Zlib
 27      "MIT AND BSD-3-Clause"
 28      "(MIT OR Apache-2.0) AND Unicode-DFS-2016"
 29      "MIT AND (MIT OR Apache-2.0)"
 30      # cargo-license appears to break on the OR inside () and then sort!
 31      # https://github.com/onur/cargo-license/issues/78
 32      "Apache-2.0) OR MIT AND (MIT"
 33      # Used by unicode-ident
 34      "(MIT OR Apache-2.0) AND Unicode-3.0"
 35  )
 36  
 37  # List of packages that don't list a license.
 38  NO_LICENSE=(
 39      # The license for "ring" is something like "ISC AND openssl AND
 40      # ssleay AND MIT"; the openssl license is not up-to-date with
 41      # modern openssl.  It includes an advertising clause. :P
 42      #
 43      # See https://gitlab.torproject.org/tpo/core/arti/-/issues/493 for
 44      # our related ticket.
 45      ring
 46      # License appears to be ISC.
 47      webpki
 48      # The unicode folks have their own license now that is no longer
 49      # "Unicode-DFS-2016" and which might not even have a SPDX
 50      # entry. But it still seems to be a permissive DFSG-compliant
 51      # license.
 52      tinystr
 53  )
 54  
 55  # List of packages which we allow to use the MPL-2.0 license.
 56  #
 57  # We need to check these individually because, if the party says
 58  # "MPL-2.0" without actually including the text of exhibit A from the
 59  # MPL, it is not unambiguous that they have applied MPL-2.0 to their
 60  # code.
 61  #
 62  # (See https://gitlab.torproject.org/tpo/core/arti/-/issues/845)
 63  MPL_20_OK=(
 64      option-ext
 65      dynasm
 66      dynasmrt
 67  )
 68  
 69  # List of packages allowed to use the LGPL-3.0-only license.
 70  #
 71  # We aren't including LGPL code in the general Arti dependency tree, this is
 72  # just meant to be a limited whitelist which allows some of our own crates
 73  # we are developing under the LGPL.
 74  LGPL_30_ONLY_OK=(
 75      equix
 76      hashx
 77  )
 78  
 79  containsElement () {
 80    local e match="$1"
 81    shift
 82    for e; do
 83        [[ "$e" == "$match" ]] && return 0;
 84    done
 85    return 1
 86  }
 87  
 88  if ! $CARGO license --help >/dev/null; then
 89      echo "cargo-license is not installed!"
 90      echo
 91      echo "For reasonable results, run:"
 92      echo "    cargo install cargo-license"
 93      exit 2
 94  fi
 95  
 96  cd "$(dirname "$0")/.."
 97  
 98  # The caller might reasonably have set CARGO to something containing spaces.
 99  # So collect the output before we set IFS.
100  output=$($CARGO license --all-features -t)
101  
102  problems=0
103  IFS=$'\n'
104  for line in $output; do
105      package=$(echo "$line" | cut -f1)
106      licenses=$(echo "$line" | cut -f5)
107  
108      # skip the first line.
109      if test "$package" = "name" && test "$licenses" = "license"; then
110  	continue;
111      fi
112      if test -z "$licenses"; then
113  	if ! containsElement "$package" "${NO_LICENSE[@]}"; then
114  	    echo "$package has no license"
115  	    problems=1
116  	fi
117  	continue
118      fi
119  
120      if test "$licenses" = "MPL-2.0"; then
121  	if ! containsElement "$package" "${MPL_20_OK[@]}"; then
122  	    echo "$package uses MPL-2.0 but has not been allow-listed."
123  	    problems=1
124  	fi
125  	continue
126      fi
127  
128      if test "$licenses" = "LGPL-3.0-only"; then
129  	if ! containsElement "$package" "${LGPL_30_ONLY_OK[@]}"; then
130  	    echo "$package uses LGPL-3.0-only but has not been allow-listed."
131  	    problems=1
132  	fi
133  	continue
134      fi
135  
136      if test "$licenses" = "LGPL-3.0-or-later OR MPL-2.0"; then
137          # priority-queue has some paperwork trouble
138          #  - MPL but missing Exhibit A
139          # We are using this in tor-rtmock, where it's IMO fine to have LGPL.
140  	# Upstream conversation here
141          #   https://github.com/garro95/priority-queue/pull/48
142  	# (or maybe followup tickets)
143  	if ! containsElement "$package" "priority-queue"; then
144  	    echo "$package uses wrong SPDX and isn't priority-queue."
145  	    problems=1
146  	fi
147  	continue
148      fi
149  
150      found_ok=0
151      if containsElement "$licenses" "${RECOGNIZED_LICENSES[@]}"; then
152  	found_ok=1
153      else
154          # TODO: By Splitting on "OR" without parsing, this can give bogus
155          # elements in the output if the license is something like "(A OR
156          # B) AND C".  Fortunately the parenthesis will save us from false
157          # negatives here, but in the end we should probably switch to a
158          # real parser.
159          for lic in ${licenses// OR /$'\n'}; do
160      	    if containsElement "$lic" "${RECOGNIZED_LICENSES[@]}"; then
161  		found_ok=1
162      		break
163  	    fi
164  	done
165      fi
166      if test $found_ok = "0"; then
167  	echo "$package does not advertise any supported license!"
168  	echo "   ($package: $licenses)"
169  	problems=1
170      fi
171  done
172  
173  if test "$problems" = 1; then
174      echo "You can suppress the above warnings by editing $0..."
175      echo "but only do so if we are actually okay with all the licenses!"
176  fi
177  
178  exit "$problems"