diff --git a/src/core/validate-graph.ts b/src/core/validate-graph.ts index 3ce4578..221b81b 100644 --- a/src/core/validate-graph.ts +++ b/src/core/validate-graph.ts @@ -60,6 +60,19 @@ export function validatePackageURL(pkg: types.PkgInfo): void { ); 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 === + (purlPkg.subpath + ? `${purlPkg.name}/${purlPkg.subpath}` + : purlPkg.name), + `name and packageURL name do not match`, + ); + break; + case 'golang': { let expected = purlPkg.namespace ? `${purlPkg.namespace}/${purlPkg.name}` diff --git a/test/core/validate-graph.test.ts b/test/core/validate-graph.test.ts index 185527a..92525e5 100644 --- a/test/core/validate-graph.test.ts +++ b/test/core/validate-graph.test.ts @@ -88,6 +88,58 @@ describe('validatePackageURL', () => { }); }); + describe('cocoapods Purl type tests', () => { + it.each([ + [ + 'cocoapods package without subspec', + { + name: 'bar', + version: '1.2.3', + purl: 'pkg:cocoapods/bar@1.2.3', + }, + ], + [ + 'cocoapods package with subspec', + { + name: 'spec/subspec', + version: '1.2.3', + purl: 'pkg:cocoapods/spec@1.2.3#subspec', + }, + ], + ])('validates cocoapods Purls: %s', (name, pkg) => { + expect(() => validatePackageURL(pkg)).not.toThrow(); + }); + + it.each([ + [ + 'package name does not match purl name', + { + name: 'foo', + version: '1.2.3', + purl: 'pkg:cocoapods/baz@1.2.3', + }, + ], + [ + 'package name does not match subspec', + { + name: 'baz/foo', + version: '1.2.3', + purl: 'pkg:cocoapods/baz@1.2.3#bar', + }, + ], + [ + 'package name does not include subspec', + { + name: 'bar', + version: '1.2.3', + purl: 'pkg:cocoapods/bar@1.2.3#baz', + }, + ], + ])('should throw on invalid purl: %s', (name, pkg) => { + expect(() => validatePackageURL(pkg)).toThrow(); + }); + }); + describe('composer Purl type tests', () => { it.each([ [