davglass / license-checker

Check NPM package licenses
Other
1.61k stars 215 forks source link

highcharts license incorrectly reported using highcharts-server license URL #254

Open loop-evgeny opened 3 years ago

loop-evgeny commented 3 years ago

package.json file:

{
  "dependencies": {
    "react-highcharts": "^16.1.0"
  }
}

Run

npm i
license-checker --packages "highcharts@6.2.0"

Output:

└─ highcharts@6.2.0
   ├─ licenses: Custom: https://www.npmjs.com/package/highcharts-server
...

However, node_modules/highcharts/package.json has this:

"license": "https://www.highcharts.com/license"

The difference is significant, because highcharts-server is MIT-licensed, while highcharts has a commercial license.

joelwkall commented 3 years ago

I've been investigating this, and found some interesting tidbits:

strobocopter commented 3 months ago

Not sure if anyone will ever look into this discussion again, but I just stumbled across this issue in combination with a custom license that was used in one of my packages.

I think I was able to track it down to this portion of code from index.js of license-checker (Lines 151 to 172 in Version: 25.0.1)

if (json.path && fs.existsSync(json.path)) {
        dirFiles = fs.readdirSync(json.path);
        files = licenseFiles(dirFiles);

        noticeFiles = dirFiles.filter(function(filename) {
            filename = filename.toUpperCase();
            var name = path.basename(filename).replace(path.extname(filename), '');
            return name === 'NOTICE';
        });
    }

    files.forEach(function(filename, index) {
        licenseFile = path.join(json.path, filename);
        // Checking that the file is in fact a normal file and not a directory for example.
        /*istanbul ignore else*/
        if (fs.lstatSync(licenseFile).isFile()) {
            var content;
            if (!moduleInfo.licenses || moduleInfo.licenses.indexOf(UNKNOWN) > -1 || moduleInfo.licenses.indexOf('Custom:') === 0) {
                //Only re-check the license if we didn't get it from elsewhere
                content = fs.readFileSync(licenseFile, { encoding: 'utf8' });
                moduleInfo.licenses = license(content);
            }

The "files" array is populated with all the files that are assumed to be related to a license by the licenseFiles function - basically checking if the file is named LICENSE, LICENSE.MD, LICENCE, LICENCE.MD or ... README[.MD]

Now if the currently found moduleInfo.licenses starts with "Custom:", which is the case if the the license-field of the package.json does not carry a valid spdx-Expression, it will replace that moduleInfo.licenses with something found in one of the files above.

If the package happens to contain a readme.md file and that readme.md file happens to contains a URL, the license function would actually match that URL with the IS_URL-Regex and return "Custom: " here:

match = IS_URL.exec(str) || IS_FILE_REFERENCE.exec(str);
        if(match) {
            return 'Custom: ' + match[1];
        } else {
            return null;
        }