snyk-mvn-plugin
Version:
Snyk CLI Maven plugin
143 lines • 5.97 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.buildDepGraph = buildDepGraph;
exports.buildWithoutVerbose = buildWithoutVerbose;
exports.buildWithVerbose = buildWithVerbose;
const yocto_queue_1 = require("@common.js/yocto-queue");
const dep_graph_1 = require("@snyk/dep-graph");
const dependency_1 = require("./dependency");
const fingerprint_1 = require("../fingerprint");
const MAVEN_BUILD_SCOPE_UNKNOWN = 'unknown';
function buildDepGraph(mavenGraph, context) {
const { rootId, nodes } = mavenGraph;
return context.verboseEnabled
? buildWithVerbose(rootId, nodes, context)
: buildWithoutVerbose(rootId, nodes, context);
}
function buildWithoutVerbose(rootId, nodes, context) {
const { fingerprintMap, includePurl, includeTestScope } = context;
const parsedRoot = parseId(rootId, true, includePurl, fingerprintMap.get(rootId));
const builder = new dep_graph_1.DepGraphBuilder({ name: 'maven' }, parsedRoot.pkgInfo, createNodeInfo(parsedRoot, context, MAVEN_BUILD_SCOPE_UNKNOWN));
const visitedMap = {};
const queue = new yocto_queue_1.default();
getItems(rootId, nodes[rootId]).forEach((item) => queue.enqueue(item));
// breadth first search
while (queue.size > 0) {
const item = queue.dequeue();
if (!item)
continue;
const { id, parentId } = item;
const parsed = parseId(id, false, includePurl, fingerprintMap.get(id));
const node = nodes[id];
if (!includeTestScope && parsed.scope === 'test' && !node.reachesProdDep) {
continue;
}
const visited = visitedMap[parsed.key];
if (visited) {
const prunedId = visited.id + ':pruned';
builder.addPkgNode(visited.pkgInfo, prunedId, {
labels: { pruned: 'true' },
});
builder.connectDep(parentId, prunedId);
continue; // don't queue any more children
}
const parentNodeId = parentId === rootId ? builder.rootNodeId : parentId;
builder.addPkgNode(parsed.pkgInfo, id, createNodeInfo(parsed, context));
builder.connectDep(parentNodeId, id);
visitedMap[parsed.key] = parsed;
getItems(id, node).forEach((item) => queue.enqueue(item));
}
return builder.build();
}
function buildWithVerbose(rootId, nodes, context) {
const { fingerprintMap, includePurl, includeTestScope } = context;
const parsedRoot = parseId(rootId, true, includePurl, fingerprintMap.get(rootId));
const builder = new dep_graph_1.DepGraphBuilder({ name: 'maven' }, parsedRoot.pkgInfo, createNodeInfo(parsedRoot, context, MAVEN_BUILD_SCOPE_UNKNOWN));
const visitedMap = {};
const stack = [];
stack.push(...getVerboseItems(rootId, [], nodes[rootId]));
// depth first search
while (stack.length > 0) {
const item = stack.pop();
if (!item)
continue;
const { id, ancestry, parentId } = item;
const parsed = parseId(id, true, includePurl, fingerprintMap.get(id));
const node = nodes[id];
if (!includeTestScope && parsed.scope === 'test' && !node.reachesProdDep) {
continue;
}
const visited = visitedMap[parsed.key];
// If verbose is enabled and our ancestry includes ourselves
// we are cyclic and should be pruned :)
if (ancestry.includes(parsed.key)) {
const prunedId = visited.id + ':pruned-cycle';
builder.addPkgNode(visited.pkgInfo, prunedId, {
labels: { pruned: 'cyclic' },
});
builder.connectDep(parentId, prunedId);
continue; // don't queue any more children
}
const parentNodeId = parentId === rootId ? builder.rootNodeId : parentId;
if (visited) {
builder.connectDep(parentNodeId, visited.id);
// use visited node when omited dependencies found (verbose)
stack.push(...getVerboseItems(visited.id, [...ancestry, parsed.key], node));
}
else {
builder.addPkgNode(parsed.pkgInfo, id, createNodeInfo(parsed, context));
builder.connectDep(parentNodeId, id);
visitedMap[parsed.key] = parsed;
// Remember to push updated ancestry here
stack.push(...getVerboseItems(id, [...ancestry, parsed.key], node));
}
}
return builder.build();
}
function createNodeInfo(depInfo, context, defaultScope = 'compile') {
if (context.showMavenBuildScope) {
return {
labels: {
'maven:build_scope': depInfo.scope ? depInfo.scope : defaultScope,
},
};
}
return;
}
function getItems(parentId, node) {
const items = [];
for (const id of node?.dependsOn || []) {
items.push({ id, parentId });
}
return items;
}
function getVerboseItems(parentId, ancestry, node) {
const items = [];
for (const id of node?.dependsOn || []) {
items.push({ id, ancestry, parentId });
}
return items;
}
function parseId(id, verboseEnabled = false, includePurl = false, fingerprintData) {
const dep = (0, dependency_1.parseDependency)(id);
const maybeClassifier = dep.classifier ? `:${dep.classifier}` : '';
const name = `${dep.groupId}:${dep.artifactId}`;
// Only do expensive operations if PURL is needed
let purl;
if (includePurl) {
purl = (0, fingerprint_1.createMavenPurlWithChecksum)(dep.groupId, dep.artifactId, dep.version, fingerprintData, dep.classifier, dep.type);
}
return {
id,
key: verboseEnabled
? `${name}:${dep.type}${maybeClassifier}:${dep.version}:${dep.scope}`
: `${name}:${dep.type}${maybeClassifier}`,
pkgInfo: {
name,
version: dep.version,
...(includePurl ? { purl } : {}),
},
scope: dep.scope,
};
}
//# sourceMappingURL=dep-graph.js.map