Reading a LICENSE file nobody declared
Most npm packages put an SPDX id in package.json and you never look further. Some don’t: the field is missing, or it says SEE LICENSE IN LICENSE. Then the file is the licence, and a few words in it decide whether you owe the author a notice.
This page explains how these licences are commonly read. It is not legal advice; for a product decision with money on it, ask a lawyer who handles open source.
What package.json can say instead of an id
npm’s documentation asks for an SPDX expression such as MIT or (MIT OR Apache-2.0), and allows two strings that are not licences at all:
"license": "SEE LICENSE IN LICENSE.txt"
a licence with no SPDX id, or a custom one: read the file
"license": "UNLICENSED"
no permission to use it under any terms
"licenses": [{ "type": "MIT" }]
the old array and object styles, now deprecated
(no "license" field at all)
the label is missing; the LICENSE file may still be there
A checker that only reads the field reports the first and the last as unknown, and every one of them lands on someone’s desk. Usually the package has a perfectly ordinary LICENSE file next to its package.json. The question is which licence that file actually is.
Look-alikes with different obligations
Several short licences share most of their words. The few that differ are the ones that tell you what to ship:
| Licence | How it differs | Notice in your app? |
|---|---|---|
| MIT | The base text: the copyright notice and permission notice “shall be included in all copies or substantial portions of the Software.” | Yes |
| MIT-0 | The same grant, with that condition removed | No |
| X11 | MIT plus: the name of the X Consortium “shall not be used in advertising or otherwise to promote” the software without written permission | Yes, and no use of the name |
| JSON | MIT plus one line: “The Software shall be used for Good, not Evil.” The FSF lists it as non-free because that line restricts use | Yes, and the use restriction |
| ISC | Permission granted “provided that the above copyright notice and this permission notice appear in all copies” | Yes |
| 0BSD | The ISC text without that proviso | No |
| BSD-3-Clause | Binary redistributions reproduce the notice in the documentation; no use of the authors’ names to endorse your product | Yes |
| BSD-4-Clause | BSD-3 plus the advertising clause: advertising materials that mention the software must display an acknowledgement. The FSF lists it as incompatible with the GPL | Yes, and in advertising |
Calling MIT-0 “MIT” costs you a line in a notices file you didn’t need. Calling JSON or BSD-4-Clause “MIT” or “BSD-3” hides a condition that someone should have read.
Why matching a phrase is not enough
MIT, MIT-0, X11 and JSON all open with the same sentence. Put side by side, the only lines that differ are these:
MIT ... to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions...
MIT-0 ... to whom the Software is furnished to do so.
X11 (the MIT text, then)
Except as contained in this notice, the name of the
X Consortium shall not be used in advertising ...
JSON (the MIT text, then)
The Software shall be used for Good, not Evil.
ISC and 0BSD are the same kind of pair, one proviso apart, and BSD-4-Clause is BSD-3-Clause with one clause more:
ISC Permission to use, copy, modify, and/or distribute this
software for any purpose with or without fee is hereby
granted, provided that the above copyright notice and
this permission notice appear in all copies.
0BSD Permission to use, copy, modify, and/or distribute this
software for any purpose with or without fee is hereby
granted.
BSD-4 3. All advertising materials mentioning features or use
of this software must display the following
acknowledgement: This product includes software
developed by the organization.
A checker that recognises MIT by its opening sentence calls all four MIT. licensegate did exactly that up to version 0.5.0, and read a BSD-4-Clause file as BSD-3-Clause for the same reason. Real files make it harder: authors rewrap the lines, swap quote marks, write “licence” for “license”, add a title and a copyright line, or paste two licences into one file.
How licensegate 0.6.0 compares the text
- Normalise. Lower case, punctuation and line breaks dropped, “licence” read as “license”, list numbers ignored, close to what the SPDX matching guidelines ask. Placeholders in the reference texts, such as the copyright holder’s name, are left out.
- Compare by three-word sequences against the reference texts of the SPDX licence list 3.29.0 for 17 short licences: MIT, MIT-0, X11, JSON, ISC, 0BSD, the BSD-2, BSD-3, BSD-3-Clear and BSD-4 clauses, Zlib, Unlicense, BSL-1.0, WTFPL, NCSA, PostgreSQL and UPL-1.0. The score is the share of the reference text found in the file, and it has to reach 75%. A title or a copyright line on top costs nothing.
- Prefer the longer relative only when its extra words are there. An X11 or JSON file contains all of MIT, so MIT scores 100% on it too. The longer licence wins when the file holds at least half of what it adds to the shorter one; an MIT-0 file is missing the MIT condition, so MIT doesn’t reach the top score there.
- Long licences by their title line. GPL, LGPL, AGPL, Apache, MPL, EPL, EUPL, CDDL and others are recognised by a heading that stands alone, so a GPL title quoted inside another licence’s text doesn’t count.
- Two licences in one file are joined with
ORwhen the file says you may choose (“dual licensed”, “at your option”), and withANDotherwise.
The same release knows every id on the SPDX list (740 licences and 86 exceptions) in any casing, so apache-2.0 in a package.json is read as Apache-2.0. About a third of those ids are rare one-off licences licensegate doesn’t put in a category; they come out as review, never as ok.
What a run shows
Four dependencies with no usable licence field. Only one needs a person:
npx -y https://licensegate.org/dl/licensegate.tgz --mode distributed --prod
licensegate 0.6.0 · mode distributed · production only · 4 packages from node_modules
REVIEW json-min@0.4.0 JSON
source-available, non-commercial or use-restricted terms: check what the licence forbids
3 ok · 1 to review · 0 blocking
Not legal advice. Grey cases (linking, plug-ins, dual licences) say REVIEW on purpose.
With --format json every row says where the licence came from. "source": "text" and the file name mark the ones read from a LICENSE file instead of package.json:
{ "name": "dual-util", "version": "3.0.1", "license": "MIT OR Apache-2.0",
"source": "text", "file": "LICENSE", "verdict": "ok" },
{ "name": "json-min", "version": "0.4.0", "license": "JSON",
"source": "text", "file": "LICENSE.txt", "verdict": "review" },
{ "name": "str-pad", "version": "2.1.0", "license": "0BSD",
"source": "text", "file": "LICENSE.md", "verdict": "ok" },
{ "name": "tiny-hash", "version": "1.0.3", "license": "MIT-0",
"source": "text", "file": "LICENSE", "verdict": "ok" }
The rows are trimmed to the fields that matter here; the full output also carries the category, the path and whether the package is a dev dependency.
What still needs a person
- Added terms. The score measures how much of a known licence the file contains, not what the author added to it. An MIT text with a sentence of the author’s own at the end is still reported as MIT. If a package matters to you, open the files marked
"source": "text"; there are usually few. - No match. A file that resembles none of the reference texts and has no recognisable title comes out as review, with the declared string (
SEE LICENSE IN…) or “no licence”. Read it, ask the author, or replace the package. - UNLICENSED or no file at all. Without a licence, the default is that you have no permission to redistribute the code. A missing label is not a grant.
Matching text by similarity is not new: GitHub’s licensee compares licence files with a Sørensen–Dice coefficient. What licensegate adds is the step after: the licence it finds is judged against how you ship, and the result goes into the same CI exit code, SBOM and sealed report as every declared licence.