blob: 1a14b5b97ca57372d2cdd97dbd243f928725f21f [file] [log] [blame]
<!DOCTYPE html>
<html lang="en">
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-112407000-2"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag() {
dataLayer.push(arguments);
}
gtag('js', new Date());
gtag('config', 'UA-112407000-2');
</script>
<!-- Google Tag Manager -->
<script>
(function (w, d, s, l, i) {
w[l] = w[l] || [];
w[l].push({
'gtm.start': new Date().getTime(),
event: 'gtm.js'
});
var f = d.getElementsByTagName(s)[0],
j = d.createElement(s),
dl = l != 'dataLayer' ? '&l=' + l : '';
j.async = true;
j.src =
'https://www.googletagmanager.com/gtm.js?id=' + i + dl;
f.parentNode.insertBefore(j, f);
})(window, document, 'script', 'dataLayer', 'GTM-KS8HHSF');
</script>
<!-- End Google Tag Manager -->
<head>
<title>Checking Node.js sub-dependencies licenses for usage and redistribution | Codewind</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="Reusing Node.js modules from NPM is technically easy, but understanding the commercial legal usage and redistribution implications can be a ‘black art’.">
<meta name="keywords" content="Eclipse, docker, container, devops, applications, development, iteration, microservices, cloud, services, rapid, integrated"/>
<link rel="icon" type="image/png" sizes="16x16" href="images/favicon/favicon-16x16.png">
<link rel="icon" type="image/png" sizes="32x32" href="images/favicon/favicon-32x32.png">
<link href="https://fonts.googleapis.com/css?family=IBM+Plex+Sans:300,400,600&display=swap" rel="stylesheet">
<!-- Bootstrap CSS CDN -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"
integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet" />
<link rel="stylesheet" href="css/styles.css">
<link rel="stylesheet" href="css/docs.css">
<link rel="stylesheet" href="css/learn.css">
<link rel="stylesheet" href="css/blog.css">
<link rel="stylesheet" href="css/guides.css">
<link rel="stylesheet" href="css/search.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.17.1/themes/prism.min.css">
</head>
<body data-spy="scroll" data-target="#toc">
<!-- Google Tag Manager (noscript) -->
<noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-KS8HHSF"
height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
<!-- End Google Tag Manager (noscript) -->
<div class="main">
<!-- Bootstrap NavBar -->
<nav class="navbar navbar-expand-xl navbar-light cw-banner fixed-top" aria-label="topnav">
<button class="navbar-toggler navbar-toggler-right" type="button" data-toggle="collapse"
data-target="#navbarNavDropdown" aria-controls="navbarNavDropdown" aria-expanded="false"
aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<a class="navbar-brand" href="/codewind/">
<img alt="Codewind logo image" title="Codewind logo image" src="images/header/header-logo.svg" class="cw-header-logo" alt="">
</a>
<div class="collapse navbar-collapse justify-content-end cw-navbar-padding" id="navbarNavDropdown">
<ul class="navbar-nav cw-navbar-nav">
<li class="nav-item cw-navbar-item cw-header-link-docs">
<a class="nav-link cw-nav-link cw-header-link-text" href="learn.html">Learn</a>
</li>
<li class="nav-item cw-navbar-item cw-header-link-news">
<a class="nav-link cw-nav-link cw-header-link-text" href="news.html">News</a>
</li>
<li class="nav-item cw-navbar-item cw-header-link-blog">
<a class="nav-link cw-nav-link cw-header-link-text" href="blog.html">Blog</a>
</li>
<li class="nav-item cw-navbar-item cw-header-link-guides">
<a class="nav-link cw-nav-link cw-header-link-text" href="guides.html">Guides</a>
</li>
<form class="form-inline my-2 my-lg-0 cw-navbar-item" action="/codewind/search.html" method="get">
<svg class="bi bi-search" width="1em" height="1em" viewBox="0 0 16 16" fill="black" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" d="M10.442 10.442a1 1 0 011.415 0l3.85 3.85a1 1 0 01-1.414 1.415l-3.85-3.85a1 1 0 010-1.415z" clip-rule="evenodd"/>
<path fill-rule="evenodd" d="M6.5 12a5.5 5.5 0 100-11 5.5 5.5 0 000 11zM13 6.5a6.5 6.5 0 11-13 0 6.5 6.5 0 0113 0z" clip-rule="evenodd"/>
</svg>
<input id="nav-search" class="form-control mr-sm-2" type="text" id="search-box" name="query" placeholder="Search">
</form>
<li class="nav-item cw-navbar-item cw-header-link">
<a class="nav-link cw-nav-link" href="https://github.com/eclipse/codewind"><img alt="Codewind Github" class="banner-image" title="Codewind Github" data-toggle="tooltip" data-placement="top" id="cw_github_stars" title="..." src="images/header/github.svg"/></a>
</li>
<li class="nav-item cw-navbar-item cw-header-link">
<a class="nav-link cw-nav-link" href="https://twitter.com/EclipseCodewind"><img alt="Codewind Twitter" class="banner-image" title="Codewind Twitter" src="images/header/twitter.png"/></a>
</li>
<li class="nav-item cw-navbar-item cw-header-link">
<a class="nav-link cw-nav-link" href="https://mattermost.eclipse.org/eclipse/channels/eclipse-codewind"><img alt="Codewind Mattermost" class="banner-image" title="Codewind Mattermost" src="images/header/mattermost.png"/></a>
</li>
<li class="nav-item cw-navbar-item cw-header-link">
<a class="nav-link cw-nav-link" href="https://www.youtube.com/channel/UCnKCVK6RFDyHFqUmXlAhCHQ"><img alt="Codewind YouTube" class="banner-image" title="Codewind YouTube" src="images/header/youtube.png"/></a>
</li>
<!-- li class="nav-item cw-navbar-item" id="download-li">
<button onClick="window.location.href='https://microclimate.dev/download/codewind';" type="button" class="btn cw-download-button">Download</button>
<a href="https://microclimate.dev/download/codewind" class="nav-link cw-nav-link cw-download-link" href="#">Download</a>
</li> -->
<!-- Smaller devices menu END -->
</ul>
</div>
</nav>
<!-- End Bootstrap NavBar -->
<div class="row" id="post-row">
<div class="col-xs-12 col-lg-1"></div>
<div class="py-5 px-5 col-xs-12 col-lg-10">
<div class="cw-blog-spacer"></div>
<div id="post-content">
<h1>Checking Node.js sub-dependencies licenses for usage and redistribution</h1>
<p>30 Oct 2019 - Nik Canvin</p>
<div role="main"><p>Reusing Node.js modules from NPM is technically easy, but understanding the commercial legal usage and redistribution implications can be a ‘black art’.</p>
<p style="text-align: center;"><img src="images/blog/npmdependencies_1.png" alt="image of npm" />
<em>‘npm list’ … then scroll and scroll and just keep going</em></p>
<p>Working as a software project manager for the last 2 years on an offering which uses 1000s of open source packages and releases every four weeks, I’ve spent considerable time discovering, understanding and manually working through several pain points regarding open source (OS) sub-dependency identification and licensing challenges.</p>
<p><em>Note: In my case I needed automation to keep up with both my project’s consumption of new OS packages, as well as the OS community’s appetite to continuously update their packages. A new microservice was developed for use by my team, which I’ve shared in a separate blog here: “<a href="/codewind/a-new-microservice-to-provide-node-js-sub-dependency-license-insights.html">A new microservice to provide Node.js sub-dependency license insights</a>.”</em></p>
<p style="text-align: center;"><img src="images/blog/npmdependencies_2.png" alt="image of license chcker" width="800px" />
<em>Enter a package name and version to check (left window) and license results (right window)</em></p>
<p>However, for anyone in a sinking ‘sub-dependency nightmare’ boat and without a magic microservice, the main purpose of this blog is to share the four main pain-points I encountered and the approaches I learned to overcome them.</p>
<h3 id="pain-point-1-developers-start-using-open-source-sub-dependencies-that-have-inappropriate-licenses">Pain point 1: Developers start using open source sub-dependencies that have inappropriate licenses</h3>
<p>When choosing open source, developers usually focus on capability and community popularity (downloads), then the declared license provided and maybe top level sub-dependency license compatibility, but unforeseen legal issues/risks buried deeper in sub-dependencies may surface late in the development process, causing the following:</p>
<ul>
<li>Best case: wasted time (developer, code committer, IP attorney, project manager), to assist with investigation and/or mitigation.</li>
<li>Medium case: removal of the offending open source, leading to reworking code, wasting time and delaying feature release (maybe even contamination of the developer environment if wrongly downloaded).</li>
<li>Worst case: loss at legal litigation with executive level consequences.
Should the developer either directly or request management to investigate the potential use of a package with sub-dependencies, then there is no quick easy way to overcome the following three additional pain-points.</li>
</ul>
<p>Should the developer either directly or request management to investigate the potential use of a package with sub-dependencies, then there is no quick easy way to overcome the following three additional pain-points.</p>
<h3 id="pain-point-2-compile-an-accurate-listing-of-production-sub-dependencies-with-versions">Pain point 2: Compile an accurate listing of ‘production’ sub-dependencies with versions</h3>
<p>There are several things to consider here:</p>
<ol>
<li>In a multi-person development team, don’t ask multiple people to manually provide listings of sub-dependencies being consumed, as you’ll likely get an inconsistent set of packages/versions (see the following bullets), so it’s often best to go directly to the source code, understand how it’s built and do it yourself. The learning curve for a non-technical project manager is pretty low, so don’t be afraid.</li>
<li>If the offering is built, using ‘npm ci’ (clean install), then the sub-dependency tree used will exactly match the package names/versions listed in the project package-lock.json file.</li>
<li>If the offering is not built using ‘npm ci’, then the actual versions used are generated at install time following semantic rules in the package.json instead. In this case, good automation is key to ensure when you release you can quickly provide the most current license and copyright notices possible.</li>
<li>Also when doing the ‘npm install’, using the ‘-production’ flag will make sure only production dependencies (not development only dependencies) are installed, as those are the ones that commercial offering license and notices files need to cater for.</li>
<li>If your offering spans multiple code repositiories (such as git repos), make sure you know which are the ones that will be used in the actual offering build. Large offerings with distributed agile development teams often spawn many repositories, which can easily become obsolete or even never contain final offering code.</li>
<li>Delta processing (only handling what’s changed since the last release) is a common approach to minimise pain, however, this can all too easily result in missed packages never being picked up in the future.</li>
</ol>
<h3 id="pain-point-3-compile-an-accurate-understanding-of-sub-dependency-licenses-and-copyright-notices">Pain point 3: Compile an accurate understanding of sub-dependency licenses and copyright notices</h3>
<p>Again this is not quite as easy as it sounds:</p>
<ol>
<li>The good news for Node.js is that the community is good at declaring the license for a module in the package.json. I used a ‘<a href="https://www.npmjs.com/package/license-checker" target="_blank">license-checker</a>’ module on NPM, which does a great job reporting the declared license and in the absence of a declaration, it parses targeted files and guesses the license.</li>
<li>However, it’s possible that code under less commercially redistribution friendly licenses lurks in package files, so it is advantageous to check all files for ‘risky keywords’ such as ‘GPL’.</li>
<li>Some packages release under ‘dual’ license terms, so you need to do your due diligence to ensure that you can use a package under the accepted license terms for your offering.</li>
<li>Each of the cases above can require manual investigation, so rediscovery of risks and issues can result in wasted time, especially when packages with trickier cases release frequently.</li>
</ol>
<h3 id="pain-point-4-if-you-are-required-to-follow-a-legal-ip-clearance-process-then-ensuring-only-previously-uncleared-packages-are-checked-and-cleared">Pain point 4: If you are required to follow a legal IP clearance process*, then ensuring only previously uncleared packages are checked and cleared</h3>
<p>*An example ‘legal IP clearance process’ is the <a href="https://www.eclipse.org/projects/handbook/#ip" target="_blank">Eclipse IP process</a>, which is required for Eclipse projects.
I found the tricky parts here, are:</p>
<ul>
<li>Maintaining a list of package versions previously cleared, then using it to help developers choose which open source packages to use in future and also when processing previously uncleared packages at release time.</li>
<li>When producing the final offering license and copyright notices for a release, then all the packages present should be included, not just the delta in the bullet above. You should also be able to exclude any packages no longer present since the last release.</li>
</ul>
<h3 id="in-summary">In summary</h3>
<p>As the use of open source continues to balloon with the exponential growth of sub-dependencies (see this 30 billion monthly downloads <a href="https://blog.npmjs.org/post/180868064080/this-year-in-javascript-2018-in-review-and-npms" target="_blank">NPM blog</a>), coupled with the need pick up the latest security vulnerability fixes in the latest package versions, automation is the obvious way to ensure any subsequent legal risks are consciously mitigated.</p>
<p><img src="images/blog/npmdependencies_3.png" alt="image of Codewind" width="800px" /></p>
<p>Using the Eclipse Codewind developer tools, the automation in this case was implemented as a microservice to enable the widest possible reuse within my company, either as-is or as part of other parties’ automation workflows. Find out more in this related blog: “<a href="/codewind/a-new-microservice-to-provide-node-js-sub-dependency-license-insights.html">A new microservice to provide Node.js sub-dependency license insights.</a></p>
<p>Node.js is not the only language with challenging sub-dependency package identification and licensing risks, so similar work drilling into other languages is in progress.</p>
<p>As checking Node.js sub-dependency licenses and copyrights is only part of the legal work required for typical release (other work being to check for other language packages, as well as creating final license and notices files), further microservices are also in progress to automate many other legal and non-legal offering release steps.</p>
</div>
</div>
</div>
<div class="col-xs-12 col-lg-1"></div>
</div>
<!-- footer row -->
<footer>
<div id="footer-div-mobile">
<div class="row">
<div class="col-sm-12 text-center">
<span>Useful Links:</span>
<br/><br/>
<a class="cw-footer-links" href="http://www.eclipse.org">Eclipse Foundation</a><br/>
<a class="cw-footer-links" href="http://www.eclipse.org/legal/privacy.php">Privacy Policy</a><br/>
<a class="cw-footer-links" href="http://www.eclipse.org/legal/termsofuse.php">Website Terms of Use</a><br/>
<a class="cw-footer-links" href="http://www.eclipse.org/legal/copyright.php">Copyright Agent</a><br/>
<a class="cw-footer-links" href="http://www.eclipse.org/legal">Legal</a><br/>
</div>
</div>
<div class="cw_footer_display_flex cw-footer-same-height cw-footer-center">
<div class="cw_footer_display_icons row">
<div class="cw-footer-col text-center col-md-3 col-sm-6 col-xs-12">
<div>
<div>
<a href="mailto:codewind-dev@eclipse.org"><img alt="Send us an email" title="Send us an email" src="images/footer/email-icon.svg" class="cw-logo" /></a>
</div>
</div>
</div>
<div class="cw-footer-col text-center col-md-3 col-sm-6 col-xs-12">
<div>
<div>
<a href="https://twitter.com/EclipseCodewind"><img alt="Codewind Twitter" title="Codewind Twitter" src="images/footer/twitter-logo.svg" class="cw-logo" /></a>
</div>
</div>
</div>
<div class="cw-footer-col text-center col-md-3 col-sm-6 col-xs-12">
<div>
<div>
<a href="https://github.com/eclipse/codewind"><img alt="Codewind Github" title="Codewind Github" src="images/footer/github-logo.svg"
class="cw-logo" /></a>
</div>
</div>
</div>
<div class=" cw-footer-col text-center col-md-3 col-sm-6 col-xs-12">
<div>
<div>
<a href="https://mattermost.eclipse.org/eclipse/channels/eclipse-codewind"><img alt="Codewind Mattermost" title="Codewind Mattermost" src="images/footer/mattermost-logo.png" class="cw-logo-mm" /></a>
</div>
</div>
</div>
<div class="cw-footer-col text-center col-md-3 col-sm-6 col-xs-12">
<div>
<div>
<a href="https://www.youtube.com/channel/UCnKCVK6RFDyHFqUmXlAhCHQ"><img alt="Codewind YouTube" title="Codewind YouTube" src="images/footer/youtube-logo-dark.svg"
class="cw-logo" /></a>
</div>
</div>
</div>
<div class="cw-footer-col text-center col-md-3 col-sm-6 col-xs-12">
<div>
<div>
<a href="http://www.eclipse.org"><img class="cw-logo-eclipse-mobile" alt="Eclipse" title="Eclipse" src="images/footer/eclipse.svg"/></a>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="row cw-footer-desktop" id="footer-div">
<div class="cw-footer-left">
<div class="px-5 cw-font-12>
<span class="cw-font-14">Useful Links:</span>
<br/><br/>
<a class="cw-footer-links" href="http://www.eclipse.org">Eclipse Foundation</a><br/>
<a class="cw-footer-links" href="http://www.eclipse.org/legal/privacy.php">Privacy Policy</a><br/>
<a class="cw-footer-links" href="http://www.eclipse.org/legal/termsofuse.php">Website Terms of Use</a><br/>
<a class="cw-footer-links" href="http://www.eclipse.org/legal/copyright.php">Copyright Agent</a><br/>
<a class="cw-footer-links" href="http://www.eclipse.org/legal">Legal</a><br/>
</div>
</div>
<div class="cw-footer-border-right"></div>
<div class="cw_footer_display_flex cw-footer-same-height cw-footer-center">
<div class="cw_footer_display_icons">
<div class="cw-footer-col text-center">
<div>
<div>
<a href="mailto:codewind-dev@eclipse.org"><img alt="Send us an email" title="Send us an email" src="images/footer/email-icon.svg" class="cw-logo" /></a>
</div>
</div>
</div>
<div class="cw-footer-col text-center">
<div>
<div>
<a href="https://twitter.com/EclipseCodewind"><img alt="Codewind Twitter" title="Codewind Twitter" src="images/footer/twitter-logo.svg" class="cw-logo" /></a>
</div>
</div>
</div>
<div class="cw-footer-col text-center">
<div>
<div>
<a href="https://github.com/eclipse/codewind"><img alt="Codewind Github" title="Codewind Github" src="images/footer/github-logo.svg"
class="cw-logo" /></a>
</div>
</div>
</div>
<div class=" cw-footer-col text-center">
<div class="cw-logo-mm" >
<div class="cw-logo-mm" >
<a href="https://mattermost.eclipse.org/eclipse/channels/eclipse-codewind"><img alt="Codewind Mattermost" title="Codewind Mattermost" src="images/footer/mattermost-logo.png" class="cw-logo-mm" /></a>
</div>
</div>
</div>
<div class="cw-footer-col text-center">
<div>
<div>
<a href="https://www.youtube.com/channel/UCnKCVK6RFDyHFqUmXlAhCHQ"><img alt="Codewind YouTube" title="Codewind YouTube" src="images/footer/youtube-logo-dark.svg"
class="cw-logo" /></a>
</div>
</div>
</div>
</div>
</div>
<div class="cw-footer-border-right"></div>
<div class="cw-footer-right cw-footer-same-height cw-footer-vcenter">
<div class="cw-footer-eclipse-img cw-footer-same-height px-5 ">
<a href="http://www.eclipse.org">
<img alt="Eclipse" title="Eclipse" src="images/footer/eclipse.svg"/>
</a>
</div>
</div>
</div>
</footer>
<!-- footer row END -->
<!-- Jquery -->
<script
src="https://code.jquery.com/jquery-3.4.1.min.js"
integrity="sha256-CSXorXvZcTkaix6Yvo6HppcZGetbYMGWSFlBw8HfCJo="
crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js"
integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous">
</script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js"
integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous">
</script>
<!-- Font Awesome JS -->
<script defer src="https://use.fontawesome.com/releases/v5.0.13/js/solid.js"
integrity="sha384-tzzSw1/Vo+0N5UhStP3bvwWPq+uvzCMfrN1fEFe+xBmv1C/AtVX5K0uZtmcHitFZ" crossorigin="anonymous">
</script>
<script defer src="https://use.fontawesome.com/releases/v5.0.13/js/fontawesome.js"
integrity="sha384-6OIrr52G08NpOFSZdxxz1xdNSndlD4vdcf/q2myIUVO0VsqaGHJsB0RaBE01VTOY" crossorigin="anonymous">
</script>
<script src="js/jquery.matchHeight-min.js"></script>
<script src="js/index.js"></script>
<script src="js/docs.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.17.1/prism.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.17.1/components/prism-docker.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.17.1/components/prism-json.min.js"></script>
</div>
</body>
</html>