UNPKG

@snyk/dep-graph

Version:

Snyk dependency graph library

114 lines 5.41 kB
"use strict"; 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