
Research
/Security News
Critical Vulnerability in NestJS Devtools: Localhost RCE via Sandbox Escape
A flawed sandbox in @nestjs/devtools-integration lets attackers run code on your machine via CSRF, leading to full Remote Code Execution (RCE).
swagger-object-validator
Advanced tools
Validate your objects against a swagger spec and receive in-depth error traces
Validate an Object against a given swagger (V2.0) API definition.
A swagger definition specifies an API with requests and data models, and there are a lot of compilers to create server and client skeletons. There are some tools that validate the requests that were sent to the server, but surprisingly there is a huge lack of (good) validators for response bodies.
Just test it quickly and be amazed:
Please note: Request validation and the validation of a swagger spec itself is explicitly not intended (there are other modules available), only the validation of the objects returned from the server is part of this module. Ensure that your swagger spec is valid to prevent unexpected errors.
The API is awesome, it gives you easy and full control over what's happening:
The following swagger specifications are validated:
Let's assume you got a pet from your pet store and want to validate it.
import * as SwaggerValidator from 'swagger-object-validator';
let validator = new SwaggerValidator.Handler('https://raw.githubusercontent.com/OAI/OpenAPI-Specification/master/examples/v2.0/yaml/petstore.yaml');
let pet = {
id: 123,
name: 'Doge'
};
validator.validateModel(pet, 'Pet', (err, result) => {
console.log(result.humanReadable());
});
var swaggerValidator = require('swagger-object-validator');
var validator = new swaggerValidator.Handler('https://raw.githubusercontent.com/OAI/OpenAPI-Specification/master/examples/v2.0/yaml/petstore.yaml');
var pet = {
id: 123,
name: 'Doge'
};
validator.validateModel(pet, 'Pet', function (err, result) {
console.log(result.humanReadable());
});
Both will print out "Valid", since they comply to the swagger specification.
So lets change our pet model to be invalid (the rest of the code remains the same):
var pet = {
id: 'This is not a number',
foo: 'bar',
tag: [
'This is an optional argument, but it',
'Should be a String, not an Array of Strings'
]
}
Now it will print out:
Missing required property:
- At Pet/name
Type mismatch:
- At Pet/id
- Should be: "number"
- Is: "string"
Additional Property:
- At Pet/foo
Type mismatch:
- At Pet/tag
- Should be: "string"
- Is: "array"
This is the human readable error trace, because we called result.humanReadable()
. It will print out a full path to the error, where each step is seperated with a slash. They come quite handy if you need to find an error in a very complex model, and they support array positions and polymorphism!
The human readable trace is just a rendered version of result.errors
, which looks like this:
[
{
"errorType": 0,
"trace": [
{
"stepName": "Pet"
},
{
"stepName": "name"
}
]
},
{
"trace": [
{
"stepName": "Pet"
},
{
"stepName": "id"
}
],
"errorType": 2,
"typeIs": "string",
"typeShouldBe": "number"
},
{
"errorType": 1,
"trace": [
{
"stepName": "Pet"
},
{
"stepName": "foo"
}
]
},
{
"trace": [
{
"stepName": "Pet"
},
{
"stepName": "tag"
}
],
"errorType": 2,
"typeIs": "array",
"typeShouldBe": "string"
}
]
If you don't like the error types as integers (which will happen if you don't use TypeScript), call result.errorsWithStringTypes()
and all those errorTypes will be called "MISSING_REQUIRED_PROPERTY", "TYPE_MISMATCH" and "ADDITIONAL_PROPERTY".
You may load JSON or yaml files from your disk or from the interwebs. It doesn't matter!
import * as SwaggerValidator from 'swagger-object-validator';
let validator = new SwaggerValidator.Handler('https://raw.githubusercontent.com/OAI/OpenAPI-Specification/master/examples/v2.0/yaml/petstore.yaml');
// or
let validator = new SwaggerValidator.Handler('./petstore.yaml');
// or
let validator = new SwaggerValidator.Handler('./petstore.json');
// or
let petStore = require('./petstore.json');
let validator = new SwaggerValidator.Handler(petStore);
Up to now we always loaded the complete Swagger Specification. Maybe you don't need that and already know the exact model spec? Just validate against a model spec directly:
import * as SwaggerValidator from 'swagger-object-validator';
let validator = new SwaggerValidator.Handler();
let spec = {
type: "array",
items: {
type: "string"
}
}
let model = ['1', '2'];
validator.validateModel(model, spec, (err, result) => {
console.log(result.humanReadable());
});
If you need to validate a model against a definition that is not part of the definitions section, you can fetch the model specification like so:
import * as SwaggerValidator from 'swagger-object-validator';
let validator = new SwaggerValidator.Handler('https://raw.githubusercontent.com/YStrauch/swagger-object-validator/master/test/specs/yaml/swagger.yaml');
// Fetch the unnamed model from i.e. a path
let schema = json.paths['/person'].post.parameters[0].schema
validator.validateModel({'name': 'Homer'}, schema).then(result => {
console.log(result.humanReadable());
});
You can hand in a configuration object. Before diving in each of them let's look over it quickly:
interface IValidatorConfig {
// for relative $refs:
partialsDir?: string;
// allow additional properties not defined in the Spec
allowAdditionalProperties?: boolean;
// allow usage of x-nullable for properties, defaults to disallow
allowXNullable?: boolean;
// HTTP and HTTPS are allowed by default
disallowHttp?: boolean;
disallowHttps?: boolean;
// add custom validation rules (sync and async)
customValidation?: (
test: any,
schema: Swagger.Schema,
spec: Swagger.Spec,
trace: Array<ITraceStep>,
otherErrors: Array<ICustomValidationError>,
resolve?: (validationErrors: ICustomValidationError[]) => void,
reject?: (reason: string) => void
) => ICustomValidationError[] | void | undefined;
// you can ignore certain errors
ignoreError?: ( // cb to ignore errors
error: IValidationError,
value: any,
schema: Swagger.Schema,
spec: Swagger.Spec
) => boolean;
}
$ref gives you the possibility to split your specification across different files (or even servers). There are multiple types of $refs:
// internal $ref
$ref: '#/definitions/Pet'
// $ref to the interwebs
$ref: 'https://raw.githubusercontent.com/OAI/OpenAPI-Specification/master/examples/v2.0/yaml/petstore.yaml#/definitions/Pet'
// relative $ref
$ref: './pet.json'
The last kind of $refs, the relative ones, uses process.cwd() to determine the absolute path. If you want those $refs to resolve to another directory, you may specify a base path:
let config = {
partialsDir: '/some/path/'
}
let validator = new SwaggerValidator.Handler('spec.yml', config);
As described in the paragraph above Swagger allows references to the interwebs. If you do not want this you can set a corresponding option:
let config = {
disallowHttp: true,
disallowHttps: true
};
let validator = new Handler('http://raw.githubusercontent.com/OAI/OpenAPI-Specification/master/examples/v2.0/yaml/petstore.yaml', config);
By default, every property that is not specified within the swagger definition adds an ADDITIONAL_PROPERTY error to the stack. If you want to allow certain properties you can do that with the ignoreError function (see "Ignoring Errors" below), or you can allow all additional properties at once with a config entry:
let config: IValidatorConfig = {
allowAdditionalProperties: true
};
x-nullable
propertiesA common extension for Swagger 2 is x-nullable
, based on
nullable
from the OpenAPI 3 spec.
This allows a property to be returned as null
instead of the intended type.
By enabling this configuration, the x-nullable
property is recognized and respected
when validating types.
let config: IValidatorConfig = {
allowXNullable: true
};
You may want to ignore certain errors. Let's assume you need some magic to allow a certain string to be valid on a field that should normally be a number (because of reasons):
let config: IValidatorConfig = {
ignoreError: (error, value, schema, spec) => {
// ignore type mismatches on Pet/id when a certain value occures
return error.errorType === ValidationErrorType.TYPE_MISMATCH
&& error.trace[0].stepName === 'Pet'
&& error.trace[1].stepName === 'id'
&& schema.type === 'integer'
&& value === 'magicKeyThatWillBeAllowed';
}
};
let validator = new Handler('https://raw.githubusercontent.com/OAI/OpenAPI-Specification/master/examples/v2.0/yaml/petstore.yaml', config);
let pet = {
id: 'magicKeyThatWillBeAllowed',
name: 'Doge'
};
validator.validateModel(pet, 'Pet', (err, result) => {
console.log(result.humanReadable()); // valid
You may need to implement custom rules, for example you may want to ensure that a pet name always starts with an uppercase letter. (For that specific use case you should probably just use a "pattern" regex swagger rule but for the sake of documentation we forget about this)
let config: IValidatorConfig = {
customValidation: (test, schema, spec, trace, otherErrors) => {
// only validate Pet/name
if (trace.length !== 2
|| trace[trace.length - 2].stepName !== 'Pet'
|| trace[trace.length - 1].stepName !== 'name') {
// the property is not a pet name, so do not return any validation errors
return [];
}
// no need to ensure that petName is actually a string, because this will be already done for you
let firstChar = test.substr(0,1);
if (firstChar !== firstChar.toUpperCase()) {
return [
// You may throw a custom error, where content is any type
{
errorType: ValidationErrorType.CUSTOM, // or 6 for JS
trace: trace,
content: 'Name must start with an uppercase letter'
}
];
}
// pet starts with an uppercase, so do not return validation errors
return [];
}
};
You may either return an array of errors, or if you need to do asynchronously magic, you can use the resolve callback (or the reject callback to throw a critical error). Make sure not to mix return and resolve though.
customValidation: (test, schema, spec, trace, otherErrors, resolve, reject) => {
setTimeout(() => resolve([]), 100);
}
Important:
Swagger-Object-Validator can work with two types of polymorphism.
Medium is a superclass of Image and Video. The discriminator type can either be "Image" or "Video". This is the way swagger intended polymorphism.
Medium:
required:
- type
properties:
type:
type: string
Image:
required:
- source
allOf:
- $ref: '#/definitions/Medium'
properties:
source:
type: string
Video:
required:
- length
allOf:
- $ref: '#/definitions/Medium'
properties:
length:
type: integer
let medium = {
type: 'Image',
source: 'source' // property only exists within image
};
// the plot in polymorphism is that you validate
// your model against the abstract Medium class,
// not against the concrete Image class
validator.validateModel(medium, 'Medium', (err, result) => {
//...
});
Instead of using the Name of the object, enum polymorphism uses enums to differentiate:
Section:
required:
- sectionType
discriminator: sectionType
properties:
sectionType:
type: string
enum:
- TEXT_SECTION
- IMAGE_SECTION
TextSection:
allOf:
- $ref: '#/definitions/Section'
properties:
sectionType:
type: string
enum:
- TEXT_SECTION
textContent:
type: string
ImageSection:
required:
- imageSource
allOf:
- $ref: '#/definitions/Section'
properties:
sectionType:
type: string
enum:
- IMAGE_SECTION
imageSource:
type: string
let section = {
sectionType: 'TEXT_SECTION',
textContent: 'Custom polymorphed text section'
};
The stack trace will tell you if polymorphism was detected. So if a Medium may be an Image, and Polymorphism was detected, a trace may look like this:
{
"errors": [
{
"errorType": 0,
"trace": [
{
"stepName": "Medium",
"concreteModel": "Image" // here
},
{
"stepName": "source"
}
]
}
]
}
HumanReadable:
Missing required property:
- At Medium<Image>/source
validateModel()
without a full-fledged spec (i.e. just the model definition), the first error step did previously not have a name. This was changed to the dedicated name 'root'.Wanna help? Sure. Please make sure to use an IDE with TSLint and EditorConfig installed. Always work test-driven, for each feature or bug you fix there needs to be a test.
npm run-script watch:test # Build the application, run tests and watch the FS
npm run-script debug # Very useful to trace bugs. You need a remote debugging software, I use VSCode debugger
FAQs
Validate your objects against a swagger spec and receive in-depth error traces
The npm package swagger-object-validator receives a total of 3,124 weekly downloads. As such, swagger-object-validator popularity was classified as popular.
We found that swagger-object-validator 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.
Research
/Security News
A flawed sandbox in @nestjs/devtools-integration lets attackers run code on your machine via CSRF, leading to full Remote Code Execution (RCE).
Product
Customize license detection with Socket’s new license overlays: gain control, reduce noise, and handle edge cases with precision.
Product
Socket now supports Rust and Cargo, offering package search for all users and experimental SBOM generation for enterprise projects.