@snyk/dep-graph
Version:
Snyk dependency graph library
114 lines • 5.41 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.validatePackageURL = exports.validateGraph = void 0;
const graphlib = require("../graphlib");
const packageurl_js_1 = require("packageurl-js");
const errors_1 = require("./errors");
const reGolangPseudoVersion = /(v\d+\.\d+\.\d+)-(.*?)(\d{14})-([0-9a-f]{12})/;
const reGolangExactVersion = /^(.*?)(\+incompatible)?$/;
function assert(condition, msg) {
if (!condition) {
throw new errors_1.ValidationError(msg);
}
}
function validateGraph(graph, rootNodeId, pkgs, pkgNodes) {
assert((graph.predecessors(rootNodeId) || []).length === 0, `"${rootNodeId}" is not really the root`);
const reachableFromRoot = graphlib.alg.postorder(graph, [rootNodeId]);
const nodeIds = graph.nodes();
assert(JSON.stringify(nodeIds.sort()) === JSON.stringify(reachableFromRoot.sort()), 'not all graph nodes are reachable from root');
const pkgIds = Object.keys(pkgs);
const pkgsWithoutInstances = pkgIds.filter((pkgId) => !pkgNodes[pkgId] || pkgNodes[pkgId].size === 0);
assert(pkgsWithoutInstances.length === 0, 'not all pkgs have instance nodes');
for (const pkgId in pkgs) {
try {
validatePackageURL(pkgs[pkgId]);
}
catch (e) {
throw new errors_1.ValidationError(`invalid pkg ${pkgId}: ${e}`);
}
}
}
exports.validateGraph = validateGraph;
function validatePackageURL(pkg) {
var _a;
if (!pkg.purl) {
return;
}
try {
const purl = packageurl_js_1.PackageURL.fromString(pkg.purl);
// validate package name
switch (purl.type) {
// Within Snyk, maven packages use <namespace>:<name> as their *name*, but
// we expect those to be separated correctly in the PackageURL.
case 'maven':
assert(pkg.name === purl.namespace + ':' + purl.name, `name and packageURL name do not match`);
break;
// CocoaPods have an optional subspec encoded in the subpath
// component of the purl, which – if present – should
// be appended to the spec.
case 'cocoapods':
assert(pkg.name ===
(purl.subpath ? `${purl.name}/${purl.subpath}` : purl.name), `name and packageURL name do not match`);
break;
case 'golang': {
let expected = purl.namespace
? `${purl.namespace}/${purl.name}`
: purl.name;
if (purl.subpath)
expected += `/${purl.subpath}`;
assert(pkg.name === expected, `name and packageURL name do not match`);
break;
}
case 'composer':
case 'npm':
case 'swift':
assert(pkg.name ===
(purl.namespace ? `${purl.namespace}/${purl.name}` : purl.name), `name and packageURL name do not match`);
break;
// The PURL spec for Linux distros does not include the source in the name.
// This is why we relax the assertion here and match only on the package name:
// <source name>/<package name> - we omit the source name
// For now, make this exception only for deb to cover a support case.
case 'deb': {
const pkgName = pkg.name.split('/').pop();
assert(pkgName === purl.name, 'name and packageURL name do not match');
if (((_a = purl.qualifiers) === null || _a === void 0 ? void 0 : _a['upstream']) && pkg.name.includes('/')) {
const pkgSrc = pkg.name.split('/')[0];
const pkgUpstream = purl.qualifiers['upstream'].split('@')[0];
assert(pkgSrc === pkgUpstream, 'source and packageURL source do not match');
}
break;
}
default:
assert(pkg.name === purl.name, `name and packageURL name do not match`);
}
// validate package version
switch (purl.type) {
// the Snyk version of a golang module is either
// - the version without "v", e.g. v1.2.3 -> 1.2.3
// - the pseudo-version hash, e.g. v0.0.0-000-acf48ae230a1 -> #acf48ae230a1
case 'golang': {
let version = purl.version;
if (purl.version) {
const maybePseudoVersion = reGolangPseudoVersion.exec(purl.version);
const maybeExactVersion = reGolangExactVersion.exec(purl.version);
if (maybePseudoVersion) {
version = `#${maybePseudoVersion[4]}`;
}
else if (maybeExactVersion) {
version = maybeExactVersion[1].replace(/^v/, '');
}
}
assert(pkg.version === version, `version and packageURL version do not match. want ${pkg.version} have: ${version}`);
break;
}
default:
assert(pkg.version === purl.version, `version and packageURL version do not match`);
}
}
catch (e) {
throw new errors_1.ValidationError(`packageURL validation failed: ${e}`);
}
}
exports.validatePackageURL = validatePackageURL;
//# sourceMappingURL=validate-graph.js.map
;