Refine purl parsing and tests
This commit is contained in:
@@ -57,13 +57,15 @@ test('it raises an error if an empty allow list is specified', async () => {
|
||||
test('it raises an error when an invalid package-url is used for deny-packages', async () => {
|
||||
setInput('deny-packages', 'not-a-purl')
|
||||
|
||||
await expect(readConfig()).rejects.toThrow(`Error parsing purl`)
|
||||
await expect(readConfig()).rejects.toThrow(`Error parsing package-url`)
|
||||
})
|
||||
|
||||
test('it raises an error when an argument to deny-groups is missing a namespace', async () => {
|
||||
setInput('deny-groups', 'pkg:npm/my-fun-org')
|
||||
|
||||
await expect(readConfig()).rejects.toThrow(`purl must have a namespace`)
|
||||
await expect(readConfig()).rejects.toThrow(
|
||||
`package-url must have a namespace`
|
||||
)
|
||||
})
|
||||
|
||||
test('it raises an error when given an unknown severity', async () => {
|
||||
|
||||
+26
-4
@@ -4,20 +4,20 @@ import {parsePURL} from '../src/purl'
|
||||
test('parsePURL returns an error if the purl does not start with "pkg:"', () => {
|
||||
const purl = 'not-a-purl'
|
||||
const result = parsePURL(purl)
|
||||
expect(result.error).toEqual('purl must start with "pkg:"')
|
||||
expect(result.error).toEqual('package-url must start with "pkg:"')
|
||||
})
|
||||
|
||||
test('parsePURL returns an error if the purl does not contain an ecosystem', () => {
|
||||
test('parsePURL returns an error if the purl does not contain a type', () => {
|
||||
const purl = 'pkg:/'
|
||||
const result = parsePURL(purl)
|
||||
expect(result.error).toEqual('purl must contain an ecosystem')
|
||||
expect(result.error).toEqual('package-url must contain a type')
|
||||
})
|
||||
|
||||
test('parsePURL returns an error if the purl does not contain a namespace or name', () => {
|
||||
const purl = 'pkg:ecosystem/'
|
||||
const result = parsePURL(purl)
|
||||
expect(result.type).toEqual('ecosystem')
|
||||
expect(result.error).toEqual('purl must contain a namespace or name')
|
||||
expect(result.error).toEqual('package-url must contain a namespace or name')
|
||||
})
|
||||
|
||||
test('parsePURL returns a PURL with the correct values in the happy case', () => {
|
||||
@@ -88,6 +88,28 @@ test('parsePURL table test', () => {
|
||||
error: null
|
||||
}
|
||||
},
|
||||
{
|
||||
purl: 'pkg:/?',
|
||||
expected: {
|
||||
type: '',
|
||||
namespace: null,
|
||||
name: null,
|
||||
version: null,
|
||||
original: 'pkg:/?',
|
||||
error: 'package-url must contain a type'
|
||||
}
|
||||
},
|
||||
{
|
||||
purl: 'pkg:ecosystem/#',
|
||||
expected: {
|
||||
type: 'ecosystem',
|
||||
namespace: null,
|
||||
name: null,
|
||||
version: null,
|
||||
original: 'pkg:ecosystem/#',
|
||||
error: 'package-url must contain a namespace or name'
|
||||
}
|
||||
},
|
||||
{
|
||||
purl: 'pkg:ecosystem/name@version#subpath?attributes=123',
|
||||
expected: {
|
||||
|
||||
+36
-28
@@ -873,8 +873,8 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
||||
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
||||
exports.parsePURL = exports.PurlSchema = void 0;
|
||||
const z = __importStar(__nccwpck_require__(3301));
|
||||
// the basic purl type, containing ecosystem, namespace, name, and version.
|
||||
// other than ecosystem, all fields are nullable. this is for maximum flexibility
|
||||
// the basic purl type, containing type, namespace, name, and version.
|
||||
// other than type, all fields are nullable. this is for maximum flexibility
|
||||
// at the cost of strict adherence to the package-url spec.
|
||||
exports.PurlSchema = z.object({
|
||||
type: z.string(),
|
||||
@@ -884,7 +884,7 @@ exports.PurlSchema = z.object({
|
||||
original: z.string(),
|
||||
error: z.string().nullable()
|
||||
});
|
||||
const PURL_ECOSYSTEM = /pkg:([a-zA-Z0-9-_]+)\/.*/;
|
||||
const PURL_TYPE = /pkg:([a-zA-Z0-9-_]+)\/.*/;
|
||||
function parsePURL(purl) {
|
||||
const result = {
|
||||
type: '',
|
||||
@@ -895,19 +895,19 @@ function parsePURL(purl) {
|
||||
error: null
|
||||
};
|
||||
if (!purl.startsWith('pkg:')) {
|
||||
result.error = 'purl must start with "pkg:"';
|
||||
result.error = 'package-url must start with "pkg:"';
|
||||
return result;
|
||||
}
|
||||
const ecosystem = purl.match(PURL_ECOSYSTEM);
|
||||
if (ecosystem === null) {
|
||||
result.error = 'purl must contain an ecosystem';
|
||||
const type = purl.match(PURL_TYPE);
|
||||
if (!type) {
|
||||
result.error = 'package-url must contain a type';
|
||||
return result;
|
||||
}
|
||||
result.type = ecosystem[1];
|
||||
result.type = type[1];
|
||||
const parts = purl.split('/');
|
||||
// the first 'part' should be 'pkg:ecosystem'
|
||||
if (parts.length < 2 || parts[1].length === 0) {
|
||||
result.error = 'purl must contain a namespace or name';
|
||||
if (parts.length < 2 || !parts[1]) {
|
||||
result.error = 'package-url must contain a namespace or name';
|
||||
return result;
|
||||
}
|
||||
let namePlusRest;
|
||||
@@ -919,13 +919,17 @@ function parsePURL(purl) {
|
||||
namePlusRest = parts[2];
|
||||
}
|
||||
const name = namePlusRest.match(/([^@#?]+)[@#?]?.*/);
|
||||
if (name === null) {
|
||||
if (!result.namespace && !name) {
|
||||
result.error = 'package-url must contain a namespace or name';
|
||||
return result;
|
||||
}
|
||||
if (!name) {
|
||||
// we're done here
|
||||
return result;
|
||||
}
|
||||
result.name = decodeURIComponent(name[1]);
|
||||
const version = namePlusRest.match(/@([^#?]+)[#?]?.*/);
|
||||
if (version === null) {
|
||||
if (!version) {
|
||||
return result;
|
||||
}
|
||||
result.version = decodeURIComponent(version[1]);
|
||||
@@ -981,7 +985,7 @@ const PackageURL = z
|
||||
if (purl.error) {
|
||||
context.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: `Error parsing purl: ${purl.error}`
|
||||
message: `Error parsing package-url: ${purl.error}`
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -1000,7 +1004,7 @@ const PackageURLWithNamespace = z
|
||||
if (purl.namespace === null) {
|
||||
context.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: `purl must have a namespace, and the namespace must be followed by '/'`
|
||||
message: `package-url must have a namespace, and the namespace must be followed by '/'`
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -49894,8 +49898,8 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
||||
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
||||
exports.parsePURL = exports.PurlSchema = void 0;
|
||||
const z = __importStar(__nccwpck_require__(3301));
|
||||
// the basic purl type, containing ecosystem, namespace, name, and version.
|
||||
// other than ecosystem, all fields are nullable. this is for maximum flexibility
|
||||
// the basic purl type, containing type, namespace, name, and version.
|
||||
// other than type, all fields are nullable. this is for maximum flexibility
|
||||
// at the cost of strict adherence to the package-url spec.
|
||||
exports.PurlSchema = z.object({
|
||||
type: z.string(),
|
||||
@@ -49905,7 +49909,7 @@ exports.PurlSchema = z.object({
|
||||
original: z.string(),
|
||||
error: z.string().nullable()
|
||||
});
|
||||
const PURL_ECOSYSTEM = /pkg:([a-zA-Z0-9-_]+)\/.*/;
|
||||
const PURL_TYPE = /pkg:([a-zA-Z0-9-_]+)\/.*/;
|
||||
function parsePURL(purl) {
|
||||
const result = {
|
||||
type: '',
|
||||
@@ -49916,19 +49920,19 @@ function parsePURL(purl) {
|
||||
error: null
|
||||
};
|
||||
if (!purl.startsWith('pkg:')) {
|
||||
result.error = 'purl must start with "pkg:"';
|
||||
result.error = 'package-url must start with "pkg:"';
|
||||
return result;
|
||||
}
|
||||
const ecosystem = purl.match(PURL_ECOSYSTEM);
|
||||
if (ecosystem === null) {
|
||||
result.error = 'purl must contain an ecosystem';
|
||||
const type = purl.match(PURL_TYPE);
|
||||
if (!type) {
|
||||
result.error = 'package-url must contain a type';
|
||||
return result;
|
||||
}
|
||||
result.type = ecosystem[1];
|
||||
result.type = type[1];
|
||||
const parts = purl.split('/');
|
||||
// the first 'part' should be 'pkg:ecosystem'
|
||||
if (parts.length < 2 || parts[1].length === 0) {
|
||||
result.error = 'purl must contain a namespace or name';
|
||||
if (parts.length < 2 || !parts[1]) {
|
||||
result.error = 'package-url must contain a namespace or name';
|
||||
return result;
|
||||
}
|
||||
let namePlusRest;
|
||||
@@ -49940,13 +49944,17 @@ function parsePURL(purl) {
|
||||
namePlusRest = parts[2];
|
||||
}
|
||||
const name = namePlusRest.match(/([^@#?]+)[@#?]?.*/);
|
||||
if (name === null) {
|
||||
if (!result.namespace && !name) {
|
||||
result.error = 'package-url must contain a namespace or name';
|
||||
return result;
|
||||
}
|
||||
if (!name) {
|
||||
// we're done here
|
||||
return result;
|
||||
}
|
||||
result.name = decodeURIComponent(name[1]);
|
||||
const version = namePlusRest.match(/@([^#?]+)[#?]?.*/);
|
||||
if (version === null) {
|
||||
if (!version) {
|
||||
return result;
|
||||
}
|
||||
result.version = decodeURIComponent(version[1]);
|
||||
@@ -50002,7 +50010,7 @@ const PackageURL = z
|
||||
if (purl.error) {
|
||||
context.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: `Error parsing purl: ${purl.error}`
|
||||
message: `Error parsing package-url: ${purl.error}`
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -50021,7 +50029,7 @@ const PackageURLWithNamespace = z
|
||||
if (purl.namespace === null) {
|
||||
context.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: `purl must have a namespace, and the namespace must be followed by '/'`
|
||||
message: `package-url must have a namespace, and the namespace must be followed by '/'`
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
+1
-1
File diff suppressed because one or more lines are too long
+16
-12
@@ -1,7 +1,7 @@
|
||||
import * as z from 'zod'
|
||||
|
||||
// the basic purl type, containing ecosystem, namespace, name, and version.
|
||||
// other than ecosystem, all fields are nullable. this is for maximum flexibility
|
||||
// the basic purl type, containing type, namespace, name, and version.
|
||||
// other than type, all fields are nullable. this is for maximum flexibility
|
||||
// at the cost of strict adherence to the package-url spec.
|
||||
export const PurlSchema = z.object({
|
||||
type: z.string(),
|
||||
@@ -14,7 +14,7 @@ export const PurlSchema = z.object({
|
||||
|
||||
export type PackageURL = z.infer<typeof PurlSchema>
|
||||
|
||||
const PURL_ECOSYSTEM = /pkg:([a-zA-Z0-9-_]+)\/.*/
|
||||
const PURL_TYPE = /pkg:([a-zA-Z0-9-_]+)\/.*/
|
||||
|
||||
export function parsePURL(purl: string): PackageURL {
|
||||
const result: PackageURL = {
|
||||
@@ -26,19 +26,19 @@ export function parsePURL(purl: string): PackageURL {
|
||||
error: null
|
||||
}
|
||||
if (!purl.startsWith('pkg:')) {
|
||||
result.error = 'purl must start with "pkg:"'
|
||||
result.error = 'package-url must start with "pkg:"'
|
||||
return result
|
||||
}
|
||||
const ecosystem = purl.match(PURL_ECOSYSTEM)
|
||||
if (ecosystem === null) {
|
||||
result.error = 'purl must contain an ecosystem'
|
||||
const type = purl.match(PURL_TYPE)
|
||||
if (!type) {
|
||||
result.error = 'package-url must contain a type'
|
||||
return result
|
||||
}
|
||||
result.type = ecosystem[1]
|
||||
result.type = type[1]
|
||||
const parts = purl.split('/')
|
||||
// the first 'part' should be 'pkg:ecosystem'
|
||||
if (parts.length < 2 || parts[1].length === 0) {
|
||||
result.error = 'purl must contain a namespace or name'
|
||||
if (parts.length < 2 || !parts[1]) {
|
||||
result.error = 'package-url must contain a namespace or name'
|
||||
return result
|
||||
}
|
||||
let namePlusRest: string
|
||||
@@ -49,13 +49,17 @@ export function parsePURL(purl: string): PackageURL {
|
||||
namePlusRest = parts[2]
|
||||
}
|
||||
const name = namePlusRest.match(/([^@#?]+)[@#?]?.*/)
|
||||
if (name === null) {
|
||||
if (!result.namespace && !name) {
|
||||
result.error = 'package-url must contain a namespace or name'
|
||||
return result
|
||||
}
|
||||
if (!name) {
|
||||
// we're done here
|
||||
return result
|
||||
}
|
||||
result.name = decodeURIComponent(name[1])
|
||||
const version = namePlusRest.match(/@([^#?]+)[#?]?.*/)
|
||||
if (version === null) {
|
||||
if (!version) {
|
||||
return result
|
||||
}
|
||||
result.version = decodeURIComponent(version[1])
|
||||
|
||||
+2
-2
@@ -15,7 +15,7 @@ const PackageURL = z
|
||||
if (purl.error) {
|
||||
context.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: `Error parsing purl: ${purl.error}`
|
||||
message: `Error parsing package-url: ${purl.error}`
|
||||
})
|
||||
}
|
||||
})
|
||||
@@ -35,7 +35,7 @@ const PackageURLWithNamespace = z
|
||||
if (purl.namespace === null) {
|
||||
context.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: `purl must have a namespace, and the namespace must be followed by '/'`
|
||||
message: `package-url must have a namespace, and the namespace must be followed by '/'`
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user