Awesome
@exodus/schemasafe
A code-generating JSON Schema validator that attempts to be reasonably secure.
Supports draft-04/06/07/2019-09/2020-12 and the
discriminator
OpenAPI keyword.
Features
- Converts schemas to self-contained JavaScript files, can be used in the build process.
Integrates nicely with bundlers, so one won't need to generate code in runtime, and that works with CSP. - Optional
requireValidation: true
mode enforces full validation of the input object.
Usingmode: "strong"
is recommended, — it combines that option with additional schema safety checks. - Does not fail open on unknown or unprocessed keywords — instead throws at build time if schema was not fully understood. That is implemented by tracking processed keywords and ensuring that none remain uncovered.
- Does not fail open on schema problems — instead throws at build time.
E.g. it will detect mistakes like{type: "array", "maxLength": 2}
. - About 2000 lines of code, non-minified.
- Uses secure code generation approach to prevent data from schema from leaking into the generated code without being JSON-wrapped.
- 0 dependencies
- Very fast
- Supports JSON Schema draft-04/06/07/2019-09/2020-12 and a strict subset of the
discriminator
OpenAPI keyword. - Can assign defaults and/or remove additional properties when schema allows to do that safely. Throws at build time if those options are used with schemas that don't allow to do that safely.
- Can be used as a schema linter.
Installation
npm install --save @exodus/schemasafe
Usage
Simply pass a schema to compile it:
const { validator } = require('@exodus/schemasafe')
const validate = validator({
type: 'object',
required: ['hello'],
properties: {
hello: {
type: 'string'
}
}
})
console.log('should be valid', validate({ hello: 'world' }))
console.log('should not be valid', validate({}))
Or use the parser API (running in strong mode by default):
const { parser } = require('@exodus/schemasafe')
const parse = parser({
$schema: 'https://json-schema.org/draft/2019-09/schema',
type: 'object',
required: ['hello'],
properties: {
hello: {
pattern: '^[a-z]+$',
type: 'string'
}
},
additionalProperties: false
})
console.log(parse('{"hello": "world" }')) // { valid: true, value: { hello: 'world' } }
console.log(parse('{}')) // { valid: false }
Parser API is recommended, because this way you can avoid handling unvalidated JSON objects in non-string form at all in your code.
Options
See options documentation for the full list of supported options.
Custom formats
@exodus/schemasafe
supports the formats specified in JSON schema v4 (such as date-time).
If you want to add your own custom formats pass them as the formats options to the validator:
const validate = validator({
type: 'string',
format: 'no-foo'
}, {
formats: {
'no-foo': (str) => !str.includes('foo'),
}
})
console.log(validate('test')) // true
console.log(validate('foo')) // false
const parse = parser({
$schema: 'https://json-schema.org/draft/2019-09/schema',
type: 'string',
format: 'only-a'
}, {
formats: {
'only-a': /^a+$/,
}
})
console.log(parse('"aa"')) // { valid: true, value: 'aa' }
console.log(parse('"ab"')) // { valid: false }
External schemas
You can pass in external schemas that you reference using the $ref
attribute as the schemas
option
const ext = {
type: 'string'
}
const schema = {
$ref: 'ext#' // references another schema called ext
}
// pass the external schemas as an option
const validate = validator(schema, { schemas: { ext: ext }})
console.log(validate('hello')) // true
console.log(validate(42)) // false
schemas
can be either an object as shown above, a Map
, or plain array of schemas (given that
those have corresponding $id
set at top level inside schemas themselves).
Enabling errors shows information about the source of the error
When the includeErrors
option is set to true
, @exodus/schemasafe
also outputs:
keywordLocation
: a JSON pointer string as an URI fragment indicating which sub-schema failed, e.g.#/properties/item/type
instanceLocation
: a JSON pointer string as an URI fragment indicating which property of the object failed validation, e.g.#/item
const schema = {
type: 'object',
required: ['hello'],
properties: {
hello: {
type: 'string'
}
}
}
const validate = validator(schema, { includeErrors: true })
validate({ hello: 100 });
console.log(validate.errors)
// [ { keywordLocation: '#/properties/hello/type', instanceLocation: '#/hello' } ]
Or, similarly, with parser API:
const schema = {
$schema: 'https://json-schema.org/draft/2019-09/schema',
type: 'object',
required: ['hello'],
properties: {
hello: {
type: 'string',
pattern: '^[a-z]+$',
}
},
additionalProperties: false,
}
const parse = parser(schema, { includeErrors: true })
console.log(parse('{ "hello": 100 }'));
// { valid: false,
// error: 'JSON validation failed for type at #/hello',
// errors: [ { keywordLocation: '#/properties/hello/type', instanceLocation: '#/hello' } ]
// }
Only the first error is reported by default unless allErrors
option is also set to true
in
addition to includeErrors
.
See Error handling for more information.
Generate Modules
See the doc/samples directory to see how @exodus/schemasafe
compiles
supported test suites.
To compile a validator function to an IIFE, call validate.toModule()
:
const { validator } = require('@exodus/schemasafe')
const schema = {
type: 'string',
format: 'hex'
}
// This works with custom formats as well.
const formats = {
hex: (value) => /^0x[0-9A-Fa-f]*$/.test(value),
}
const validate = validator(schema, { formats })
console.log(validate.toModule())
/** Prints:
* (function() {
* 'use strict'
* const format0 = (value) => /^0x[0-9A-Fa-f]*$/.test(value);
* return (function validate(data) {
* if (data === undefined) data = null
* if (!(typeof data === "string")) return false
* if (!format0(data)) return false
* return true
* })})();
*/
Performance
@exodus/schemasafe
uses code generation to turn a JSON schema into javascript code that is easily
optimizeable by v8 and extremely fast.
See Performance for information on options that might affect performance both ways.
Contributing
Get a fully set up development environment with:
git clone https://github.com/ExodusMovement/schemasafe
cd schemasafe
git submodule update --init --recursive
yarn
yarn lint
yarn test
Previous work
This is based on a heavily rewritten version of the amazing (but outdated) is-my-json-valid by @mafintosh.
Compared to is-my-json-valid
, @exodus/schemasafe
adds security-first design, many new features,
newer spec versions support, slimmer and more maintainable code, 0 dependencies, self-contained JS
module generation, fixes bugs and adds better test coverage, and drops support for outdated Node.js
versions.