Notes on Using Licensed and Licensee for OSS Licensing and SBOM: Rails Apps

Have you ever wondered what Open Source Software (OSS) is being using in your product and what licenses apply to these dependencies? GitHub/licensed is a tool that can help to scan your codebase and create a list of licenses for you. Let’s look at how to use it for Rails applications.

Note:

As mentioned on their page, Licensed is not a complete open source license compliance solution. You should review their disclaimers to understand how and when to use it.

The underlying idea is as below:

  • Create a list of dependencies for your project. This is called a Software Bill of Material (SBOM) and in the case of Rails apps, this is extracted from your Gemfile
  • Check the license that the dependency uses by scanning the license-like files. Licensed uses a library called licensee for doing this.
  • Match them against a list of allowed licenses that are compatible with your project and intentions.
  • Highlight dependencies that do not match, or licenses that cannot be verified for manual review.

To do these, we have a couple of extra steps that we need to do:

  • Install licensed and its dependencies
  • Create a configuration file that can be used for the project

Getting Started – Install Ruby and Rails

First, we need to:

Since I use pik to switch between Ruby installations on my PC, I select Ruby 3.3 as the current Ruby. I also ensure that the DevKit is on the path by doing d:\Ruby33-x64\ridk_use\ridk.cmd enable (you will need to adjust the base path depending on where your Ruby is installed).

$ ruby -v
ruby 3.3.0 (2023-12-25 revision 5124f9ac75) [x64-mingw-ucrt]

$ rails -v
Rails 8.0.0

Getting Started – Install Licensed

We just install the gem as normal.


$ gem install licensed
Fetching reverse_markdown-2.1.1.gem
Fetching pathname-common_prefix-0.0.2.gem
Fetching tomlrb-2.0.3.gem
Fetching ruby-xxHash-0.4.0.2.gem
Fetching thor-1.3.2.gem
Fetching rugged-1.7.2.gem
Fetching json-2.8.2.gem
Fetching net-http-0.5.0.gem
Fetching licensed-5.0.0.gem
Fetching faraday-net_http-3.4.0.gem
Fetching faraday-2.12.1.gem
Fetching sawyer-0.9.2.gem
Fetching octokit-9.2.0.gem
Fetching licensee-9.17.1.gem
Successfully installed tomlrb-2.0.3
Successfully installed thor-1.3.2
Successfully installed ruby-xxHash-0.4.0.2
Successfully installed reverse_markdown-2.1.1
Successfully installed pathname-common_prefix-0.0.2
Installing required msys2 packages: mingw-w64-ucrt-x86_64-libssh2 mingw-w64-ucrt-x86_64-cmake
Building native extensions. This could take a while...
Successfully installed rugged-1.7.2
Building native extensions. This could take a while...
Successfully installed json-2.8.2
Successfully installed net-http-0.5.0
Successfully installed faraday-net_http-3.4.0
Successfully installed faraday-2.12.1
Successfully installed sawyer-0.9.2
Successfully installed octokit-9.2.0
Successfully installed licensee-9.17.1
Successfully installed licensed-5.0.0
Parsing documentation for tomlrb-2.0.3
Installing ri documentation for tomlrb-2.0.3
Parsing documentation for thor-1.3.2
Installing ri documentation for thor-1.3.2
Parsing documentation for ruby-xxHash-0.4.0.2
Installing ri documentation for ruby-xxHash-0.4.0.2
Parsing documentation for reverse_markdown-2.1.1
Installing ri documentation for reverse_markdown-2.1.1
Parsing documentation for pathname-common_prefix-0.0.2
Installing ri documentation for pathname-common_prefix-0.0.2
Parsing documentation for rugged-1.7.2
Installing ri documentation for rugged-1.7.2
Parsing documentation for json-2.8.2
Installing ri documentation for json-2.8.2
Parsing documentation for net-http-0.5.0
Couldn't find file to include 'doc/net-http/examples.rdoc' from lib/net/http.rb
...
Couldn't find file to include 'doc/net-http/included_getters.rdoc' from lib/net/http/responses.rb
Installing ri documentation for net-http-0.5.0
Parsing documentation for faraday-net_http-3.4.0
Installing ri documentation for faraday-net_http-3.4.0
Parsing documentation for faraday-2.12.1
Installing ri documentation for faraday-2.12.1
Parsing documentation for sawyer-0.9.2
Installing ri documentation for sawyer-0.9.2
Parsing documentation for octokit-9.2.0
Installing ri documentation for octokit-9.2.0
Parsing documentation for licensee-9.17.1
Installing ri documentation for licensee-9.17.1
Parsing documentation for licensed-5.0.0
Installing ri documentation for licensed-5.0.0
Done installing documentation for tomlrb, thor, ruby-xxHash, reverse_markdown, pathname-common_prefix, 
  rugged, json, net-http, faraday-net_http, faraday, sawyer, octokit, licensee, licensed after 9 seconds
