Security News
New Python Packaging Proposal Aims to Solve Phantom Dependency Problem with SBOMs
PEP 770 proposes adding SBOM support to Python packages to improve transparency and catch hidden non-Python dependencies that security tools often miss.
async-validate
Advanced tools
Asynchronous validation for node and the browser.
npm i async-validate
Define validation rules, assign them to a schema using the necessary plugins and call validate:
var Schema = require('..')
, descriptor = {name: {type: "string", required: true}}
, schema = new Schema(descriptor)
, source = {};
Schema.plugin([
require('../plugin/core'),
require('../plugin/string')]);
schema.validate(source, function(err, res) {
if(err) {
throw err;
}else if(res) {
// validation failed, res.errors is an array of all errors
// res.fields is a map keyed by field name with an array of
// errors per field
return console.dir(res.errors)
}
// validation passed
});
A descriptor is a collection of validation rules as a map of fields to rules, rules may be declared as an object
, array
or function
.
var descriptor = {
name: {type: 'string', required: true}
}
You may declare an array
to use multiple validation rules per field, see multiple rules.
Use an inline function definition for application specific rules, see inline rule.
function rule(cb)
Rules are functions that perform validation of a value, they are invoked in the scope of a rule instance (file, api docs).
A rule function can access all relevant properties and methods using this
and should raise an error if this.value
fails a validation test, see errors.
The plugin rule method of declaring rule functions is preferred as it is the most modular.
The rule function is assigned directly to the field:
var descriptor = {
id: function(cb) {
// if this.value has error condition call this.raise()
cb();
}
}
Assigned to the validator
field so that you may pass data from the rule to the function:
var descriptor = {
id: {
foo: 'bar',
validator: function(cb) {
console.log(this.foo);
// if this.value has error condition call this.raise()
cb();
}
}
}
Plugin that assigns the rule function as a static method.
Create a plugin module:
module.exports = function() {
// declare static rule function with name `id`
this.main.id = function id(cb) {
// if this.value has error condition call this.raise()
cb();
}
}
Load and use the plugin:
var Schema = require('async-validate');
Schema.plugin([require('./rule')]);
var descriptor = {
id: {type: 'id'}
}
The static id
method will then be invoked for every rule of type id
, this is the most portable style as it enables easily moving validation rules into modules and packages that may be shared.
It is often useful to test against multiple validation rules for a single field, to do so make the rule an array of objects, for example:
var descriptor = {
email: [
{type: "string", required: true},
function(cb) {
// test if email address (this.value) already exists
// in a database and call this.raise() if it does
cb();
}
]
}
If you need to validate deep object properties you may do so for validation rules that are of the object
or array
type by assigning nested rules to a fields
property of the rule.
var descriptor = {
name: {type: "string", required: true},
address: {
type: "object",
required: true,
fields: {
street: {type: "string", required: true},
city: {type: "string", required: true},
zip: {type: "string", required: true, len: 8, message: "invalid zip"}
}
}
}
var validator = new schema(descriptor);
validator.validate({address: {}}, function(err, res) {
// res.errors contains errors for name, street, city, zip
});
Note that if you do not specify the required
property on the parent rule it is perfectly valid for the field not to be declared on the source object and the deep validation rules will not be executed as there is nothing to validate against.
Deep rule validation creates a schema for the nested rules so you can also specify the options
passed to the schema.validate()
method.
var descriptor = {
name: {type: "string", required: true},
address: {
type: "object",
required: true,
options: {single: true, first: true},
fields: {
street: {type: "string", required: true},
city: {type: "string", required: true},
zip: {type: "string", required: true, len: 8, message: "invalid zip"}
}
}
}
var validator = new schema(descriptor);
validator.validate({address: {}}, function(err, res) {
// now res.errors only contains errors for name and street
});
The parent rule is also validated so if you have a set of rules such as:
var descriptor = {
roles: {
type: "array",
required: true,
len: 3,
fields: {
0: {type: "string", required: true},
1: {type: "string", required: true},
2: {type: "string", required: true}
}
}
}
And supply a source object of {roles: ["admin", "user"]}
then two errors will be created. One for the array length mismatch and one for the missing required array entry at index 2.
To raise an error in a validation rule call raise, the signature for raise is equivalent to util.format
except that it may also accept a Reason as the first argument.
function id(cb) {
if(!/^[a-z0-9-]+$/i.test(this.value)) {
this.raise('%s is not a valid id', this.field);
}
cb();
}
Decorate the error with a reason:
function id(cb) {
var reason;
if(!/^[a-z0-9-]+$/i.test(this.value)) {
reason = this.getReason(
'id', {description: 'Field value failed pattern match'});
this.raise(reason, '%s is not a valid id', this.field);
}
cb();
}
Adding a reason allows associating an identifier with an error and optional meta data about the error reason.
To use schema types you should load plugins for the types you wish to validate:
var schema = require('async-validate');
schema.plugin([
require('async-validate/plugin/array'),
require('async-validate/plugin/boolean'),
require('async-validate/plugin/number'),
require('async-validate/plugin/string')
])
As a shortcut you may use all available types with:
require('async-validate/plugin/all');
See plugins for the type plugins that ship with this module and zephyr for documentation on the plugin system.
The plugin fixture and the plugin test provide an example of creating a type plugin.
This section describes the recognised rule properties and their behaviour, if you are using an assigned rule or plugin rule you can define properties on the rule object and they are available to the rule function via this.rule
.
The type
property indicates the type of rule to use, a type corresponds to a plugin function and the plugin should have been loaded.
Recognised type values are:
string
: Must be of type string
.number
: Must be of type number
.boolean
: Must be of type boolean
.method
: Must be of type function
.null
: Must strictly equal null
.regexp
: Must be an instance of RegExp
or a string that does not generate an exception when creating a new RegExp
.integer
: Must be of type number
and an integer.float
: Must be of type number
and a floating point number.array
: Must be an array as determined by Array.isArray
.object
: Must be of type object
and not Array.isArray
.enum
: Value must exist in the list
.date
: Value must be valid as determined by moment().isValid()
.When the object
plugin has been loaded the type
field may be a function in which case the value must be an instanceof
the function assigned to type
.
When a rule is of the object
type and additional
is set to false
an error is raised if the source object contains any properties not in the schema.
Rules of the object
and array
type may declare a fields
object which declares a nested schema, see deep rules.
The message
rule property defines the error message when validation fails, it overrides any default message. The property may be a string
or function
, see messages.
The required
rule property indicates that the field must exist on the source object being validated.
The pattern
rule property is a regular expression that the value must match to pass validation.
A range is defined using the min
and max
properties. For string
and array
types comparison is performed against the length
, for number
types the number must not be less than min
nor greater than max
.
To validate an exact length of a field specify the len
property. For string
and array
types comparison is performed on the length
property, for the number
type this property indicates an exact match for the number
, ie, it may only be strictly equal to len
.
If the len
property is combined with the min
and max
range properties, len
takes precedence.
Used with the array
type as a shorthand for validating array values, may be an object
or array
containing validation rules.
When values
is an object it is applied to all array elements in the source array otherwise each values
entry is compared against each source array entry which allows mixed types to be used in arrays.
Note that values
is expanded to fields
, see deep rules.
To validate a value from a list of possible values use the enum
type with a list
property containing the valid values for the field, for example:
var descriptor = {
role: {type: "enum", list: ['admin', 'user', 'guest']}
}
Validating dates can be complex but using moment date validation is substantially easier.
If no format
is specified for a rule that is a date
type then it is assumed the date is ISO 8601. If a format is specified then the date is validated according to the specified format.
It is recommended you read the moment documentation on the isValid
method to understand what validation is performed.
The important part is:
Note: It is not intended to be used to validate that the input string matches the format string. Because the strictness of format matching can vary depending on the application and business requirements, this sort of validation is not included in Moment.js.
This limitation may be overcome by combining a pattern
in a date rule, for example:
var descriptor = {
active: {
type: "date",
format: "YYYY-MM-DD",
pattern: /^([\d]{4})-([\d]{2})-([\d]{2})$/
}
}
It is typical to treat required fields that only contain whitespace as errors. To add an additional test for a string that consists solely of whitespace add a whitespace
property to a rule with a value of true
. The rule must be a string
type.
You may wish to sanitize user input instead of testing for whitespace, see transform for an example that would allow you to strip whitespace.
Depending upon your application requirements, you may need i18n support or you may prefer different validation error messages.
The easiest way to achieve this is to assign a message
to a rule:
{name:{type: "string", required: true, message: "Name is required"}}
You may also use a function for the rule message, it is invoked in the scope of the validator and passed the original message and replacement parameters:
var descriptor = {
name: {
type: "string",
required: true,
message: function(message, parameters) {
return this.field + ' is required';
}
}
}
If you just want to change the default messages:
var Schema = require('async-validate')
, messages = require('async-validate/messages')
, descriptor = {name:{type: "string", required: true}}
, schema;
messages.required = "%s is a required field";
schema = new Schema(descriptor, {messages: messages});
Potentially you may require the same schema validation rules for different languages, in which case duplicating the schema rules for each language does not make sense.
In this scenario you could just require your own messages file for the language and assign it to the schema:
var Schema = require('async-validate')
, messages = require('messages-es')
, descriptor = {name:{type: "string", required: true}}
, schema = new Schema(descriptor, {messages: messages});
If you are defining your own rule functions it is better practice to assign the message strings to a messages object and then access the messages via the this.messages
property within the function.
Sometimes it is necessary to transform a value before validation, possibly to coerce the value or to sanitize it in some way. To do this add a transform
function to the validation rule. The property is transformed prior to validation and re-assigned to the source object to mutate the value of the property in place.
Without the transform
function validation would fail due to the pattern not matching as the input contains leading and trailing whitespace, but by adding the transform function validation passes and the field value is sanitized at the same time.
var Schema = require('..')
, descriptor = {
name: {
type: "string",
required: true, pattern: /^[a-z]+$/,
transform: function(value) {
return value.trim();
}
}
}
, schema = new Schema(descriptor)
, source = {name: " user "};
Schema.plugin([
require('../plugin/core'),
require('../plugin/string')]);
schema.validate(source, function(err, res) {
console.dir(source.name);
});
function Schema(descriptor, [opts])
Encapsulates the rules associated with a descriptor and the logic for performing validation.
function messages([messages])
Get or set the messages associated with the schema.
function validate(source, [options], cb)
Validates a source object against the schema rules.
source
: The object to validate.options
: An object describing processing options for the validation.cb
: Callback function to invoke when validation completes.Options:
first
: Invoke callback when the first validation rule generates an error, no more validation rules are processed.single
: Only ever return a single error, typically used in conjunction with first
when a validation rule could generate multiple errors.keys
: Specifies the keys on the source object to be validated. Use this option to validate fields in a determinate order or to validate a subset of the rules assigned to a schema.parallel
: A boolean indicating that the validation should be executed in parallel.field
: Field name for the root object, default is source
when not specified.rules
: Rules to apply to the root source object, may be an array or a single rule object.function plugin(plugins)
Static plugin loader method; accepts an array of plugin functions.
function clone(source, [target])
Static clone; deep copies simple objects and arrays, RegExp
instances are passed by reference.
function Reason(id, [opts])
Represents the reason for a validation error, may be created using getReason()
.
You must supply a reason id
; if opts
are passed they are assigned as properties of the reason instance. When toString()
is called on a Reason
instance the id
is returned.
function Rule(opts)
Encapsulates the data associated with a validation rule and the value to be validated. Rule functions are invoked in the scope of a Rule
instance which exposes the following public fields:
rule
: The validation rule in the schema descriptor.value
: The value of the source object property being validated.field
: The name of the field being validated.source
: The source object passed to validate()
.options
: The options passed to validate()
.messages
: Reference to the schema messages.errors
: Array of errors for the field validation.reasons
: Map of default error reasons.function isRoot()
Determine if this validation is being performed against the root source object.
function getReason(id, [opts])
Create a reason for a validation error, returns a Reason
instance suitable for passing as the first argument to raise.
function raise([reason], message, ...)
Adds an error message to the list of errors encountered during validation of a value.
The first argument may optionally be a Reason
instance returned by getReason()
allowing a user to associate an identifier with the validation error and optional additional information. A validation error generated with a Reason
has a reason
field referencing the supplied reason.
When replacement parameters are supplied the behaviour is identical to util.format
.
function format(message, ...)
Format a message with replacement parameters like util.format
.
Useful when a rule declares message
as a function and wishes to construct the error message with parameters.
function shouldValidate()
Returns a boolean
derived from the rule required
property and other factors to determine if the value should be subject to the validation rule, typically invoked within a rule validation function.
function diff(expected, received)
Compare two arrays, return false
if they are equal otherwise return an array that is the difference between the supplied arrays.
function required()
Validate a required field, typically invoked from a rule function, raises an error if a required field is not present.
function pattern()
Validate using a regexp pattern, typically invoked from a rule function, raises an error if a value fails to match a rule regexp pattern.
function range()
Validates that a value falls within a given range or is of a specific length, typically invoked from a rule function, raises an error if a value is out of bounds.
Run the test specifications:
npm test
Compile test specifications for the browser (open test/index.html
):
npm run spec
Generate code coverage:
npm run cover
Create a standalone browserify build:
npm run browserify
Remove generated files:
npm run clean
To generate all documentation:
npm run docs
Generate the project readme file mdp:
npm run readme
Everything is MIT. Read the license if you feel inclined.
Generated by mdp(1).
FAQs
Asynchronous validation for node and the browser
The npm package async-validate receives a total of 131 weekly downloads. As such, async-validate popularity was classified as not popular.
We found that async-validate demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 2 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
PEP 770 proposes adding SBOM support to Python packages to improve transparency and catch hidden non-Python dependencies that security tools often miss.
Security News
Socket CEO Feross Aboukhadijeh discusses open source security challenges, including zero-day attacks and supply chain risks, on the Cyber Security Council podcast.
Security News
Research
Socket researchers uncover how threat actors weaponize Out-of-Band Application Security Testing (OAST) techniques across the npm, PyPI, and RubyGems ecosystems to exfiltrate sensitive data.