diff --git a/package-lock.json b/package-lock.json index cf4abd9e85..28e06c6ff1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17324,6 +17324,7 @@ }, "devDependencies": { "@types/json-stable-stringify": "^1.0.34", + "ajv": "^8.12.0", "ses": "^0.18.8", "tmp-promise": "^3.0.3" }, @@ -17331,6 +17332,22 @@ "node": "^16.20.0 || ^18.0.0 || ^20.0.0" } }, + "packages/core/node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, "packages/core/node_modules/type-fest": { "version": "4.3.3", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.3.3.tgz", diff --git a/packages/core/package.json b/packages/core/package.json index 100a2f3a4d..352696f6b4 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -17,6 +17,7 @@ }, "devDependencies": { "@types/json-stable-stringify": "^1.0.34", + "ajv": "^8.12.0", "ses": "^0.18.8", "tmp-promise": "^3.0.3" }, diff --git a/packages/core/test/policy/invalid-bad-path.json b/packages/core/test/policy/invalid-bad-path.json new file mode 100644 index 0000000000..c5420e87ad --- /dev/null +++ b/packages/core/test/policy/invalid-bad-path.json @@ -0,0 +1,7 @@ +{ + "resolutions": { + "some>package": { + "some>other>package": "/etc/passwd" + } + } +} diff --git a/packages/core/test/policy/invalid-bad-pkg-name.json b/packages/core/test/policy/invalid-bad-pkg-name.json new file mode 100644 index 0000000000..8b47d86c37 --- /dev/null +++ b/packages/core/test/policy/invalid-bad-pkg-name.json @@ -0,0 +1,9 @@ +{ + "resources": { + "bad>!!packagename": { + "globals": { + "console": true + } + } + } +} diff --git a/packages/core/test/policy/invalid-empty.json b/packages/core/test/policy/invalid-empty.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/packages/core/test/policy/invalid-empty.json @@ -0,0 +1 @@ +{} diff --git a/packages/core/test/policy/valid.json b/packages/core/test/policy/valid.json new file mode 100644 index 0000000000..617d43575d --- /dev/null +++ b/packages/core/test/policy/valid.json @@ -0,0 +1,63 @@ +{ + "resources": { + "browserify>buffer": { + "globals": { + "console": true + }, + "packages": { + "browserify>buffer>base64-js": true, + "browserify>buffer>ieee754": true + } + }, + "browserify>events": { + "globals": { + "console": true + } + }, + "browserify>process": { + "globals": { + "clearTimeout": true, + "setTimeout": true + } + }, + "browserify>string_decoder": { + "packages": { + "browserify>util>safe-buffer": true + } + }, + "browserify>util>safe-buffer": { + "packages": { + "browserify>buffer": true + } + }, + "keccak": { + "packages": { + "browserify>buffer": true, + "keccak>readable-stream": true + } + }, + "keccak>readable-stream": { + "packages": { + "browserify>browser-resolve": true, + "browserify>buffer": true, + "browserify>events": true, + "browserify>inherits": true, + "browserify>process": true, + "browserify>string_decoder": true, + "keccak>readable-stream>util-deprecate": true + } + }, + "keccak>readable-stream>util-deprecate": { + "globals": { + "console.trace": true, + "console.warn": true, + "localStorage": true + } + } + }, + "resolutions": { + "some>package": { + "some>other>package": "./here" + } + } +} diff --git a/packages/core/test/policySchema.spec.js b/packages/core/test/policySchema.spec.js new file mode 100644 index 0000000000..4cd4ec78a5 --- /dev/null +++ b/packages/core/test/policySchema.spec.js @@ -0,0 +1,29 @@ +const test = require('ava') +const Ajv = require('ajv') + +const schema = require('../schema/lavamoat-policy.v0-0-1.schema.json') +const validPolicy = require('./policy/valid.json') +const invalidPolicyEmpty = require('./policy/invalid-empty.json') +const invalidBadPkgName = require('./policy/invalid-bad-pkg-name.json') +const invalidBadPath = require('./policy/invalid-bad-path.json') + +const ajv = new Ajv({ allErrors: true }) +const validate = ajv.compile(schema) +test('policy schema - known good policy', (t) => { + t.truthy(validate(validPolicy)) +}) + +test('policy schema - invalid policy - empty', (t) => { + t.falsy(validate(invalidPolicyEmpty)) + t.snapshot(validate.errors) +}) + +test('policy schema - invalid policy - bad pkg name', (t) => { + t.falsy(validate(invalidBadPkgName)) + t.snapshot(validate.errors) +}) + +test('policy schema - invalid policy - bad resolution path', (t) => { + t.falsy(validate(invalidBadPath)) + t.snapshot(validate.errors) +}) diff --git a/packages/core/test/snapshots/policySchema.spec.js.md b/packages/core/test/snapshots/policySchema.spec.js.md new file mode 100644 index 0000000000..f21ae84e23 --- /dev/null +++ b/packages/core/test/snapshots/policySchema.spec.js.md @@ -0,0 +1,69 @@ +# Snapshot report for `test/policySchema.spec.js` + +The actual snapshot is saved in `policySchema.spec.js.snap`. + +Generated by [AVA](https://avajs.dev). + +## policy schema - invalid policy - empty + +> Snapshot 1 + + [ + { + instancePath: '', + keyword: 'required', + message: 'must have required property \'resources\'', + params: { + missingProperty: 'resources', + }, + schemaPath: '#/anyOf/0/required', + }, + { + instancePath: '', + keyword: 'required', + message: 'must have required property \'resolutions\'', + params: { + missingProperty: 'resolutions', + }, + schemaPath: '#/anyOf/1/required', + }, + { + instancePath: '', + keyword: 'anyOf', + message: 'must match a schema in anyOf', + params: {}, + schemaPath: '#/anyOf', + }, + ] + +## policy schema - invalid policy - bad pkg name + +> Snapshot 1 + + [ + { + instancePath: '/resources', + keyword: 'additionalProperties', + message: 'must NOT have additional properties', + params: { + additionalProperty: 'bad>!!packagename', + }, + schemaPath: '#/properties/resources/additionalProperties', + }, + ] + +## policy schema - invalid policy - bad resolution path + +> Snapshot 1 + + [ + { + instancePath: '/resolutions/some>package/some>other>package', + keyword: 'pattern', + message: 'must match pattern "^(\\.{1,2})(/(?=[^/\\0])[^/\\0]+)*/?$"', + params: { + pattern: '^(\\.{1,2})(/(?=[^/\\0])[^/\\0]+)*/?$', + }, + schemaPath: '#/properties/resolutions/patternProperties/%5E(%40%5Ba-z0-9-~0%5D%5Ba-z0-9-._~0%5D*~1)%3F%5Ba-z0-9-~0%5D%5Ba-z0-9-._~0%5D*(%3E(%40%5Ba-z0-9-~0%5D%5Ba-z0-9-._~0%5D*~1)%3F%5Ba-z0-9-~0%5D%5Ba-z0-9-._~0%5D*)*%24/patternProperties/%5E(%40%5Ba-z0-9-~0%5D%5Ba-z0-9-._~0%5D*~1)%3F%5Ba-z0-9-~0%5D%5Ba-z0-9-._~0%5D*(%3E(%40%5Ba-z0-9-~0%5D%5Ba-z0-9-._~0%5D*~1)%3F%5Ba-z0-9-~0%5D%5Ba-z0-9-._~0%5D*)*%24/pattern', + }, + ] diff --git a/packages/core/test/snapshots/policySchema.spec.js.snap b/packages/core/test/snapshots/policySchema.spec.js.snap new file mode 100644 index 0000000000..468f95f013 Binary files /dev/null and b/packages/core/test/snapshots/policySchema.spec.js.snap differ