Skip to content

Commit

Permalink
feat: output license usage and issues
Browse files Browse the repository at this point in the history
  • Loading branch information
gabidobo committed Feb 3, 2023
1 parent 23c68f6 commit 99aa988
Show file tree
Hide file tree
Showing 5 changed files with 184 additions and 2 deletions.
17 changes: 15 additions & 2 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
const {getDependencyGraph, addDependencyGraphData} = require('sandworm-utils');
const {getDependencyVulnerabilities} = require('./vulnerabilities/dependencies');
const {getDependencyVulnerabilities} = require('./issues/vulnerabilities');
const { getLicenseIssues, getLicenseUsage } = require('./issues/license');
const {buildTree, buildTreemap} = require('./charts');
const {getReports} = require('./vulnerabilities/utils');
const {getReports} = require('./issues/utils');
const csv = require('./charts/csv');

const getReport = async ({
Expand All @@ -24,6 +25,8 @@ const getReport = async ({
onProgress({type: 'start', stage: 'vulnerabilities'});
let dependencyVulnerabilities;
let rootVulnerabilities;
let licenseUsage;
let licenseIssues;

try {
dependencyVulnerabilities = await getDependencyVulnerabilities({
Expand All @@ -41,6 +44,14 @@ const getReport = async ({
} catch (error) {
errors.push(error);
}

try {
licenseUsage = await getLicenseUsage({dependencies: dGraph.prodDependencies});
licenseIssues = await getLicenseIssues({licenseUsage, packageGraph});
} catch (error) {
errors.push(error);
}

onProgress({type: 'end', stage: 'vulnerabilities'});

const options = {
Expand Down Expand Up @@ -85,6 +96,8 @@ const getReport = async ({
dependencyGraph: dGraph,
dependencyVulnerabilities: dependencyVulnerabilities.filter(({findings: {affects}}) => affects.length),
rootVulnerabilities,
licenseUsage,
licenseIssues,
svgs,
csv: csvData,
allDependencies: jsonData,
Expand Down
164 changes: 164 additions & 0 deletions src/issues/license.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
const licenseGroups = require('./licenses.json');
const {getFindings} = require('./utils');

const LICENSE_TYPES = [
'Public Domain',
'Permissive',
'Weakly Protective',
'Strongly Protective',
'Network Protective',
'Uncategorized',
'Invalid',
];

const DEFAULT_POLICY = {
high: ['cat:Network Protective', 'cat:Strongly Protective'],
moderate: ['cat:Weakly Protective'],
};

const typeForLicense = (license) => {
if (!license || license === 'N/A') {
return 'N/A';
}
if (license.includes(' ')) {
// Parse simple AND/OR SPDX expressions
if ((license.match(/\s/g) || []).length === 2) {
let expressionLicenses;
let condition;

if (license.match(/ or /i)) {
condition = 'or';
expressionLicenses = license.replace(/[()]/g, '').split(/ or /i);
} else if (license.match(/ and /i)) {
condition = 'and';
expressionLicenses = license.replace(/[()]/g, '').split(/ and /i);
}

if (expressionLicenses) {
const expressionTypes = [
typeForLicense(expressionLicenses[0]),
typeForLicense(expressionLicenses[1]),
];

if ([expressionTypes[0], expressionTypes[1]].includes('Invalid')) {
return 'Invalid';
}

const aggregateExpressionType =
LICENSE_TYPES[
condition === 'or'
? Math.min(
LICENSE_TYPES.indexOf(expressionTypes[0]),
LICENSE_TYPES.indexOf(expressionTypes[1]),
)
: Math.max(
LICENSE_TYPES.indexOf(expressionTypes[0]),
LICENSE_TYPES.indexOf(expressionTypes[1]),
)
];

return aggregateExpressionType;
}
}
return 'Expression';
}
return licenseGroups.types.find(({licenses}) => licenses.includes(license))?.type || 'Invalid';
};

module.exports = {
typeForLicense,
getLicenseUsage: ({dependencies = []}) => {
const licenseUsage = dependencies.reduce((agg, {name, version, license}) => {
const licenseName = license || 'N/A';

return {
...agg,
[licenseName]: {
meta: {
type: typeForLicense(licenseName),
},
dependencies: (agg[licenseName]?.dependencies || []).concat([{name, version}]),
},
};
}, {});

return licenseUsage;
},

getLicenseIssues: ({licenseUsage, packageGraph}) => {
const issues = [];
const allLicenses = Object.keys(licenseUsage);

if (allLicenses.includes('N/A')) {
issues.push({
severity: 'critical',
title: 'Dependencies have no specified license',
dependencies: licenseUsage['N/A'].dependencies,
});
}

Object.entries(licenseUsage).forEach(([licenseName, {meta, dependencies}]) => {
const licenseType = meta?.type;

if (!licenseName.includes(' ') && !['Expression', 'N/A'].includes(licenseType)) {
if (!licenseGroups.osiApproved.includes(licenseName)) {
issues.push({
severity: 'low',
title: `Dependencies use a license that is not OSI approved: ${licenseName}`,
dependencies,
});
}
}

if (!licenseType || licenseType === 'Uncategorized') {
issues.push({
severity: 'high',
title: `Dependencies use an atypical license: ${licenseName}`,
dependencies,
});
} else if (licenseType === 'Invalid') {
issues.push({
severity: 'high',
title: `Dependencies use an invalid SPDX license: ${licenseName}`,
dependencies,
});
} else if (licenseType === 'Expression') {
issues.push({
severity: 'high',
title: `Dependencies use a custom license expression: ${licenseName}`,
dependencies,
});
}

Object.entries(DEFAULT_POLICY).forEach(([severity, includes]) => {
if (includes.includes(licenseName)) {
issues.push({
severity,
title: `Dependencies use potentially problematic license: ${licenseName}`,
dependencies,
});
} else if (includes.includes(`cat:${licenseType}`)) {
issues.push({
severity,
title: `Dependencies use ${licenseType} license: ${licenseName}`,
dependencies,
});
}
});
});

return issues.reduce(
(agg, {severity, title, dependencies}) =>
agg.concat(
dependencies.map(({name, version}) => ({
severity,
title,
name,
version,
findings: getFindings(packageGraph, name, version),
})),
),
[],
);
},
};
5 changes: 5 additions & 0 deletions src/issues/licenses.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"types": [{"type":"Network Protective","licenses":["AGPL-1.0","AGPL-1.0-only","AGPL-1.0-or-later","AGPL-3.0","AGPL-3.0-only","AGPL-3.0-or-later","CPAL-1.0","EUPL-1.0","EUPL-1.1","EUPL-1.2","RPL-1.1","RPL-1.5","SSPL-1.0"]},{"type":"Uncategorized","licenses":["AAL","Abstyles","Adobe-2006","Adobe-Glyph","ADSL","Afmparse","Aladdin","AMDPLPA","AML","AMPAS","ANTLR-PD","ANTLR-PD-fallback","APAFML","App-s2p","Arphic-1999","Baekmuk","Bahyph","Barr","Bitstream-Vera","blessing","BlueOak-1.0.0","Borceux","BSD-1-Clause","BSD-2-Clause-Patent","BSD-2-Clause-Views","BSL-1.0","BUSL-1.1","bzip2-1.0.5","bzip2-1.0.6","C-UDA-1.0","CAL-1.0","CAL-1.0-Combined-Work-Exception","Caldera","CC-BY-1.0","CC-BY-2.0","CC-BY-2.5","CC-BY-2.5-AU","CC-BY-3.0","CC-BY-3.0-AT","CC-BY-3.0-DE","CC-BY-3.0-IGO","CC-BY-3.0-NL","CC-BY-3.0-US","CC-BY-4.0","CC-BY-NC-1.0","CC-BY-NC-2.0","CC-BY-NC-2.5","CC-BY-NC-3.0","CC-BY-NC-3.0-DE","CC-BY-NC-4.0","CC-BY-NC-ND-1.0","CC-BY-NC-ND-2.0","CC-BY-NC-ND-2.5","CC-BY-NC-ND-3.0","CC-BY-NC-ND-3.0-DE","CC-BY-NC-ND-3.0-IGO","CC-BY-NC-ND-4.0","CC-BY-NC-SA-1.0","CC-BY-NC-SA-2.0","CC-BY-NC-SA-2.0-FR","CC-BY-NC-SA-2.0-UK","CC-BY-NC-SA-2.5","CC-BY-NC-SA-3.0","CC-BY-NC-SA-3.0-DE","CC-BY-NC-SA-3.0-IGO","CC-BY-NC-SA-4.0","CC-BY-ND-1.0","CC-BY-ND-2.0","CC-BY-ND-2.5","CC-BY-ND-3.0","CC-BY-ND-3.0-DE","CC-BY-ND-4.0","CC-BY-SA-1.0","CC-BY-SA-2.0","CC-BY-SA-2.0-UK","CC-BY-SA-2.1-JP","CC-BY-SA-2.5","CC-BY-SA-3.0","CC-BY-SA-3.0-AT","CC-BY-SA-3.0-DE","CC-BY-SA-4.0","CC-PDDC","CDL-1.0","CDLA-Permissive-1.0","CDLA-Permissive-2.0","CDLA-Sharing-1.0","CERN-OHL-1.1","CERN-OHL-1.2","CERN-OHL-P-2.0","CERN-OHL-S-2.0","CERN-OHL-W-2.0","checkmk","COIL-1.0","Community-Spec-1.0","copyleft-next-0.3.0","copyleft-next-0.3.1","CPOL-1.02","Crossword","CrystalStacker","Cube","curl","D-FSL-1.0","diffmark","DL-DE-BY-2.0","DOC","Dotseqn","DRL-1.0","dvipdfm","ECL-1.0","eCos-2.0","eGenix","Elastic-2.0","Entessa","EPICS","etalab-2.0","EUDatagrid","Eurosym","Fair","FDK-AAC","Frameworx-1.0","FreeBSD-DOC","FreeImage","FSFAP","FSFUL","FSFULLR","FSFULLRWD","GD","GFDL-1.1","GFDL-1.1-invariants-only","GFDL-1.1-invariants-or-later","GFDL-1.1-no-invariants-only","GFDL-1.1-no-invariants-or-later","GFDL-1.1-only","GFDL-1.1-or-later","GFDL-1.2","GFDL-1.2-invariants-only","GFDL-1.2-invariants-or-later","GFDL-1.2-no-invariants-only","GFDL-1.2-no-invariants-or-later","GFDL-1.2-only","GFDL-1.2-or-later","GFDL-1.3","GFDL-1.3-invariants-only","GFDL-1.3-invariants-or-later","GFDL-1.3-no-invariants-only","GFDL-1.3-no-invariants-or-later","GFDL-1.3-only","GFDL-1.3-or-later","Giftware","GL2PS","Glide","Glulxe","GLWTPL","gnuplot","HaskellReport","Hippocratic-2.1","HPND","HPND-sell-variant","HTMLTIDY","IBM-pibs","IJG","ImageMagick","Imlib2","Info-ZIP","Intel","Intel-ACPI","Jam","JPNIC","Knuth-CTAN","LAL-1.2","LAL-1.3","Latex2e","Leptonica","LGPLLR","Libpng","libpng-2.0","libselinux-1.0","libtiff","libutil-David-Nugent","LiLiQ-P-1.1","LiLiQ-R-1.1","LiLiQ-Rplus-1.1","Linux-man-pages-copyleft","Linux-OpenIB","LPL-1.0","LPL-1.02","LZMA-SDK-9.11-to-9.20","LZMA-SDK-9.22","MakeIndex","Minpack","MirOS","MIT-Modern-Variant","MIT-open-group","MITNFA","mpi-permissive","mpich2","mplus","MS-LPL","MTLL","MulanPSL-1.0","MulanPSL-2.0","Multics","Mup","NAIST-2003","NBPL-1.0","NCGL-UK-2.0","NCSA","Net-SNMP","NetCDF","Newsletr","NGPL","NICTA-1.0","NIST-PD","NIST-PD-fallback","NLOD-1.0","NLOD-2.0","Noweb","NPL-1.0","NPL-1.1","NPOSL-3.0","NRL","NTP","NTP-0","Nunit","O-UDA-1.0","OCCT-PL","OCLC-2.0","ODC-By-1.0","OFL-1.0","OFL-1.0-no-RFN","OFL-1.0-RFN","OFL-1.1","OFL-1.1-no-RFN","OFL-1.1-RFN","OGC-1.0","OGDL-Taiwan-1.0","OGL-Canada-2.0","OGL-UK-1.0","OGL-UK-2.0","OGL-UK-3.0","OGTSL","OML","OPL-1.0","OPUBL-1.0","OSET-PL-2.1","Parity-6.0.0","Parity-7.0.0","Plexus","PolyForm-Noncommercial-1.0.0","PolyForm-Small-Business-1.0.0","PostgreSQL","PSF-2.0","psfrag","psutils","Python-2.0.1","Qhull","Rdisc","RPSL-1.0","RSA-MD","Saxpath","SCEA","SchemeReport","Sendmail-8.23","SGI-B-1.0","SGI-B-1.1","SGI-B-2.0","SHL-0.5","SHL-0.51","SimPL-2.0","SMLNJ","SMPPL","SNIA","Spencer-86","Spencer-94","Spencer-99","SSH-OpenSSH","SSH-short","StandardML-NJ","SWL","TAPR-OHL-1.0","TCP-wrappers","TMate","TORQUE-1.1","TOSL","TU-Berlin-1.0","TU-Berlin-2.0","UCL-1.0","Unicode-DFS-2015","Unicode-DFS-2016","Unicode-TOU","UPL-1.0","VOSTROM","VSL-1.0","Wsuipa","wxWindows","X11-distribute-modifications-variant","Xerox","Xnet","xpp","XSkat","Zed","Zend-2.0"]},{"type":"Permissive","licenses":["0BSD","AFL-1.1","AFL-1.2","AFL-2.0","AFL-2.1","AFL-3.0","Apache-1.0","Apache-1.1","Apache-2.0","APSL-1.0","Artistic-2.0","Beerware","BSD-2-Clause","BSD-2-Clause-FreeBSD","BSD-2-Clause-NetBSD","BSD-3-Clause","BSD-3-Clause-Attribution","BSD-3-Clause-Clear","BSD-3-Clause-LBNL","BSD-3-Clause-Modification","BSD-3-Clause-No-Military-License","BSD-3-Clause-No-Nuclear-License","BSD-3-Clause-No-Nuclear-License-2014","BSD-3-Clause-No-Nuclear-Warranty","BSD-3-Clause-Open-MPI","BSD-4-Clause","BSD-4-Clause-Shortened","BSD-4-Clause-UC","BSD-Protection","BSD-Source-Code","CECILL-1.0","CECILL-1.1","CECILL-2.0","CECILL-2.1","CECILL-B","CNRI-Jython","CNRI-Python","CNRI-Python-GPL-Compatible","Condor-1.1","DSDP","ECL-2.0","EFL-1.0","EFL-2.0","FTL","ICU","iMatix","ISC","JasPer-2.0","JSON","MIT","MIT-0","MIT-advertising","MIT-CMU","MIT-enna","MIT-feh","Naumen","NLPL","OLDAP-1.1","OLDAP-1.2","OLDAP-1.3","OLDAP-1.4","OLDAP-2.0","OLDAP-2.0.1","OLDAP-2.1","OLDAP-2.2","OLDAP-2.2.1","OLDAP-2.2.2","OLDAP-2.3","OLDAP-2.4","OLDAP-2.5","OLDAP-2.6","OLDAP-2.7","OLDAP-2.8","OpenSSL","PHP-3.0","PHP-3.01","Python-2.0","Ruby","Sendmail","TCL","W3C","W3C-19980720","W3C-20150513","WTFPL","X11","XFree86-1.1","xinetd","Zlib","zlib-acknowledgement","ZPL-1.1","ZPL-2.0","ZPL-2.1"]},{"type":"Strongly Protective","licenses":["APL-1.0","CPL-1.0","EPL-1.0","EPL-2.0","GPL-1.0","GPL-1.0+","GPL-1.0-only","GPL-1.0-or-later","GPL-2.0","GPL-2.0+","GPL-2.0-only","GPL-2.0-or-later","GPL-2.0-with-autoconf-exception","GPL-2.0-with-bison-exception","GPL-2.0-with-classpath-exception","GPL-2.0-with-font-exception","GPL-2.0-with-GCC-exception","GPL-3.0","GPL-3.0+","GPL-3.0-only","GPL-3.0-or-later","GPL-3.0-with-autoconf-exception","GPL-3.0-with-GCC-exception","IPA","MS-RL","ODbL-1.0","OSL-1.0","OSL-1.1","OSL-2.0","OSL-2.1","OSL-3.0","QPL-1.0","Vim"]},{"type":"Public Domain","licenses":["CC0-1.0","PDDL-1.0","SAX-PD","Unlicense"]},{"type":"Weakly Protective","licenses":["APSL-1.1","APSL-1.2","APSL-2.0","Artistic-1.0","Artistic-1.0-cl8","Artistic-1.0-Perl","BitTorrent-1.0","BitTorrent-1.1","CATOSL-1.1","CDDL-1.0","CDDL-1.1","CECILL-C","ClArtistic","CUA-OPL-1.0","ErlPL-1.1","gSOAP-1.3b","Interbase-1.0","IPL-1.0","LGPL-2.0","LGPL-2.0+","LGPL-2.0-only","LGPL-2.0-or-later","LGPL-2.1","LGPL-2.1+","LGPL-2.1-only","LGPL-2.1-or-later","LGPL-3.0","LGPL-3.0+","LGPL-3.0-only","LGPL-3.0-or-later","LPPL-1.0","LPPL-1.1","LPPL-1.2","LPPL-1.3a","LPPL-1.3c","Motosoto","MPL-1.0","MPL-1.1","MPL-2.0","MPL-2.0-no-copyleft-exception","MS-PL","NASA-1.3","Nokia","NOSL","RHeCos-1.1","RSCPL","SISSL","SISSL-1.2","Sleepycat","SPL-1.0","SugarCRM-1.1.3","Watcom-1.0","YPL-1.0","YPL-1.1","Zimbra-1.3","Zimbra-1.4"]}],
"osiApproved": ["0BSD","AAL","AFL-1.1","AFL-1.2","AFL-2.0","AFL-2.1","AFL-3.0","AGPL-3.0","AGPL-3.0-only","AGPL-3.0-or-later","Apache-1.1","Apache-2.0","APL-1.0","APSL-1.0","APSL-1.1","APSL-1.2","APSL-2.0","Artistic-1.0","Artistic-1.0-cl8","Artistic-1.0-Perl","Artistic-2.0","BSD-1-Clause","BSD-2-Clause","BSD-2-Clause-Patent","BSD-3-Clause","BSD-3-Clause-LBNL","BSL-1.0","CAL-1.0","CAL-1.0-Combined-Work-Exception","CATOSL-1.1","CDDL-1.0","CECILL-2.1","CERN-OHL-P-2.0","CERN-OHL-S-2.0","CERN-OHL-W-2.0","CNRI-Python","CPAL-1.0","CPL-1.0","CUA-OPL-1.0","ECL-1.0","ECL-2.0","EFL-1.0","EFL-2.0","Entessa","EPL-1.0","EPL-2.0","EUDatagrid","EUPL-1.1","EUPL-1.2","Fair","Frameworx-1.0","GPL-2.0","GPL-2.0+","GPL-2.0-only","GPL-2.0-or-later","GPL-3.0","GPL-3.0+","GPL-3.0-only","GPL-3.0-or-later","GPL-3.0-with-GCC-exception","HPND","Intel","IPA","IPL-1.0","ISC","Jam","LGPL-2.0","LGPL-2.0+","LGPL-2.0-only","LGPL-2.0-or-later","LGPL-2.1","LGPL-2.1+","LGPL-2.1-only","LGPL-2.1-or-later","LGPL-3.0","LGPL-3.0+","LGPL-3.0-only","LGPL-3.0-or-later","LiLiQ-P-1.1","LiLiQ-R-1.1","LiLiQ-Rplus-1.1","LPL-1.0","LPL-1.02","LPPL-1.3c","MirOS","MIT","MIT-0","MIT-Modern-Variant","Motosoto","MPL-1.0","MPL-1.1","MPL-2.0","MPL-2.0-no-copyleft-exception","MS-PL","MS-RL","MulanPSL-2.0","Multics","NASA-1.3","Naumen","NCSA","NGPL","Nokia","NPOSL-3.0","NTP","OCLC-2.0","OFL-1.1","OFL-1.1-no-RFN","OFL-1.1-RFN","OGTSL","OLDAP-2.8","OSET-PL-2.1","OSL-1.0","OSL-2.0","OSL-2.1","OSL-3.0","PHP-3.0","PHP-3.01","PostgreSQL","Python-2.0","QPL-1.0","RPL-1.1","RPL-1.5","RPSL-1.0","RSCPL","SimPL-2.0","SISSL","Sleepycat","SPL-1.0","UCL-1.0","Unicode-DFS-2016","Unlicense","UPL-1.0","VSL-1.0","W3C","Watcom-1.0","wxWindows","Xnet","Zlib","ZPL-2.0","ZPL-2.1"],
"deprecated": ["AGPL-1.0","AGPL-3.0","BSD-2-Clause-FreeBSD","BSD-2-Clause-NetBSD","bzip2-1.0.5","eCos-2.0","GFDL-1.1","GFDL-1.2","GFDL-1.3","GPL-1.0","GPL-1.0+","GPL-2.0","GPL-2.0+","GPL-2.0-with-autoconf-exception","GPL-2.0-with-bison-exception","GPL-2.0-with-classpath-exception","GPL-2.0-with-font-exception","GPL-2.0-with-GCC-exception","GPL-3.0","GPL-3.0+","GPL-3.0-with-autoconf-exception","GPL-3.0-with-GCC-exception","LGPL-2.0","LGPL-2.0+","LGPL-2.1","LGPL-2.1+","LGPL-3.0","LGPL-3.0+","Nunit","StandardML-NJ","wxWindows"]
}
File renamed without changes.
File renamed without changes.

0 comments on commit 99aa988

Please sign in to comment.