Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/lib/plugins/get-multi-plugin-result.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export interface ScannedProjectCustom
callGraph?: CallGraph;
}

interface FailedProjectScanError {
export interface FailedProjectScanError {
targetFile?: string;
error?: Error;
errMessage: string;
Expand Down
79 changes: 78 additions & 1 deletion src/lib/snyk-test/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,13 @@ import { DepGraphData } from '@snyk/dep-graph';

import config from '../config';
import { color } from '../theme';
import * as path from 'path';
import { Options } from '../types';
import { ConcatStream } from '../stream';
import { ContainerTarget, GitTarget } from '../project-metadata/types';
import { CLI, ProblemError } from '@snyk/error-catalog-nodejs-public';
import { CustomError } from '../errors';
import { FailedProjectScanError } from '../plugins/get-multi-plugin-result';

export function assembleQueryString(options) {
const org = options.org || config.org || null;
Expand Down Expand Up @@ -131,6 +135,79 @@ export async function printEffectiveDepGraph(
});
}

/**
* printEffectiveDepGraphError writes an error output for failed dependency graph resolution
* to the destination stream in a format consistent with printEffectiveDepGraph.
* This is used when --print-effective-graph-with-errors is set but dependency resolution failed.
*/
export async function printEffectiveDepGraphError(
root: string,
failedProjectScanError: FailedProjectScanError,
destination: Writable,
): Promise<void> {
return new Promise((res, rej) => {
// Normalize the target file path to be relative to root, consistent with printEffectiveDepGraph
const normalisedTargetFile = failedProjectScanError.targetFile
? path.relative(root, failedProjectScanError.targetFile)
: failedProjectScanError.targetFile;

const problemError = getOrCreateErrorCatalogError(failedProjectScanError);
const serializedError = problemError.toJsonApi().body();

const effectiveGraphErrorOutput = {
error: serializedError,
normalisedTargetFile,
};

new ConcatStream(
new JsonStreamStringify(effectiveGraphErrorOutput),
Readable.from('\n'),
)
.on('end', res)
.on('error', rej)
.pipe(destination);
});
}

/**
* Checks if either --print-effective-graph or --print-effective-graph-with-errors is set.
*/
export function shouldPrintEffectiveDepGraph(opts: Options): boolean {
return !!opts['print-effective-graph'];
return (
!!opts['print-effective-graph'] ||
shouldPrintEffectiveDepGraphWithErrors(opts)
Comment on lines +177 to +178
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The shouldPrintEffectiveDepGraph() check is used in multiple places and should remain true for both --print-effective-graph and --print-effective-graph-with-errors

);
}

/**
* shouldPrintEffectiveDepGraphWithErrors checks if the --print-effective-graph-with-errors flag is set.
* This is used to determine if the effective dep-graph with errors should be printed.
*/
export function shouldPrintEffectiveDepGraphWithErrors(opts: Options): boolean {
return !!opts['print-effective-graph-with-errors'];
}

/**
* getOrCreateErrorCatalogError returns a ProblemError instance for consistent error catalog usage.
* This helper is used to ensure errors are wrapped in a ProblemError so they can be reported in a standardized way,
* especially when converting thrown errors from plugins and flows to error catalog format.
*/
export function getOrCreateErrorCatalogError(
failedProjectScanError: FailedProjectScanError,
): ProblemError {
const { error, errMessage } = failedProjectScanError;
if (error instanceof ProblemError) {
return error;
}

if (error instanceof CustomError) {
if (error.errorCatalog) {
return error.errorCatalog;
}
return new CLI.GeneralCLIFailureError(
error.userMessage ?? error.message ?? errMessage,
);
}

return new CLI.GeneralCLIFailureError(errMessage);
}
9 changes: 9 additions & 0 deletions src/lib/snyk-test/run-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,11 @@ import {
RETRY_DELAY,
printDepGraph,
printEffectiveDepGraph,
printEffectiveDepGraphError,
assembleQueryString,
shouldPrintDepGraph,
shouldPrintEffectiveDepGraph,
shouldPrintEffectiveDepGraphWithErrors,
} from './common';
import config from '../config';
import * as analytics from '../analytics';
Expand Down Expand Up @@ -667,6 +669,13 @@ async function assembleLocalPayloads(
'getDepsFromPlugin returned failed results, cannot run test/monitor',
failedResults,
);

if (shouldPrintEffectiveDepGraphWithErrors(options)) {
for (const failed of failedResults) {
await printEffectiveDepGraphError(root, failed, process.stdout);
}
}

if (options['fail-fast']) {
// should include failure message if applicable
const message = errorMessages.length
Expand Down
1 change: 1 addition & 0 deletions src/lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ export interface Options {
'print-tree'?: boolean;
'print-dep-paths'?: boolean;
'print-effective-graph'?: boolean;
'print-effective-graph-with-errors'?: boolean;
'remote-repo-url'?: string;
criticality?: string;
scanAllUnmanaged?: boolean;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
invalid-json-syntax
"name": "invalid-project",
"version": "1.0.0"
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"name": "valid-project",
"version": "1.0.0",
"description": "A valid npm project for testing",
"main": "index.js"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"name": "with-vulnerable-lodash-dep",
"version": "1.2.3",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"license": "ISC",
"dependencies": {
"lodash": "4.17.15"
}
}
Loading