diff --git a/src/lib/print-deps.ts b/src/lib/print-deps.ts index 3e5a8a1382..1b6eb9a651 100644 --- a/src/lib/print-deps.ts +++ b/src/lib/print-deps.ts @@ -3,7 +3,6 @@ import * as depGraphLib from '@snyk/dep-graph'; import { DepDict, Options, MonitorOptions } from './types'; import { legacyCommon as legacyApi } from '@snyk/cli-interface'; import { countPathsToGraphRoot } from './utils'; -import { jsonStringifyLargeObject } from './json'; export async function maybePrintDepGraph( options: Options | MonitorOptions, @@ -20,18 +19,11 @@ export async function maybePrintDepGraph( )) as legacyApi.DepTree; maybePrintDepTree(options, depTree); } else { - if (options['print-deps']) { - if (options.json) { - console.warn( - '--print-deps --json option not yet supported for large projects. Displaying graph json output instead', - ); - // TODO @boost: add as output graphviz 'dot' file to visualize? - console.log(jsonStringifyLargeObject(depGraph.toJSON())); - } else { - console.warn( - '--print-deps option not yet supported for large projects. Try with --json.', - ); - } + if (options['print-deps'] && !options.json) { + // don't print a warning when --json is being used, it can invalidate the JSON output + console.warn( + '--print-deps option not yet supported for large projects. Try with --json.', + ); } } } @@ -42,13 +34,10 @@ export function maybePrintDepTree( options: Options | MonitorOptions, rootPackage: legacyApi.DepTree, ) { - if (options['print-deps']) { - if (options.json) { - // Will produce 2 JSON outputs, one for the deps, one for the vuln scan. - console.log(jsonStringifyLargeObject(rootPackage)); - } else { - printDepsForTree({ [rootPackage.name!]: rootPackage }); - } + if (options['print-deps'] && !options.json) { + // only print human readable output tree if NOT using --json + // to ensure this output does not invalidate JSON output + printDepsForTree({ [rootPackage.name!]: rootPackage }); } } diff --git a/src/lib/snyk-test/legacy.ts b/src/lib/snyk-test/legacy.ts index e82b587f44..87f8a4a487 100644 --- a/src/lib/snyk-test/legacy.ts +++ b/src/lib/snyk-test/legacy.ts @@ -165,6 +165,7 @@ export interface LegacyVulnApiResult extends BasicResultData { filesystemPolicy?: boolean; uniqueCount?: any; remediation?: RemediationChanges; + depGraph?: depGraphLib.DepGraphData; } export interface BaseImageRemediation { @@ -451,6 +452,9 @@ function convertTestDepGraphResultToLegacy( severityThreshold, remediation: result.remediation, }; + if (options['print-deps']) { + legacyRes.depGraph = depGraph.toJSON(); + } return legacyRes; } diff --git a/test/jest/acceptance/cli-json-file-output.spec.ts b/test/jest/acceptance/cli-json-file-output.spec.ts index ec01fa175b..6d7b7a503a 100644 --- a/test/jest/acceptance/cli-json-file-output.spec.ts +++ b/test/jest/acceptance/cli-json-file-output.spec.ts @@ -4,6 +4,7 @@ import { createProjectFromWorkspace } from '../util/createProject'; import { runSnykCLI } from '../util/runSnykCLI'; import { humanFileSize } from '../../utils'; import { getServerPort } from '../util/getServerPort'; +import * as depGraphLib from '@snyk/dep-graph'; jest.setTimeout(1000 * 60); @@ -112,4 +113,45 @@ describe('test --json-file-output', () => { expect(fileExists).toBeFalsy(); expect(code).toEqual(0); }); + + describe('print-deps', () => { + it('saves JSON output to file with depGraph when --print-deps is used', async () => { + const project = await createProjectFromWorkspace('maven-app'); + const outputPath = 'json-file-output.json'; + + const { code } = await runSnykCLI( + `test --print-deps --json-file-output=${outputPath}`, + { + cwd: project.path(), + env, + }, + ); + + expect(code).toEqual(0); + const json = await project.readJSON(outputPath); + expect(json.depGraph).toBeTruthy(); + const depGraph = depGraphLib.createFromJSON(json.depGraph); + expect(depGraph.getPkgs()).toContainEqual({ + name: 'axis:axis', + version: '1.4', + }); + }); + + it('saves JSON output to file without a depGraph when --print-deps is not used', async () => { + const project = await createProjectFromWorkspace('maven-app'); + const outputPath = 'json-file-output.json'; + + const { code } = await runSnykCLI( + `test --json-file-output=${outputPath}`, + { + cwd: project.path(), + env, + }, + ); + + expect(code).toEqual(0); + const json = await project.readJSON(outputPath); + expect(json.depGraph).toBeUndefined(); + }); + }); }); diff --git a/test/jest/acceptance/cli-json-output.spec.ts b/test/jest/acceptance/cli-json-output.spec.ts index ace22fd15c..71a445c0db 100644 --- a/test/jest/acceptance/cli-json-output.spec.ts +++ b/test/jest/acceptance/cli-json-output.spec.ts @@ -4,6 +4,7 @@ import { getServerPort } from '../util/getServerPort'; import { runSnykCLI } from '../util/runSnykCLI'; import { AppliedPolicyRules } from '../../../src/lib/formatters/types'; import * as Parser from 'jsonparse'; +import * as depGraphLib from '@snyk/dep-graph'; jest.setTimeout(1000 * 60); @@ -193,4 +194,38 @@ describe('test --json', () => { } }); }); + + describe('print-deps', () => { + it('JSON output contains depGraph when --print-deps is used', async () => { + const project = await createProjectFromWorkspace('maven-app'); + + const { code, stdout } = await runSnykCLI(`test --print-deps --json`, { + cwd: project.path(), + env, + }); + + expect(code).toEqual(0); + console.log(stdout); + + const json = JSON.parse(stdout); + const depGraph = depGraphLib.createFromJSON(json.depGraph); + expect(depGraph.getPkgs()).toContainEqual({ + name: 'axis:axis', + version: '1.4', + }); + }); + + it('JSON output has no depGraph when --print-deps is not used', async () => { + const project = await createProjectFromWorkspace('maven-app'); + + const { code, stdout } = await runSnykCLI(`test --json`, { + cwd: project.path(), + env, + }); + + expect(code).toEqual(0); + const json = JSON.parse(stdout); + expect(json.depGraph).toBeUndefined(); + }); + }); });