From 059bf0fe376ff9643aee455fbe345855ebea708e Mon Sep 17 00:00:00 2001 From: "Kyle E. Mitchell" Date: Thu, 1 Dec 2022 12:33:58 -0800 Subject: [PATCH] Validate configuration against a JSON schema --- configuration-schema.json | 46 +++++++++++++++++++++++++++++++++++++++ index.js | 34 ++++++++++------------------- package.json | 1 + tests/schema.test.js | 33 ++++++++++++++++++++++++++++ 4 files changed, 91 insertions(+), 23 deletions(-) create mode 100644 configuration-schema.json create mode 100644 tests/schema.test.js diff --git a/configuration-schema.json b/configuration-schema.json new file mode 100644 index 0000000..544750c --- /dev/null +++ b/configuration-schema.json @@ -0,0 +1,46 @@ +{ + "type": "object", + "required": [ + "licenses" + ], + "properties": { + "licenses": { + "type": "object", + "properties": { + "blueOak": { + "enum": [ + "lead", + "bronze", + "silver", + "gold" + ] + }, + "spdx": { + "type": "array", + "items": { + "$ref": "#/definitions/nonEmptyString" + } + }, + "osi": { + "type": "boolean" + } + }, + "additionalProperties": false + }, + "packages": { + "type": "object", + "patternProperties": { + ".+": { + "$ref": "#/definitions/nonEmptyString" + } + } + } + }, + "additionalProperties": false, + "definitions": { + "nonEmptyString": { + "type": "string", + "minLength": 1 + } + } +} diff --git a/index.js b/index.js index 0a0a917..ce62e62 100644 --- a/index.js +++ b/index.js @@ -11,7 +11,8 @@ var satisfies = require('semver').satisfies var spdxAllowed = require('spdx-whitelisted') function licensee (configuration, path, callback) { - if (!validConfiguration(configuration)) { + var errors = configurationErrors(configuration) + if (errors) { return callback(new Error('Invalid configuration')) } configuration.licenses = compileLicenseAllowlist(configuration) @@ -51,29 +52,16 @@ function licensee (configuration, path, callback) { } } -function validConfiguration (configuration) { - return ( - isObject(configuration) && - has(configuration, 'licenses') && - isObject(configuration.licenses) && - has(configuration, 'packages') - ? ( - // Validate `packages` property. - isObject(configuration.packages) && - Object.keys(configuration.packages) - .every(function (key) { - return isString(configuration.packages[key]) - }) - ) : true - ) -} +var Ajv = require('ajv') +var ajv = new Ajv({ allErrors: true }) +var validate = ajv.compile(require('./configuration-schema')) -function isObject (argument) { - return argument && typeof argument === 'object' -} - -function isString (argument) { - return typeof argument === 'string' +function configurationErrors (configuration) { + if (validate(configuration)) { + return false + } else { + return validate.errors + } } function findIssues (configuration, dependencies) { diff --git a/package.json b/package.json index a0f2490..d3a3d95 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "dependencies": { "@blueoak/list": "^9.0.0", "@npmcli/arborist": "^6.1.2", + "ajv": "^8.11.2", "correct-license-metadata": "^1.4.0", "docopt": "^0.6.2", "has": "^1.0.3", diff --git a/tests/schema.test.js b/tests/schema.test.js new file mode 100644 index 0000000..d2f2e7e --- /dev/null +++ b/tests/schema.test.js @@ -0,0 +1,33 @@ +var tap = require('tap') +var Ajv = require('ajv') +var schema = require('../configuration-schema') + +var ajv = new Ajv({ allErrors: true }) +var validate = ajv.compile(schema) + +tap.test('configuration schema', function (test) { + ajv.validateSchema(schema) + test.same(ajv.errors, null, 'no schema errors') + test.end() +}) + +tap.test('valid configuration', function (test) { + test.ok( + validate({ + licenses: { osi: true, blueOak: 'silver' }, + packages: { licensee: '1.0.0' } + }), + 'valid configuration validates' + ) + test.end() +}) + +tap.test('invalid configuration', function (test) { + test.notOk( + validate({ + licenses: { blueoak: 'silver' } + }), + 'configuration with lower-case "blueoak" fails' + ) + test.end() +})