14 gems installed

Using Licensed

If you’re really eager to get started, the natural thing to do would be to go to your Rails project and type in:

$ licensed list
D:/Ruby33-x64/lib/ruby/gems/3.3.0/gems/licensed-5.0.0/lib/licensed/configuration.rb:358:in `find_config': Licensed configuration not found in C:/Users/mohit (Licensed::Configuration::LoadError)
        from D:/Ruby33-x64/lib/ruby/gems/3.3.0/gems/licensed-5.0.0/lib/licensed/configuration.rb:272:in `load_from'
        from D:/Ruby33-x64/lib/ruby/gems/3.3.0/gems/licensed-5.0.0/lib/licensed/cli.rb:105:in `config'
        from D:/Ruby33-x64/lib/ruby/gems/3.3.0/gems/licensed-5.0.0/lib/licensed/cli.rb:46:in `list'
        from D:/Ruby33-x64/lib/ruby/gems/3.3.0/gems/thor-1.3.2/lib/thor/command.rb:28:in `run'
        from D:/Ruby33-x64/lib/ruby/gems/3.3.0/gems/thor-1.3.2/lib/thor/invocation.rb:127:in `invoke_command'
        from D:/Ruby33-x64/lib/ruby/gems/3.3.0/gems/thor-1.3.2/lib/thor.rb:538:in `dispatch'
        from D:/Ruby33-x64/lib/ruby/gems/3.3.0/gems/thor-1.3.2/lib/thor/base.rb:584:in `start'
        from D:/Ruby33-x64/lib/ruby/gems/3.3.0/gems/licensed-5.0.0/exe/licensed:5:in `<top (required)>'
        from D:/Ruby33-x64/bin/licensed:36:in `load'
        from D:/Ruby33-x64/bin/licensed:36:in `<main>'

It would be premature to do that. As you can see the error on the first line, it says: Licensed configurationn not found and so, that’s what we need to do next.

Creating a Licensed Configuration

There is a brief guide to the configuration file viewable on their GitHub page and we fill follow that.

These are things that I set:

  • name (application name)
  • root path to the current directory (same as the configuration file)
  • set bundler to true as a metadata source
  • commented out: cache_path, source_path, samples in ignore list
  • commented out the multiple apps settings at the end
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
# If not set, defaults to the directory name of `source_path`
name: 'Hello Rails 8'

# Path is relative to the location of the configuration file and specifies
# the root to expand all paths from
# If not set, defaults to a git repository root
root: '.'

# Path is relative to configuration root and specifies where cached metadata will be stored.
# If not set, defaults to '.licenses'
#cache_path: 'relative/path/to/cache'

# Path is relative to configuration root and specifies the working directory when enumerating dependencies
# Optional for single app configuration, required when specifying multiple apps
# Defaults to current directory when running `licensed`
#source_path: 'relative/path/to/source'

# Whether to take any action when records are detected in the cache paths that don't map to evaluated
# dependencies.
# Available values are:
# - 'error': treat stale cached records as errors.  Notify the user and fail status checks
# - 'warn', '', unset: treat stale cached records as warnings.  Notify the user but do not fail status checks
# - 'ignore': Ignore stale cached records.  Do not notify the user and do not fail status checks
# Optional, when not set this defaults to 'warn' behavior
stale_records_action: 'warn'

# Sources of metadata
sources:
  bower: true
  bundler: true

# Dependencies with these licenses are allowed and will not raise errors or warnings.
# This list does not have a default value and is required for `licensed status`
# to succeed.
allowed:
  - mit
  - apache-2.0
  - bsd-2-clause
  - bsd-3-clause
  - cc0-1.0
  - isc
  - MIT-LICENSE

# These dependencies are ignored during enumeration.
# They will not be cached, and will not raise errors or warnings.
# This configuration is intended to be used for dependencies that don't need to
# be included for compliance purposes, such as other projects owned by the current
# project's owner, internal dependencies, and dependencies that aren't shipped with
# the project like test frameworks.
ignored:
  bundler:
    #- some-internal-gem

  bower:
    #- some-internal-package

# These dependencies have licenses not on the `allowed` list and have been reviewed.
# They will be cached and checked, but will not raise errors or warnings for a
# non-allowed license.  Dependencies on this list will still raise errors if
# license text cannot be found for the dependency.
reviewed:
  bundler:
    - bcrypt-ruby

  bower:
  - classlist # public domain
  - octicons

# Specify additional license terms that have been obtained from a dependency's owner
# which apply to the dependency's license 
additional_terms:
  bundler:
    bcrypt-ruby:
      - .licenses/amendments/bundler/bcrypt-ruby/amendment.txt

# A single configuration file can be used to enumerate dependencies for multiple
# projects.  Each configuration is referred to as an "application" and must include
# a source path, at a minimum
#apps:
#  - source_path: path/to/application1
#  - source_path: path/to/application2

Finally, running Licensed

Since we followed the default name, we can now do licensed list again, or we can pass the name of the config file by doing licensed list -c .licensed.yml instead. Wow, we have 87 dependencies in a clean Rails app from bundler.

$ licensed list
Listing dependencies for Hello Rails 8
  bundler
    actioncable (8.0.0)
    actionmailbox (8.0.0)
    actionmailer (8.0.0)
    actionpack (8.0.0)
    actiontext (8.0.0)
    actionview (8.0.0)
    activejob (8.0.0)
    activemodel (8.0.0)
    activerecord (8.0.0)
    activestorage (8.0.0)
    activesupport (8.0.0)
    base64 (0.2.0)
    bcrypt_pbkdf (1.1.1)
    benchmark (0.4.0)
    bigdecimal (3.1.8)
    bootsnap (1.18.4)
    builder (3.3.0)
    bundler (2.5.23)
    concurrent-ruby (1.3.4)
    connection_pool (2.4.1)
    crass (1.0.6)
    date (3.4.0)
    dotenv (3.1.4)
    drb (2.2.1)
    ed25519 (1.3.0)
    erubi (1.13.0)
    et-orbi (1.2.11)
    fugit (1.11.1)
    globalid (1.2.1)
    i18n (1.14.6)
    importmap-rails (2.0.3)
    io-console (0.7.2)
    irb (1.14.1)
    jbuilder (2.13.0)
    kamal (2.3.0)
    logger (1.6.1)
    loofah (2.23.1)
    mail (2.8.1)
    marcel (1.0.4)
    mini_mime (1.1.5)
    minitest (5.25.1)
    msgpack (1.7.5)
    net-imap (0.5.1)
    net-pop (0.1.2)
    net-protocol (0.2.2)
    net-scp (4.0.0)
    net-sftp (4.0.0)
    net-smtp (0.5.0)
    net-ssh (7.3.0)
    nio4r (2.7.4)
    nokogiri (1.16.7)
    ostruct (0.6.1)
    propshaft (1.1.0)
    psych (5.2.0)
    puma (6.4.3)
    raabro (1.4.0)
    racc (1.8.1)
    rack (3.1.8)
    rack-session (2.0.0)
    rack-test (2.1.0)
    rackup (2.2.1)
    rails (8.0.0)
    rails-dom-testing (2.2.0)
    rails-html-sanitizer (1.6.0)
    railties (8.0.0)
    rake (13.2.1)
    rdoc (6.7.0)
    reline (0.5.11)
    securerandom (0.3.2)
    solid_cable (3.0.2)
    solid_cache (1.0.6)
    solid_queue (1.0.1)
    sqlite3 (2.2.0)
    sshkit (1.23.2)
    stimulus-rails (1.3.4)
    stringio (3.1.2)
    thor (1.3.2)
    thruster (0.1.9)
    timeout (0.4.2)
    turbo-rails (2.0.11)
    tzinfo (2.0.6)
    tzinfo-data (1.2024.2)
    uri (1.0.2)
    useragent (0.16.10)
    websocket-driver (0.7.6)
    websocket-extensions (0.1.5)
    zeitwerk (2.7.1)
  * 87 bundler dependencies

We can now cache these by doing licensed cache which will add a .licensed folder. The output will be the same list of 87 dependencies.

Now, you can run licensed status to see what it detects about the dependencies. You will be surprised that it does not show a completely clean result!

$ licensed status
Checking cached dependency records for Hello Rails 8
........F..FFFF...F..F.F...........F......FFF..F.F.F....F.........FFF......F..F...F.FF.
Errors:
* Hello Rails 8.bundler.activerecord
  version: 8.0.0, filename: ./rails/hello8/.licenses/bundler/activerecord.dep.yml, license: other, allowed: false
    - license needs review: other

* Hello Rails 8.bundler.base64
  version: 0.2.0, filename: ./rails/hello8/.licenses/bundler/base64.dep.yml, license: other, allowed: false
    - license needs review: other

* Hello Rails 8.bundler.bcrypt_pbkdf
  version: 1.1.1, filename: ./rails/hello8/.licenses/bundler/bcrypt_pbkdf.dep.yml, license: other, allowed: false
    - license needs review: other

* Hello Rails 8.bundler.benchmark
  version: 0.4.0, filename: ./rails/hello8/.licenses/bundler/benchmark.dep.yml, license: other, allowed: false
    - license needs review: other

* Hello Rails 8.bundler.bigdecimal
  version: 3.1.8, filename: ./rails/hello8/.licenses/bundler/bigdecimal.dep.yml, license: other, allowed: false
    - license needs review: other

* Hello Rails 8.bundler.concurrent-ruby
  version: 1.3.4, filename: ./rails/hello8/.licenses/bundler/concurrent-ruby.dep.yml, license: other, allowed: false
    - license needs review: other

* Hello Rails 8.bundler.date
  version: 3.4.0, filename: ./rails/hello8/.licenses/bundler/date.dep.yml, license: other, allowed: false
    - license needs review: other

* Hello Rails 8.bundler.drb
  version: 2.2.1, filename: ./rails/hello8/.licenses/bundler/drb.dep.yml, license: other, allowed: false
    - license needs review: other

* Hello Rails 8.bundler.logger
  version: 1.6.1, filename: ./rails/hello8/.licenses/bundler/logger.dep.yml, license: other, allowed: false
    - missing license text
    - license needs review: other

* Hello Rails 8.bundler.net-imap
  version: 0.5.1, filename: ./rails/hello8/.licenses/bundler/net-imap.dep.yml, license: other, allowed: false
    - license needs review: other

* Hello Rails 8.bundler.net-pop
  version: 0.1.2, filename: ./rails/hello8/.licenses/bundler/net-pop.dep.yml, license: other, allowed: false
    - license needs review: other

* Hello Rails 8.bundler.net-protocol
  version: 0.2.2, filename: ./rails/hello8/.licenses/bundler/net-protocol.dep.yml, license: other, allowed: false
    - license needs review: other

* Hello Rails 8.bundler.net-smtp
  version: 0.5.0, filename: ./rails/hello8/.licenses/bundler/net-smtp.dep.yml, license: other, allowed: false
    - license needs review: other

* Hello Rails 8.bundler.nio4r
  version: 2.7.4, filename: ./rails/hello8/.licenses/bundler/nio4r.dep.yml, license: other, allowed: false
    - license needs review: other

* Hello Rails 8.bundler.ostruct
  version: 0.6.1, filename: ./rails/hello8/.licenses/bundler/ostruct.dep.yml, license: other, allowed: false
    - license needs review: other

* Hello Rails 8.bundler.racc
  version: 1.8.1, filename: ./rails/hello8/.licenses/bundler/racc.dep.yml, license: other, allowed: false
    - license needs review: other

* Hello Rails 8.bundler.rdoc
  version: 6.7.0, filename: ./rails/hello8/.licenses/bundler/rdoc.dep.yml, license: other, allowed: false
    - license needs review: other

* Hello Rails 8.bundler.reline
  version: 0.5.11, filename: ./rails/hello8/.licenses/bundler/reline.dep.yml, license: other, allowed: false
    - license needs review: other

* Hello Rails 8.bundler.securerandom
  version: 0.3.2, filename: ./rails/hello8/.licenses/bundler/securerandom.dep.yml, license: other, allowed: false
    - license needs review: other

* Hello Rails 8.bundler.stringio
  version: 3.1.2, filename: ./rails/hello8/.licenses/bundler/stringio.dep.yml, license: other, allowed: false
    - license needs review: other

* Hello Rails 8.bundler.timeout
  version: 0.4.2, filename: ./rails/hello8/.licenses/bundler/timeout.dep.yml, license: other, allowed: false
    - license needs review: other

* Hello Rails 8.bundler.uri
  version: 1.0.2, filename: ./rails/hello8/.licenses/bundler/uri.dep.yml, license: other, allowed: false
    - license needs review: other

* Hello Rails 8.bundler.websocket-driver
  version: 0.7.6, filename: ./rails/hello8/.licenses/bundler/websocket-driver.dep.yml, license: other, allowed: false
    - license needs review: other

* Hello Rails 8.bundler.websocket-extensions
  version: 0.1.5, filename: ./rails/hello8/.licenses/bundler/websocket-extensions.dep.yml, license: other, allowed: false
    - license needs review: other


87 dependencies checked, 25 errors found.

Licensed found errors during source enumeration.  Please see https://github.com/github/licensed/tree/master/docs/commands/status.md#status-errors-and-resolutions for possible resolutions.

Ok, we found 25 errors – many in Rails and Ruby dependencies! Is that correct? The answer is Yes and No, and we’ll get into that soon.

Generating a Third Party Notices file

Once we have the dependencies identified and cached, we can use Licensed to also generate a third-pary notices file.

$ licensed notices
Writing notices for Hello Rails 8 to ./rails/hello8/.licenses/NOTICE

You can open up the file and take a look. The picture below shows a snippet of it.

Note that the dependencies identified as errors will also have their licenses included.

Dealing with Errors

There are a few ways to handle errors, depnding on the root cause. At a high level, the general strategy is as in the table below.

# Cause Action
1 Package is internal and you have full ownership Add package to ignored list
2 License is compatible with what you want but has a different name Add license name to allowed list
3 License is compatible with what you want but is not clear/ does not have a proper name Review and add package to reviewed list
4 License is compatible but license file was not found Review and add it to reviewed list
5 License is compatible with what you want but was identified wrongly Review and add it to reviewed list
6 License has additional terms Add package to additional_terms list

In all cases, once you edit and save the configuration file, you can run licensed cache and licensed status again to see the number of errors drop.

Let’s look at a few samples and how to decide what to do.

Case 1: Internal Package

We do not have such a case in our basic Rails app.

Case 2: Compatible License but with a different name

We do not have such a case in our basic Rails app, but this may be when something is called a different license (e.g., “The Software Package License” which is compatible with, as an example, “MIT License”).

Case 3: Compatible License but does not have a clear name

A lot of Ruby’s libraries fall under this group. In our case, examples are uri, timeout, ostruct etc. all of which have a COPYING file that look like this:

Ruby is copyrighted free software by Yukihiro Matsumoto <matz@netlab.jp>.
You can redistribute it and/or modify it under either the terms of the
2-clause BSDL (see the file BSDL), or the conditions below:

1. You may make and give away verbatim copies of the source form of the
    software without restriction, provided that you duplicate all of the
    original copyright notices and associated disclaimers.

2. You may modify your copy of the software in any way, provided that
    you do at least ONE of the following:
...

Case 4: License file was not found

* Hello Rails 8.bundler.logger
  version: 1.6.1, filename: ./rails/hello8/.licenses/bundler/logger.dep.yml, license: other, allowed: false
    - missing license text
    - license needs review: other

We expect that the logger has a friendly license but you can see it is identified as other – we need to review and look into it. A good place to start is the YAML file that is shown in the output: .licenses/bundler/logger.dep.yml – let’s open it and take a look at the contents that licensed cached.

1
2
3
4
5
6
7
8
9
---
name: logger
version: 1.6.1
type: bundler
summary: Provides a simple logging utility for outputting messages.
homepage: https://github.com/ruby/logger
license: other
licenses: []
notices: []

There are a few things to notice here:

  1. Since licenses and notices is empty, it means that it did not pick up a license at all – this is case 4 from our table above.
  2. There is a URL to the package, so we can go there and check the license – when we go to the website, we see that there is a COPYING file and a license file called BSDL (which stands for BSD License)

We can now decide that this is compatible and add logger to the reviewed list in the configuration file.

1
2
3
4
5
6
7
8
# These dependencies have licenses not on the `allowed` list and have been reviewed.
# They will be cached and checked, but will not raise errors or warnings for a
# non-allowed license.  Dependencies on this list will still raise errors if
# license text cannot be found for the dependency.
reviewed:
  bundler:
    - bcrypt-ruby
    - logger

I was unable to resolve this by adding it to reviewed – even after adding it, I got this error:

* Hello Rails 8.bundler.logger
  version: 1.6.1, filename: D:/projects/blog/_posts-trials/rails/hello8/.licenses/bundler/logger.dep.yml, license: other, allowed: false
    - missing license text

Even though I could see the file under the correct folder and I was able to run licensee to detect the license (at 90% accuracy), I was unable to resolve this when running licensed – it could be because I did a gem cleanup that removed some files. Finally, I had to add this to ignore list instead.

Case 5: Compatible License but identified wrongly

Under the hood, Licensed uses a library called Licensee to ascertain the license of a project. Licensee has a database of licenses (e.g., MIT, BSD, etc.) and it tries to match the license file from a project (or dependency) to determine how well it matches with the standard license text. Specifically, it does not read text like “This library is licensed under MIT License” as sufficient to determine the license. It also does not read the Ruby gem specification where the license may be stated.

Since many license files start with a Copyright text with the author names, it removes the first line of copyright text and does a comparison on the rest to see how well it matches with standard license texts. If the percentage of match is more than 95%, it determines that the software license text is essentially the same as the standard text.

Much of the time, this works fine! However, there are cases where this approach does not work so well – the most common case is when there are extra lines (e.g., multiple author copyrights, notice of license change [used to be XYZ and is now MIT], some description, etc.). The match is also significantly worse then if the license text itself is shorter (e.g., MIT or BSD 2-clause, etc.) so that part of the text that matches is even smaller, resulting in a lower % of match.

Let’s look at one such case – activerecord. We all know that activerecord is an integral part of Rails and expect it to be MIT-licensed, as Rails itself but it shows up as an error.

* Hello Rails 8.bundler.activerecord
  version: 8.0.0, filename: D:/projects/blog/_posts-trials/rails/hello8/.licenses/bundler/activerecord.dep.yml, license: other, allowed: false
    - license needs review: other

When you look at activerecord.dep.yml, this is what you see:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
---
name: activerecord
version: 8.0.0
type: bundler
summary: Object-relational mapper framework (part of Rails).
homepage: https://rubyonrails.org
license: other
licenses:
- sources: MIT-LICENSE
  text: |
    Copyright (c) David Heinemeier Hansson

    Arel originally copyright (c) 2007-2016 Nick Kallen, Bryan Helmkamp, Emilio Tagua, Aaron Patterson

    Permission is hereby granted, free of charge, to any person obtaining
    a copy of this software and associated documentation files (the
    "Software"), to deal in the Software without restriction, including
    < -- snip 14 lines -->
- sources: README.rdoc
  text: |-
    Active Record is released under the MIT license:

    * https://opensource.org/licenses/MIT
notices: []

Even though the README.rdoc says it is MIT licensed (and that’s enough for us to add it to the reviewed list), it is not automatically matched with the MIT license due to the file MIT-LICENSE not matching well. If you run licensee on it, you will see why.

$ licensee detect d:\Ruby33-x64\lib\ruby\gems\3.3.0\gems\activerecord-8.0.0
License:        NOASSERTION
Matched files:  MIT-LICENSE, README.rdoc
MIT-LICENSE:
  Content hash:  b8776133984f1a45b28e48f68c21647676d802ed
  License:       NOASSERTION
  Closest non-matching licenses:
    MIT similarity:    84.55%
    MIT-0 similarity:  66.14%
    NCSA similarity:   60.27%
README.rdoc:
  Content hash:  5bc0714f35dc5896f8f22a338fe45ec7aaab364d
  Confidence:    90.00%
  Matcher:       Licensee::Matchers::Reference
  License:       MIT
  Closest non-matching licenses:
    WTFPL similarity:          7.69%
    Zlib similarity:           2.27%
    BlueOak-1.0.0 similarity:  2.12%

You notice that the MIT similarity: is 84.55% – not high enough. This happens due to this line:

Arel originally copyright (c) 2007-2016 Nick Kallen, Bryan Helmkamp, Emilio Tagua, Aaron Patterson

Since it is considered in the match, it reduces the percentage of the confidence. If, for fun, you remove that line and try again, it shows up as a perfect match. If you don’t remove it, but add it to the first line itself (so that you have 1 copyright line), it also detects it as 100% MIT match.

So, in this case, we can add it to the reviewed list so that it is not detected as an error.

1
2
3
4
5
6
7
8
# These dependencies have licenses not on the `allowed` list and have been reviewed.
# They will be cached and checked, but will not raise errors or warnings for a
# non-allowed license.  Dependencies on this list will still raise errors if
# license text cannot be found for the dependency.
reviewed:
  bundler:
    - bcrypt-ruby
    - activerecord

Another similar example is websocket-driver which is licensed under Apache License, Version 2.0 but the text in the license file points you to the license text rather than including it. So, again, licensee fails to assign the license correctly.

Copyright 2014-2020 James Coglan

Licensed under the Apache License, Version 2.0 (the "License"); you may not use
this file except in compliance with the License. You may obtain a copy of the
License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied. See the License for the
specific language governing permissions and limitations under the License.

Case 6: Additional terms

There is already an example in the configuration file.

Conclusion and Further Reading

We have gone through setting up and running Licensed against a fresh Rails project. We also looked at some of the problems and how to resolve those issues.

Beyond what is covered here, also take a look at:

  • Licensed on GitHub: https://github.com/github/licensed
  • Status errors and resolutions: https://github.com/github/licensed/blob/main/docs/commands/status.md#status-errors-and-resolutions
  • Licensed configuration: https://github.com/github/licensed/blob/main/docs/configuration.md
  • Reviewing Dependencies: https://github.com/github/licensed/blob/main/docs/configuration/reviewing_dependencies.md
  • Licensee and how it works: https://github.com/licensee/licensee/
  • Problems with using licensee for Ruby libraries: on licensee, and also on choosealicense

If you find something interesting, do share. You can comment below, or connect with onghu@x or @onghu@ruby.social on Mastodon to discuss further.

comments powered by Disqus