diff --git a/src/cmds/generate.ts b/src/cmds/generate.ts index 8b54a54..d893d6c 100644 --- a/src/cmds/generate.ts +++ b/src/cmds/generate.ts @@ -41,7 +41,7 @@ export const builder = { choices: [OutputFormat.HTML, OutputFormat.PDF], }, view: { - // TODO: add also dependency based view when ready + choices: [SupportedViews.ORG_LICENSES, SupportedViews.PROJECT_DEPENDENCIES], default: SupportedViews.ORG_LICENSES, desc: 'How should the data be represented. Defaults to a license based view.', diff --git a/src/cmds/json.ts b/src/cmds/json.ts index 53b5de5..cb01982 100644 --- a/src/cmds/json.ts +++ b/src/cmds/json.ts @@ -12,13 +12,7 @@ export const builder = { orgPublicId: { required: true, default: undefined, - }, - view: { - // TODO: add also dependency based view when ready - default: SupportedViews.ORG_LICENSES, - desc: - 'How should the data be represented. Defaults to a license based view.', - }, + } }; export const aliases = ['j']; diff --git a/src/lib/generate-org-license-report.ts b/src/lib/generate-org-license-report.ts index 0bfe51a..3ccaa61 100644 --- a/src/lib/generate-org-license-report.ts +++ b/src/lib/generate-org-license-report.ts @@ -5,7 +5,7 @@ export * from './license-text'; export * from './get-api-token'; import { getLicenseDataForOrg, getDependenciesDataForOrg } from './api/org'; import { fetchSpdxLicenseTextAndUrl, fetchNonSpdxLicenseTextAndUrl } from './license-text'; -import { LicenseReportDataEntry, EnrichedDependency, Dependency } from './types'; +import { LicenseReportDataEntry, EnrichedDependency, Dependency, DependencyData } from './types'; const debug = debugLib('snyk-licenses:generateOrgLicensesReport'); @@ -29,7 +29,11 @@ export async function generateLicenseData( debug(`✅ Got dependencies API data for Org:${orgPublicId}`); const licenseReportData: LicenseReportData = {}; const dependenciesData = _.groupBy(dependenciesDataRaw.results, 'id'); - // TODO: what if 0? + + if (!licenseData.total) { + debug(`ℹ️ Detected 0 licenses`); + return licenseReportData; + } debug(`⏳ Processing ${licenseData.total} licenses`); const dependenciesAll = []; @@ -69,9 +73,9 @@ function enrichDependencies( dependencies: Dependency[], dependenciesData, ): EnrichedDependency[] { - const enrichDependencies = []; + const enrichDependencies: EnrichedDependency [] = []; for (const dependency of dependencies) { - const dep = dependenciesData[dependency.id]; + const dep: DependencyData[] = dependenciesData[dependency.id]; if (dep && dep[0]) { enrichDependencies.push({ ...dependency, diff --git a/src/lib/generate-report/index.ts b/src/lib/generate-report/index.ts index 7f7f6af..4bf1b29 100644 --- a/src/lib/generate-report/index.ts +++ b/src/lib/generate-report/index.ts @@ -2,19 +2,18 @@ import * as Handlebars from 'handlebars'; import * as path from 'path'; import * as fs from 'fs'; import * as debugLib from 'debug'; +import * as _ from 'lodash'; import { LicenseReportData } from '../generate-org-license-report'; import { OrgData } from '../get-org-data'; -import { generateReportName } from '../generate-report-name'; -import { SupportedViews } from '../types'; +import { SupportedViews, LicenseReportDataEntry } from '../types'; const debug = debugLib('snyk-licenses:generateHtmlReport'); const DEFAULT_TEMPLATE = './templates/licenses-view.hbs'; const transformDataFunc = { [SupportedViews.ORG_LICENSES]: transformDataForLicenseView, - // TODO: support later - // [SupportedViews.PROJECT_DEPENDENCIES]: transformDataForDependencyView, + [SupportedViews.PROJECT_DEPENDENCIES]: transformDataForDependencyView, }; export async function generateHtmlReport( @@ -50,18 +49,62 @@ function transformDataForLicenseView( return { licenses: data, orgPublicId, orgData }; } -// TODO: support later -// function transformDataForDependencyView(data: LicenseReportData) { -// return data; -// } +interface ProjectsReportData { + [projectId: string]: { + projectName: string; + projectIndex: number; + licenses: { + [licenseId: string]: LicenseReportDataEntry; + }; + }; +} + +function transformDataForDependencyView( + orgPublicId: string, + data: LicenseReportData, + orgData: OrgData, +): { + projects: ProjectsReportData; + orgPublicId: string; + orgData: OrgData; + totalProjects: number; +} { + const projectData: ProjectsReportData = {}; + let totalProjects = 0; + + for (const licenseId of Object.keys(data)) { + const licenseData = data[licenseId]; + + for (const project of licenseData.projects) { + if (!projectData[project.id]) { + totalProjects += 1; + projectData[project.id] = { + projectIndex: totalProjects, + projectName: project.name, + licenses: { + [licenseId]: licenseData, + }, + }; + } else { + projectData[project.id].licenses[licenseId] = licenseData; + } + } + } + + return { + projects: projectData, + orgPublicId, + orgData, + totalProjects, + }; +} function selectTemplate(view: SupportedViews, templateOverride?): string { switch (view) { case SupportedViews.ORG_LICENSES: return templateOverride || DEFAULT_TEMPLATE; - // TODO: support later - // case SupportedViews.PROJECT_DEPENDENCIES: - // return templateOverride || '../templates/project-dependencies-view.hbs'; + case SupportedViews.PROJECT_DEPENDENCIES: + return templateOverride || './templates/project-dependencies-view.hbs'; default: return DEFAULT_TEMPLATE; } diff --git a/src/lib/generate-report/templates/licenses-view.hbs b/src/lib/generate-report/templates/licenses-view.hbs index 7fb7f3f..6f578e3 100644 --- a/src/lib/generate-report/templates/licenses-view.hbs +++ b/src/lib/generate-report/templates/licenses-view.hbs @@ -143,7 +143,7 @@

Snyk Licenses Attribution Report

-

Org: {{orgData.name}}

+

Organization: {{orgData.name}}

{{#each licenses}}

diff --git a/src/lib/generate-report/templates/project-dependencies-view.hbs b/src/lib/generate-report/templates/project-dependencies-view.hbs index e69de29..af5cf17 100644 --- a/src/lib/generate-report/templates/project-dependencies-view.hbs +++ b/src/lib/generate-report/templates/project-dependencies-view.hbs @@ -0,0 +1,212 @@ + + + + + + + + + Snyk Licenses Report + + + + + + + {{!-- {{> header }} --}} + + +
+

Snyk Licenses Attribution Report

+

Organization: {{orgData.name}}

+ {{#each projects}} +
+

+ Project ({{projectIndex}}/{{../totalProjects}}): {{projectName}} +

+ {{#each licenses}} +
+

+ {{id}} +

+ Severity: {{severity}} + {{#if instructions}} + Legal Instructions: {{instructions}} + {{/if}} +
+ +
+
+
+
+
+ Dependencies +
+
+ Copyrights +
+
+ +
+ {{#each dependencies}} +
+
+ {{name}}@{{version}} +
+
+ {{#each copyright}} +
+ {{this}} +
+ {{/each}} +
+
+ + {{/each}} +
+
+
+
+
+ License Text +
+
{{{licenseText}}}
+
+
+

+ {{/each}} + + {{/each}} +
+ + + diff --git a/src/lib/types.ts b/src/lib/types.ts index 9b1b218..0a3a1c7 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -4,11 +4,10 @@ export interface Dependency { version: string; packageManager: string; } -//TODO: finish the type -export interface EnrichedDependency extends Dependency {} + +export type EnrichedDependency = Dependency & DependencyData; export interface LicenseReportDataEntry { - // TODO: what if it is a dual license? /** * The text of the license in HTML format */ @@ -133,6 +132,5 @@ export interface DependencyData { export const enum SupportedViews { ORG_LICENSES = 'org-licenses', - // TODO: support later - // PROJECT_DEPENDENCIES = 'project-dependencies', + PROJECT_DEPENDENCIES = 'project-dependencies', } diff --git a/test/lib/__snapshots__/generate-html-report.test.ts.snap b/test/lib/__snapshots__/generate-html-report.test.ts.snap index e3d2e06..e0f0cd8 100644 --- a/test/lib/__snapshots__/generate-html-report.test.ts.snap +++ b/test/lib/__snapshots__/generate-html-report.test.ts.snap @@ -145,7 +145,7 @@ exports[`Generate HTML report License HTML Report is generated as expected 1`] =

Snyk Licenses Attribution Report

-

Org: org

+

Organization: org

BSD-2-Clause @@ -716,7 +716,7 @@ exports[`Generate HTML report License HTML Report is generated as expected with

Custom Template Licenses Attribution Report

-

Org: org

+

Organization: org

BSD-2-Clause @@ -741,3 +741,564 @@ exports[`Generate HTML report License HTML Report is generated as expected with " `; + +exports[`Generate HTML report License HTML Report is generated as expected with project based view 1`] = ` +" + + + + + + + + Snyk Licenses Report + + + + + + +
+ +
+ +
+

Snyk Licenses Attribution Report

+

Organization: org

+
+

+ Project (1/2): snyk-fixtures/npm-with-single-dep:package.json +

+
+

+ BSD-2-Clause +

+ Severity: medium + Legal Instructions: Do not use any package with this license without speaking to anna@legal.com +
+ +
+
+
+
+
+ Dependencies +
+
+ Copyrights +
+
+ +
+
+
+ configstore@5.0.1 +
+
+
+ Copyright (c) Google +
+
+
+ +
+
+
+
+
+ License Text +
+
+ + + + + Linux Foundation Collaborative Projects + + + + + + + + + + + + + Software Package Data Exchange (SPDX) + + + + + + + + + + + + + + Home » Licenses + + BSD 2-Clause \\"Simplified\\" License + false + Full name + BSD 2-Clause \\"Simplified\\" License + + Short identifier + BSD-2-Clause + + Other web pages for this license + + + https://opensource.org/licenses/BSD-2-Clause + + + + true + + Notes + None + + Text + + + + + + Copyright (c) . All rights reserved. + + + + + Redistribution and use in source and binary forms, with or without modification, are permitted provided + that the following conditions are met: + + + + + +1. + Redistributions of source code must retain the above copyright notice, this list of conditions + and the following disclaimer. + + + +2. + Redistributions in binary form must reproduce the above copyright notice, this list of conditions + and the following disclaimer in the documentation and/or other materials provided with the + distribution. + + + THIS SOFTWARE IS PROVIDED BY + +THE COPYRIGHT HOLDERS AND CONTRIBUTORS \\"AS IS\\" AND ANY + +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + +THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR + ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + + + + Standard License Header + + There is no standard license header for the license + + + + < . All rights reserved.\\";match=\\".{0,1000}\\">> + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + <> Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + + <> Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY <> \\"AS IS\\" AND ANY <> OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL <> BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + + + + + + + © 2018 SPDX Workgroup a Linux Foundation Project. All Rights Reserved. + Linux Foundation is a registered trademark of The Linux Foundation. Linux is a registered trademark of Linus Torvalds. + Please see our privacy policy and terms of use. + + + + + + + top of page + + + + +
+
+
+

+ +
+

+ Project (2/2): snyk-fixtures/tiny-monorepo:gradle/build.gradle +

+
+

+ Unknown +

+ Severity: high + Legal Instructions: Any package with this license is not to be used. +
+ +
+
+
+
+
+ Dependencies +
+
+ Copyrights +
+
+ +
+
+
+ axis:axis-jaxrpc@1.4 +
+
+
+
+ +
+
+ axis:axis-saaj@1.4 +
+
+
+
+ +
+
+ axis:axis-wsdl4j@1.5.1 +
+
+
+
+ +
+
+
+
+
+ License Text +
+
+
+
+

+
+

+ Unlicense +

+ Severity: none +
+ +
+
+
+
+
+ Dependencies +
+
+ Copyrights +
+
+ +
+
+
+ tweetnacl@0.14.5 +
+
+
+
+ +
+
+
+
+
+ License Text +
+
+ + + + + Linux Foundation Collaborative Projects + + + + + + + + + + + + + Software Package Data Exchange (SPDX) + + + + + + + + + + + + + + Home » Licenses + + The Unlicense + false + Full name + The Unlicense + + Short identifier + Unlicense + + Other web pages for this license + + + https://unlicense.org/ + + + + true + + Notes + This is a public domain dedication + + Text + + + + This is free and unencumbered software released into the public domain. + + Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in + source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any + means. + + In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and + all copyright interest in the software to the public domain. We make this dedication for the benefit + of the public at large and to the detriment of our heirs and + successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all + present and future rights to this software under copyright law. + + + THE SOFTWARE IS PROVIDED \\"AS IS\\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT + LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN + NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. + + For more information, please refer to + + + + + Standard License Header + + There is no standard license header for the license + + + + This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means. + +In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law. + +THE SOFTWARE IS PROVIDED \\"AS IS\\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.<> For more information, please refer to <> + + + + + + + + + © 2018 SPDX Workgroup a Linux Foundation Project. All Rights Reserved. + Linux Foundation is a registered trademark of The Linux Foundation. Linux is a registered trademark of Linus Torvalds. + Please see our privacy policy and terms of use. + + + + + + + top of page + + + + +
+
+
+ + +
+ + + +" +`; diff --git a/test/lib/fixtures/custom-view.hbs b/test/lib/fixtures/custom-view.hbs index 4533b4c..f1e0358 100644 --- a/test/lib/fixtures/custom-view.hbs +++ b/test/lib/fixtures/custom-view.hbs @@ -143,7 +143,7 @@

Custom Template Licenses Attribution Report

-

Org: {{orgData.name}}

+

Organization: {{orgData.name}}

{{#each licenses}}

diff --git a/test/lib/generate-html-report.test.ts b/test/lib/generate-html-report.test.ts index e8a3de8..a702f76 100644 --- a/test/lib/generate-html-report.test.ts +++ b/test/lib/generate-html-report.test.ts @@ -2,6 +2,7 @@ import * as path from 'path'; import { generateHtmlReport } from '../../src/lib/generate-report'; import { loadJson } from '../load-json'; import { LicenseReportData } from '../../src/lib/generate-org-license-report'; +import { SupportedViews } from '../../src/lib/types'; describe('Generate HTML report', () => { const OLD_ENV = process.env; @@ -20,7 +21,7 @@ describe('Generate HTML report', () => { __dirname + '/fixtures/example-license-data.json', ) as unknown) as LicenseReportData; const orgData = { - name: "org", + name: 'org', id: 'avd-scv', slug: 'org', url: 'https://snyk.io/org/org', @@ -28,17 +29,41 @@ describe('Generate HTML report', () => { name: 'group', id: 'group-1', }, - } + }; const htmlData = await generateHtmlReport(ORG_ID, licenseRes, orgData); expect(htmlData).toMatchSnapshot(); }, 50000); + test('License HTML Report is generated as expected with project based view', async () => { + const licenseRes = (loadJson( + __dirname + '/fixtures/example-license-data.json', + ) as unknown) as LicenseReportData; + const orgData = { + name: 'org', + id: 'avd-scv', + slug: 'org', + url: 'https://snyk.io/org/org', + group: { + name: 'group', + id: 'group-1', + }, + }; + const htmlData = await generateHtmlReport( + ORG_ID, + licenseRes, + orgData, + undefined, + SupportedViews.PROJECT_DEPENDENCIES, + ); + expect(htmlData).toMatchSnapshot(); + }, 50000); + test('License HTML Report is generated as expected with a custom hbs template', async () => { const licenseRes = (loadJson( __dirname + '/fixtures/example-license-data.json', ) as unknown) as LicenseReportData; const orgData = { - name: "org", + name: 'org', id: 'avd-scv', slug: 'org', url: 'https://snyk.io/org/org', @@ -46,8 +71,13 @@ describe('Generate HTML report', () => { name: 'group', id: 'group-1', }, - } - const htmlData = await generateHtmlReport(ORG_ID, licenseRes, orgData, path.resolve(__dirname + '/fixtures/custom-view.hbs')); + }; + const htmlData = await generateHtmlReport( + ORG_ID, + licenseRes, + orgData, + path.resolve(__dirname + '/fixtures/custom-view.hbs'), + ); expect(htmlData).toMatchSnapshot(); }, 50000); test.todo('Test for when API fails aka bad org id provided'); diff --git a/test/system/__snapshots__/generate.test.ts.snap b/test/system/__snapshots__/generate.test.ts.snap index 8359be0..71f9dc7 100644 --- a/test/system/__snapshots__/generate.test.ts.snap +++ b/test/system/__snapshots__/generate.test.ts.snap @@ -14,7 +14,8 @@ Options: --template Path to custom Handelbars.js template file (*.hbs) --outputFormat Report format [choices: \\"html\\", \\"pdf\\"] [default: \\"html\\"] --view How should the data be represented. Defaults to a license - based view. [default: \\"org-licenses\\"] + based view. + [choices: \\"org-licenses\\", \\"project-dependencies\\"] [default: \\"org-licenses\\"] Missing required argument: orgPublicId" `; diff --git a/test/system/__snapshots__/json.test.ts.snap b/test/system/__snapshots__/json.test.ts.snap index 8ea5003..6270fd1 100644 --- a/test/system/__snapshots__/json.test.ts.snap +++ b/test/system/__snapshots__/json.test.ts.snap @@ -10,8 +10,6 @@ Options: --version Show version number [boolean] --help Show help [boolean] --orgPublicId [required] - --view How should the data be represented. Defaults to a license based - view. [default: \\"org-licenses\\"] Missing required argument: orgPublicId" `; diff --git a/test/system/generate.test.ts b/test/system/generate.test.ts index 365f239..c0f55b5 100644 --- a/test/system/generate.test.ts +++ b/test/system/generate.test.ts @@ -22,7 +22,6 @@ describe('`snyk-licenses-report generate <...>`', () => { `node ${main} generate --orgPublicId=${ORG_ID}`, { env: { SNYK_TOKEN: process.env.SNYK_TEST_TOKEN } }, (err, stdout) => { - console.log({err, stdout}) expect(err).toBeNull(); expect(stdout).toMatch('HTML license report saved at'); done(); @@ -35,7 +34,6 @@ describe('`snyk-licenses-report generate <...>`', () => { `node ${main} generate --orgPublicId=${ORG_ID} --outputFormat=pdf`, { env: { SNYK_TOKEN: process.env.SNYK_TEST_TOKEN } }, (err, stdout) => { - console.log({err, stdout}) expect(err).toBeNull(); expect(stdout).toMatch('PDF license report saved at'); done(); @@ -47,7 +45,6 @@ describe('`snyk-licenses-report generate <...>`', () => { `node ${main} generate --orgPublicId=${ORG_ID} --template=${__dirname + '/fixtures/custom-view.hbs'}`, { env: { SNYK_TOKEN: process.env.SNYK_TEST_TOKEN } }, (err, stdout) => { - console.log({err, stdout}) expect(err).toBeNull(); expect(stdout).toMatch('HTML license report saved at'); done();