Skip to content

Commit

Permalink
Merge pull request #113 from snyk/fix/validate-golang-version
Browse files Browse the repository at this point in the history
  • Loading branch information
mcombuechen authored Aug 31, 2023
2 parents e53cac0 + 3f1638e commit b03a342
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 26 deletions.
72 changes: 46 additions & 26 deletions src/core/validate-graph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ import { PackageURL } from 'packageurl-js';
import * as types from './types';
import { ValidationError } from './errors';

const reGolangPseudoVersion = /(v\d+\.\d+\.\d+)-(.*?)(\d{14})-([0-9a-f]{12})/;
const reGolangExactVersion = /^(.*?)(\+incompatible)?$/;

function assert(condition: boolean, msg: string) {
if (!condition) {
throw new ValidationError(msg);
Expand All @@ -14,7 +17,7 @@ export function validateGraph(
rootNodeId: string,
pkgs: { [pkgId: string]: any },
pkgNodes: { [nodeId: string]: Set<string> },
) {
): void {
assert(
(graph.predecessors(rootNodeId) || []).length === 0,
`"${rootNodeId}" is not really the root`,
Expand Down Expand Up @@ -48,14 +51,15 @@ export function validatePackageURL(pkg: types.PkgInfo): void {
}

try {
const purlPkg = PackageURL.fromString(pkg.purl);
const purl = PackageURL.fromString(pkg.purl);

switch (purlPkg.type) {
// 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 === purlPkg.namespace + ':' + purlPkg.name,
pkg.name === purl.namespace + ':' + purl.name,
`name and packageURL name do not match`,
);
break;
Expand All @@ -66,18 +70,16 @@ export function validatePackageURL(pkg: types.PkgInfo): void {
case 'cocoapods':
assert(
pkg.name ===
(purlPkg.subpath
? `${purlPkg.name}/${purlPkg.subpath}`
: purlPkg.name),
(purl.subpath ? `${purl.name}/${purl.subpath}` : purl.name),
`name and packageURL name do not match`,
);
break;

case 'golang': {
let expected = purlPkg.namespace
? `${purlPkg.namespace}/${purlPkg.name}`
: purlPkg.name;
if (purlPkg.subpath) expected += `/${purlPkg.subpath}`;
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;
}
Expand All @@ -87,9 +89,7 @@ export function validatePackageURL(pkg: types.PkgInfo): void {
case 'swift':
assert(
pkg.name ===
(purlPkg.namespace
? `${purlPkg.namespace}/${purlPkg.name}`
: purlPkg.name),
(purl.namespace ? `${purl.namespace}/${purl.name}` : purl.name),
`name and packageURL name do not match`,
);
break;
Expand All @@ -100,13 +100,10 @@ export function validatePackageURL(pkg: types.PkgInfo): void {
// For now, make this exception only for deb to cover a support case.
case 'deb': {
const pkgName = pkg.name.split('/').pop();
assert(
pkgName === purlPkg.name,
'name and packageURL name do not match',
);
if (purlPkg.qualifiers?.['upstream'] && pkg.name.includes('/')) {
assert(pkgName === purl.name, 'name and packageURL name do not match');
if (purl.qualifiers?.['upstream'] && pkg.name.includes('/')) {
const pkgSrc = pkg.name.split('/')[0];
const pkgUpstream = purlPkg.qualifiers['upstream'].split('@')[0];
const pkgUpstream = purl.qualifiers['upstream'].split('@')[0];
assert(
pkgSrc === pkgUpstream,
'source and packageURL source do not match',
Expand All @@ -115,16 +112,39 @@ export function validatePackageURL(pkg: types.PkgInfo): void {
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.name === purlPkg.name,
`name and packageURL name do not match`,
pkg.version === purl.version,
`version and packageURL version do not match`,
);
}
assert(
pkg.version === purlPkg.version,
`version and packageURL version do not match`,
);
} catch (e) {
throw new ValidationError(`packageURL validation failed: ${e}`);
}
Expand Down
24 changes: 24 additions & 0 deletions test/core/validate-graph.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,30 @@ describe('validatePackageURL', () => {
purl: 'pkg:golang/github.com/foo/[email protected]#pkg/baz',
},
],
[
'golang package with exact version',
{
name: 'github.com/foo/bar',
version: '1.2.3',
purl: 'pkg:golang/github.com/foo/[email protected]',
},
],
[
'golang package with incompatible version',
{
name: 'github.com/foo/bar',
version: '1.2.3',
purl: 'pkg:golang/github.com/foo/[email protected]+incompatible',
},
],
[
'golang package with pseudo version',
{
name: 'github.com/foo/bar',
version: '#0123456abcde',
purl: 'pkg:golang/github.com/foo/[email protected]',
},
],
])('validates golang Purls: %s', (name, pkg) => {
expect(() => validatePackageURL(pkg)).not.toThrow();
});
Expand Down

0 comments on commit b03a342

Please sign in to comment.