Security News
Research
Data Theft Repackaged: A Case Study in Malicious Wrapper Packages on npm
The Socket Research Team breaks down a malicious wrapper package that uses obfuscation to harvest credentials and exfiltrate sensitive data.
felv is a fast easy and light (synchronous and asynchronous) JavaScript validator.
Run the following command to add the package to your dev dependencies:
$ npm install --save felv
const felv = require('felv');
const schema = {
// Value of 'a' key is required and must be a number.
a: {
required: true,
type: 'number'
},
// Value of 'b' key is an array of numbers and strings.
b: {
type: Array,
items: {
type: ['string', 'number']
}
},
// Value of 'c' key is an instance of Object
// with properties i and j which are booleans
// of default value `true`.
c: {
type: Object,
properties: {
i: {
default: true,
type: 'boolean'
},
j: {
default: true,
type: 'boolean'
}
}
},
// Value of 'd' key is an instance of Object
// with properties of variable names and string values.
d: {
type: Object,
items: {
type: 'string'
}
}
};
const validatedValue = felv.validate(
{
a: 1,
b: [2, 'foo', 4],
c: {
i: false
},
d: {
x: 'foo',
y: 'bar'
}
},
schema
);
validatedValue
is equal to:
{
a: 1,
b: [2, 'foo', 4],
c: {
i: false,
j: true
},
d: {
x: 'foo',
y: 'bar'
}
}
You can easily validate a value from a schema:
const validatedValue = felv.validate(value, schema, options);
For repetitive validations, you should compile a validator to optimize validation performances. This can also be useful to detect schema format errors before validation occurs.
const validator = felv.compile(schema, options);
const validatedValue = validator.validate(value);
A schema is used to describe the format of a value. Each attribute of a schema is called a validation processor. A schema can embed other schemas to describe embedded structures thanks to some validation processors.
You can check the type of the value of a key:
{
foo: {
type: 'number'
},
bar: {
type: Date
},
foobar: {
type: ['number', 'string'] // number or string items
}
}
Matching values:
{
foo: 2,
bar: new Date(),
foobar: 3
}
{
foo: 2,
bar: new Date(),
foobar: 'foo'
}
You can specify a default value:
{
foo: {
default: 3,
type: 'number'
},
bar: {
default: 6
}
}
Matching value:
{
foo: 2
}
Validated value:
{
foo: 2,
bar: 6
}
You can specify a required value:
{
foo: {
required: true,
type: 'number'
},
bar: {
type: 'number'
}
}
Matching value:
{
foo: 2
}
You can specify embedded items of arrays and objects:
{
foo: {
type: Array,
items: {
type: 'number'
}
},
bar: {
type: Object,
items: {
type: 'number'
}
}
}
Matching value:
{
foo: [2, 3, 7],
bar: {a: 1, b: 5}
}
You can specify properties of objects:
{
foo: {
type: Object,
properties: {
a: {
required: true,
type: 'number'
},
b: {
default: 'bar',
type: 'string'
}
}
}
}
Matching value:
{
foo: {a: 4}
}
Validated value:
{
foo: {a: 4, b: 'bar'}
}
You can give a function to format input values:
{
foo: {
format: value => (Array.isArray(value) ? value : [value]),
type: Array,
items: {
type: 'string'
}
}
}
Matching value:
{
foo: 'bar'
}
Validated value:
{
foo: ['bar']
}
You can give a function to make custom validations and format output values:
{
foo: {
type: 'number',
validate: (value, expected) => {
if (value < 1 || value > 9) {
// Throw a validation error.
expected('a number between 1 and 9');
}
return value;
}
},
bar: {
type: 'number',
// Alter validated value.
validate: value => (value > 1 ? value * 10 : value)
}
}
Matching value:
{
foo: 4,
bar: 2
}
Validated value:
{
foo: 4,
bar: 20
}
You can specify a custom error message that will be set on error occurrence:
const schema = {
foo: {
required: true,
type: 'number',
error: 'You must specify a number for "foo"'
}
};
try {
felv.validate({foo: 'bar'}, schema);
} catch(error) {
console.log(error.customMessage); // Display `You must specify a number for "foo"`.
}
You can specify different validation ways givin an array of schemas:
{
foo: [
{
required: true,
type: 'number'
},
{
default: 'bar',
type: 'string'
}
]
}
Matching values:
{foo: 2}
{foo: 'foo'}
{}
Corresponding validated values:
{foo: 2}
{foo: 'foo'}
{foo: 'bar'}
You can use asynchronous functions in validation processors accepting a function (like format
and validate
) or directly return a Promise
. In that case, a Promise
will be returned by the validate
method.
Example:
const asyncSchema = {
foo: {
validate: async (value) => {
const validatedValue = await asyncValidationFunction(value);
return validatedValue + 1;
}
},
bar: {
format: (value) => {
return new Promise((resolve, reject) => {
asyncFormattingFunction(formattedValue => resolve(formattedValue));
});
}
}
};
const validatedValue = await felv.validate({foo: 3, bar: 4}, asyncSchema);
For simplicity, validation processors are independent from each other.
Validation processors are processed in a specific order:
You can modulate validation with following options:
Whether or not to force a Promise
result even on synchronous return.
Default value:
{
async: false
}
Whether or not to automatically convert some value types.
Default value:
{
convert: true
}
string
=> boolean
:
"false"
=> false
"true"
=> true
"0"
=> false
"1"
=> true
number
=> boolean
:
0
=> false
!== 0
=> true
string
=> number
:
"100"
=> 100
boolean
=> number
:
false
=> 0
true
=> 1
A validation object to be passed as second argument (options
) in format
validation processors:
{
foo: {
format: (value, options) => {
// Format value from options.
}
}
}
Default value:
{
formatting: {}
}
Whether or not to process full validation event after a first error occurred.
Default value:
{
full: false
}
Whether or not to use the original value for validated value. This increase performances but must not be used for cases where the original value must be preserved.
Default value:
{
immutable: false
}
Whether or not to process schema on a list of items (array or object) instead of an object with defined properties.
Default value:
{
list: false
}
A namespace to prefix paths in error messages.
Default value:
{
namespace: '$'
}
Whether or not fields are required by default.
Default value:
{
required: false
}
A validation object to be passed as third argument (options
) in validate
validation processors:
{
foo: {
validate: (value, expected, options) => {
// Validate 'value' from 'options'.
// Throw validation error with 'expected'.
}
}
}
expected
function take 3 arguments:
{string} expectedType
: Expected type (or value description){Array.<*>} [expectedValues]
: Optional expected values{string} [customMessage]
: Optional custom error message
Default value:
{
validation: {}
}
const validator = felv.compile({foo: {type: 'string'}});
try {
const validatedValue = validator.validate({foo: 3});
} catch (error) {
// Error type: 'ValidationError'
console.log(error.name);
// Explicit message: '[$.foo](type) Expected value to be a string, got a number of value `3` instead'
console.log(error.message);
// Path of the failing value: '$.foo'
console.log(error.path);
// Failing schema attribute/validation processor: 'type'
console.log(error.attribute);
// Subject of failure: 'value'
console.log(error.subject);
// Expected type: 'string'
console.log(error.expectedType);
// Expected values: []
console.log(error.expectedValues);
// Got(gotten) type: 'number'
console.log(error.gotValue);
// Got(gotten) value: 3
console.log(error.gotValue);
// Custom message (not defined here): ''
console.log(error.customMessage);
}
You can process full validation to retrieve all validation errors of a value against a schema. This can be useful to validate a user form and return all errors for instance:
// Compile form schema at program initialization.
const schema = {
firstname: {
required: true,
type: 'string',
// Define custom error message of occuring validation error.
error: 'Firstname must be filled'
},
lastname: {
required: true,
type: 'string'
},
email: {
required: true,
type: 'string',
validate: (value, expected) => {
if (!/@/.test(value)) {
// Define custom error message with third argument
// of expected function.
expected('email', null, 'Email is incorrect');
}
return value;
},
// `error` attribute does not overwrite already defined custom message
// like 'Email is incorrect' in above validate attribute function.
error: 'Email must be filled'
},
password: {
required: true,
type: 'string',
validate: (value, expected) => {
if (value.length < 8) {
expected('a string of at least 8 character');
} else if (!/[A-Z]/.test(value)) {
expected('a string with at least one uppercase letter');
}
return value;
},
error: 'Password must at least contains 8 characters with at least one uppercase letter'
}
};
const options = {
namespace: 'form',
full: true
};
const validator = felv.compile(schema, options);
// Use compiled validator to validate user input on user subscription.
// We assume `req.body` equals `{email: 'dumb', password: 'dumb'}`
try {
const validatedValue = validator.validate({
firstname: req.body.firstname,
lastname: req.body.lastname,
email: req.body.email,
password: req.body.email
});
// Do stuff like persisting user in database...
} catch (error) {
// Error type: 'FullValidationError'
console.log(error.name);
// Use `error.pathErrorMessages` to get validation error messages associated with failing paths.
assert.deepEqual(error.pathErrorMessages, {
'form.firstname': 'Firstname must be filled',
'form.lastname': '[form.lastname](required) Expected value to be a defined value, got `undefined` instead',
'form.email': 'Email is incorrect',
'form.password': 'Password must at least contains 8 characters with at least one uppercase letter'
});
// Use `error.pathErrors` to get the validation errors instead of messages.
// Use `error.errorMessages` to get validation error message list.
assert.deepEqual(error.errorMessages, [
'Email is incorrect',
'Firstname must be filled',
'Password must at least contains 8 characters with at least one uppercase letter',
'[form.lastname](required) Expected value to be a defined value, got `undefined` instead'
]);
// Use `error.errors` to get the validation errors instead of messages.
}
Many npm
scripts are available to help testing:
$ npm run {script}
check
: lint and check unit and integration testslint
: linttest
: check unit teststest-coverage
: check coverage of unit teststest-debug
: debug unit teststest-integration
: check integration teststest-performance
: check performance (just for fun)test-watch
: work in TDD!Use npm run check
to check that everything is ok.
If you want to contribute, just fork this repository and make a pull request!
Your development must respect these rules:
You must keep test coverage at 100%.
FAQs
Fast easy and light JavaScript validator.
The npm package felv receives a total of 1 weekly downloads. As such, felv popularity was classified as not popular.
We found that felv demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer 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
Research
The Socket Research Team breaks down a malicious wrapper package that uses obfuscation to harvest credentials and exfiltrate sensitive data.
Research
Security News
Attackers used a malicious npm package typosquatting a popular ESLint plugin to steal sensitive data, execute commands, and exploit developer systems.
Security News
The Ultralytics' PyPI Package was compromised four times in one weekend through GitHub Actions cache poisoning and failure to rotate previously compromised API tokens.