
Research
Security News
Lazarus Strikes npm Again with New Wave of Malicious Packages
The Socket Research Team has discovered six new malicious npm packages linked to North Korea’s Lazarus Group, designed to steal credentials and deploy backdoors.
api-doc-validator
Advanced tools
@url [METHOD] path
/**
* @url POST /path/:param
*/
Validate parameters of @url
path
@params [OBJECT_NAME =] json-schema|OBJECT_NAME
/**
* @url GET /users/:id
* @params {
* id: number,
* }
* @call users.get(id)
*/
or with OBJECT_NAME
assign for future use
/**
* @url GET /users/:id
* @params User = {
* id: number,
* }
*/
or use an external schema as root schema
/**
* @url GET /users/:id
* @params User
*/
or extend external schema
/**
* @url GET /users/:id
* @params {
* ...User,
* name: string,
* }
*/
Validate @url
query parameters
@query [OBJECT_NAME =] json-schema|OBJECT_NAME
/**
* @url GET /users
* @query {
* id: number,
* }
* @call users.get(id)
*/
Example of valid request GET /users?id=1
Names of fields in @params
and query
should be different to use them in @call
/**
* @url GET /users/:id
* @params {
* id: number,
* }
* @query {
* name: string,
* }
* @call users.get(id, name)
*/
@body [OBJECT_NAME =] json-schema|OBJECT_NAME
/**
* @body {
* id: number,
* name: string,
* }
*/
Response http code and validation of response body.
@response [CODE] [OBJECT_NAME =] json-schema|OBJECT_NAME
Response for 200
code
/**
* @response {
* id: number,
* name: string,
* }
*/
Validators for different codes of same request
/**
* @response 200 {
* id: number,
* name: string,
* }
* @response 500 {
* message: string,
* }
*/
Word used to filter validators in target file. You can use shortcut @ns
/**
* Namespace for this validator will be "default"
*
* @url POST /users
* @body {id: number}
*/
/**
* @namespace test
* @url POST /test-success
* @response 2xx {success: boolean}
*/
/**
* @ns test
* @url POST /test-error
* @response 5xx {error: boolean}
*/
Example of generation express middleware with only test
validators
npx adv -c path/to/config.json -n test -e ./test-validator.js
Define the new schema for future usage
@schema OBJECT_NAME = json-schema|OBJECT_NAME
/**
* @schema User = {
* id: number,
* name: string,
* }
*/
or just to make shorter schema name
/**
* @schema User = SomeVeryLongSchemaName
*/
You should provide valid js code of method call. This code will be used in your API tests.
@call object-method-call
/**
* @call object.method(param1, param2)
*/
HTTP request method. Default is config.defaultMethod
GET|POST|PUT|DELETE|HEAD|OPTIONS
HTTP response code. Default is config.defaultCode
Formats:
200
regular code number2xx
any code between 200 and 299200 - 400
any code between 200 and 400200 || 3xx || 400 - 500
or expressionExample
/**
* @response 2xx || 301 User = {id: number}
*/
URL pathname
. For path parsing used path-to-regexp lib.
Parameters like :id
can be used in @call
as parameter of method call with same name.
/**
* @url GET /users/:id(\d+)
* @call users.get(id)
*/
Any valid js object name like objectName
or with field of any deep objectName.fieldName.field
Object that describes how validate another object. For object validation used ajv lib with few modifications for less code writing.
Default ajv schema
schema = {
type: "object",
additionalProperties: false,
required: ['id', /* "name", */ 'enabled', 'list', 'user', 'enumOfStrings'],
properties: {
id: {
type: "number",
// extra fields for number: maximum, minimum, exclusiveMaximum, exclusiveMinimum, multipleOf
},
name: {
type: "string",
// extra fields for string: maxLength, minLength, pattern, format
},
enabled: {
type: "boolean",
// no extra fields
},
list: {
type: "array",
// extra fields for array: maxItems, minItems, uniqueItems, items, additionalItems, contains
},
user: {
type: "object",
// extra fields for object: maxProperties, minProperties, required, properties, patternProperties,
// additionalProperties, dependencies, propertyNames
},
enumOfStrings: {
type: "string",
enum: ["user", "guest", "owner"]
},
}
}
Simplified description of schema
schema = {
id: number,
[name]: string,
enabled: boolean,
listOfObjects: [{
id: number,
type: string,
}],
listOfNumbers: [{
type: "number"
}],
// which means list is array of numbers
user: {
id: number,
type: string,
},
enumOfStrings: "user" || "guest" || "owner",
}
So, if any object in a schema (including root) has field type
with one of the string values
"number"
, "integer"
, "string"
, "boolean"
, "array"
, "object"
or "null"
than it means this object is validator.
In any other cases this object will be converted to "object"
validator. Example
schema = {
days: [number],
list: [{
id: number,
type: string,
}],
user: {
id: number,
type: string,
},
parent: {
type: "object",
},
}
Will be converted to
schema = {
type: "object",
additionalProperties: false,
required: ['days', 'list', 'user', 'parent'],
properties: {
days: {
type: "array",
items: {
type: "number"
}
},
list: {
type: "array",
items: {
type: "object",
required: ["id", "type"],
properties: {
id: {
type: "number"
},
type: {
type: "string"
},
}
}
},
user: {
type: "object",
required: ["id", "type"],
properties: {
id: {
type: "number"
},
type: {
type: "string"
},
}
},
parent: {
type: "object",
},
}
}
By default, all fields in an object are required. To make field optional just put it in brackets.
schema = {
id: number,
[name]: string,
}
schema = {
type: "object",
additionalProperties: false,
required: ["id"],
properties: {
id: {type: "number"},
name: {type: "string"},
},
}
Instead of short number
validator you can use one of following number patterns as value of object field.
int
number without floating-pointpositive
positive number including 0
negative
negative number excluding 0
id
number more than 0
schema = {
id: id,
price: positive,
list: [int],
}
Will be converted to
schema = {
type: "object",
additionalProperties: false,
required: ['id', 'price', 'list'],
properties: {
id: {
type: "number",
minimum: 1,
},
price: {
type: "number",
minimum: 0,
},
list: {
type: "array",
items: {
type: "integer",
}
},
},
}
Instead of short string
validator you can use one of following string patterns as value of object field.
date
full-date according to RFC3339.time
time with optional time-zone.date-time
date-time from the same source (time-zone is optional, in ajv it's mandatory)date-time-tz
date-time with time-zone requireduri
full URI.uri-reference
URI reference, including full and relative URIs.uri-template
URI template according to RFC6570email
email address.hostname
host name according to RFC1034.filename
name (words with dashes) with extensionipv4
IP address v4.ipv6
IP address v6.regex
tests whether a string is a valid regular expression by passing it to RegExp constructor.uuid
Universally Unique Identifier according to RFC4122.schema = {
id: uuid,
email: email,
created_at: date-time,
days: [date],
}
Will be converted to
schema = {
type: "object",
additionalProperties: false,
required: ['id', 'email', 'created_at', 'days'],
properties: {
id: {
type: "string",
format: "uuid",
},
email: {
type: "string",
format: "email",
},
created_at: {
type: "string",
format: "date-time",
},
days: {
type: "array",
items: {
type: "string",
format: "date",
}
},
}
}
Using OBJECT_NAME
you can inject an external schema in a current schema.
/**
* @url GET /users/:id
* @response User = {
* id: number,
* name: string,
* }
*/
/**
* @url POST /users
* @body {
* action: 'update' || 'delete',
* user: User,
* }
*/
Instead of anyOf
you can use ||
operator
schema = {
data: User || Account || {type: "object"}
}
will be
schema = {
type: "object",
additionalProperties: false,
required: ['data'],
properties: {
data: {
anyOf: [
{/* schema of User */},
{/* schema of Account */},
{type: "object"},
]
}
}
}
Instead of allOf
you can use &&
operator
schema = {
data: User && Account && {type: "object"}
}
will be
schema = {
type: "object",
additionalProperties: false,
required: ['data'],
properties: {
data: {
allOf: [
{/* schema of User */},
{/* schema of Account */},
{type: "object"},
]
}
}
}
To extend you can use object spread operator
User = {
id: number,
data: string,
}
UserExtra = {
name: string,
created_at: date,
}
schema = {
...User,
...UserExtra,
age: number, // add field
data: undefined, // remove field
created_at: date-time, // overwrite field
}
will be
schema = {
type: "object",
additionalProperties: false,
required: ['id', 'name', 'created_at', 'age'],
properties: {
id: {type: "number"},
name: {type: "string"},
created_at: {type: "string", format: "date-time"},
age: {type: "number"},
}
}
Also, you can overwrite validator options
schema = {
...User,
type: "object",
additionalProperties: true,
}
Important to add type: "object"
it says to compiler that this object is pure ajv validator, not simplified version.
schema = {
type: "object",
additionalProperties: true,
properties: {
id: {type: "number"},
data: {type: "string"},
}
}
You extend even non object validators
phone = {
type: "string",
pattern: "^\\d+$"
}
schema = {
...phone,
type: "string",
maxLength: 20,
}
Uniq sample of JavaScript code with some method call of some object, which will be generated for testing purposes.
In method call you can use named parameters from @url
/**
* @url GET /users
* @call users.get()
*/
/**
* @url GET /users/:id
* @call users.get(id)
*/
/**
* @url POST /users/:id/settings
* @call users.setSettings(id)
*/
/**
* @url POST /users/:id/settings
* @call users.settings.update(id)
*/
There can be any number of nested objects and any method name. Only names of parameters should be equal.
with npx
npx adv -c path/to/config.json
Parameters:
-c, --config <path> path to config json file
-a, --api-client <path> generate api client
-b, --base-url <url> default Api.baseUrl
-e, --express <path> generate express middleware validator
-n, --namespace <namespace> generate validators only with this namespace or comma separated namespaces
-M, --default-method <method> default @url METHOD
-C, --default-code <code> default @response CODE
-T, --jsdoc-typedefs <boolean> generate typedef, default true
-R, --jsdoc-refs <boolean> use references to jsdoc @typedef or replace them with reference body, default true
-P, --extra-props <boolean> value for ajv "object" additionalProperties, default false
Install in to your project packages ajv, ajv-formats (optional if you not using String patterns), request (if you don't like request
then you will need to implement Api.request
) and path-to-regexp. Client will depend on them.
Generate the API client with npx adv -c path/to/config.json -a path/to/your/app/api-client.js
Example
/**
* @url POST /users
* @body User = {id: number, name: string}
* @response 200 User
* @call addUser()
*/
/**
* @url GET /users/:id
* @params {id: number}
* @response 200 User
* @call users.get()
*/
Generated api-client.js
will export class Api
.
const Api = require('./path/to/your/app/api-client.js');
const client = new Api(/* "https://new.base.url" optionaly, default is Api.baseUrl */);
console.log(Api.baseUrl); // value from config.baseUrl or --base-url cli option
console.log(Api.endpoints); // parsed endpoints from comments
// optionaly
Api.getAjv = () => createYourAjvInstance();
Api.request = function ({
method,
url,
params /* url params like /:id */,
query,
body,
endpoint /* object from Api.endpoints */,
context /* Api class instance */
}) {
return sendRequestReturnPromise(context.baseUrl + url);
}
// --
await client.addUser({id: 1, name: 'Test'});
client.users.get({id: 1} /* or just 1 if @params has only one parameter */)
.then(user => {
console.log(user.name);
console.log(client.requestCookieJar); // @see request.jar() https://github.com/request/request#examples
})
.catch(err => {
// @see Validation errors handling
});
Install in to your project packages ajv, ajv-formats (optional if you not using String patterns) and path-to-regexp. Middleware depends on them.
Generate the middleware with npx adv -c path/to/config.json -e path/to/your/app/validator.js
Then add middleware to your express app
const validator = require('./path/to/your/app/validator.js');
// optionaly
validator.getAjv = () => createYourAjvInstance();
// --
app.use(validator);
app.post('...', (req, res) => {});
app.use(function (err, req, res, next) {
if (err instanceof validator.RequestValidationError) {
// @see Validation errors handling
}
else if (err instanceof validator.ResponseValidationError) {
// @see Validation errors handling
}
else {
next(err);
}
// or use base class
if (err instanceof validator.ValidationError) {
// @see Validation errors handling
}
else {
next(err);
}
});
Generate the middleware with npx adv -c path/to/config.json -e path/to/your/app/validator.js
Then add it to your app
const validator = require('./validator.js');
function sendMessage(path, data) {
try {
var validateResponse = validator({
url: path,
body: data,
});
}
catch (err) {
// @see RequestValidationError
}
return ajax(path, data).then(function (result) {
if (validateResponse) {
try {
validateResponse(result);
}
catch (err) {
// @see ResponseValidationError
}
}
return result;
});
}
Both Api class and middleware exports three classes:
ValidationError
- base class, extends Error
RequestValidationError
- class of request validation error, extends ValidationError
ResponseValidationError
- class of response validation error, extends ValidationError
let err; // error from api client or middleware validator
let context; // Api class or middleware
if (err instanceof context.RequestValidationError) {
console.log(err.message);
console.log(err.property); // query | params | body
console.log(err.errors); // @see https://github.com/ajv-validator/ajv/blob/master/docs/api.md#validation-errors
}
else if (err instanceof context.ResponseValidationError) {
console.log(err.message); // "Invalid response body"
console.log(err.errors); // @see https://github.com/ajv-validator/ajv/blob/master/docs/api.md#validation-errors
}
// or use base class
if (err instanceof context.ValidationError) {
console.log(err.message);
console.log(err.property);
console.log(err.errors);
}
include
array of paths to files relative to config path, glob pattern usedexclude
array of paths to files to be excludeddefaultMethod
overwrites default METHOD. Default is GET
defaultCode
overwrites default CODE. Default is 200
jsdocTypedefs
generate @typedef
for each named schema. Default is true
jsdocRefs
use references to @typedef
or replace them with reference body. Default is true
apiClient
same as --api-client <path>
express
same as --express <path>
namespace
same as --namespace <namespace>
extraProps
same as --extra-props <boolean>
All paths are relative to config file location.
Example
{
"include": [
"src/**/*.js",
"src/**/*.php"
],
"exclude": [
"src/tests"
],
"defaultMethod": "POST",
"defaultCode": "2xx || 301"
}
FAQs
api doc and validator
We found that api-doc-validator demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 0 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.
Research
Security News
The Socket Research Team has discovered six new malicious npm packages linked to North Korea’s Lazarus Group, designed to steal credentials and deploy backdoors.
Security News
Socket CEO Feross Aboukhadijeh discusses the open web, open source security, and how Socket tackles software supply chain attacks on The Pair Program podcast.
Security News
Opengrep continues building momentum with the alpha release of its Playground tool, demonstrating the project's rapid evolution just two months after its initial launch.