Add type validation in params, queries headers

Add type validation in params, queries headers. Extract common functionality.
This commit is contained in:
Luis Tejeda
2022-02-28 16:23:22 -06:00
parent 50631020d2
commit d91284547c
6 changed files with 385 additions and 205 deletions

View File

@@ -1,6 +1,6 @@
const { formatDataPath,
formatSchemaPathFromAJVErrorToConvertToDataPath,
typesMap } = require('../common/schemaUtilsCommon');
isTypeValue } = require('../common/schemaUtilsCommon');
var _ = require('lodash');
const IGNORED_KEYWORDS = ['propertyNames', 'const', 'additionalItems', 'dependencies'],
@@ -8,17 +8,6 @@ const IGNORED_KEYWORDS = ['propertyNames', 'const', 'additionalItems', 'dependen
{ validateSchemaAJVDraft04 } = require('./ajvValidatorDraft04'),
specialDraft = 'http://json-schema.org/draft-04/schema#';
/**
* Checks if value is postman variable or not
*
* @param {string} type - type to look for
* @param {string} format - format from schema
* @returns {Boolean} postman variable or not
*/
function getDefaultFromTypeAndFormat(type, format) {
return typesMap[type][format];
}
/**
* Checks if value is postman variable or not
*
@@ -30,104 +19,6 @@ function isPmVariable (value) {
return _.isString(value) && _.startsWith(value, '{{') && _.endsWith(value, '}}');
}
/**
* Checks if value is the representation of its type like:
* "<integer>"
*
* @param {string} value - Value to check for
* @param {string} type - The type in the schemna
* @returns {Boolean} the value is the representation of its type
*/
function compareType(value, type) {
return value === '<' + type + '>';
}
/**
* Checks if value is the representation of its type like:
* "<integer>"
* Works in array types
* @param {string} value - Value to check for
* @param {*} types - The types in the schemna
* @returns {Boolean} the value is the representation of its type
*/
function isTypeValueArrayCheck(value, types) {
return types.find((type) => {
return compareType(value, type);
}) !== undefined;
}
/**
* Checks if value is the representation of its type like:
* "<integer>"
* Works in array types
* @param {string} value - Value to check for
* @param {*} types - The types in the schemna
* @returns {Boolean} the value is the representation of its type
*/
function checkValueOnlyTypes(value, types) {
return Array.isArray(types) ? isTypeValueArrayCheck(value, types) : compareType(value, types);
}
/**
* Checks if value is the representation of its type like:
* "<integer>"
* Works in array types
* @param {string} value - Value to check for
* @param {*} types - The types in the schema
* @param {*} format - format from the schema
* @returns {Boolean} the value is the representation of its type
*/
function checkValueTypesAndFormat(value, types, format) {
let typesNotInMapp = [],
typesArray = Array.isArray(types) ? types : [types],
found = typesArray.find((type) => {
let defaultValue;
if (typesMap.hasOwnProperty(type)) {
defaultValue = getDefaultFromTypeAndFormat(type, format);
// in case the format is a custom format (email, hostname etc.)
// https://swagger.io/docs/specification/data-models/data-types/#string
if (!defaultValue && format) {
defaultValue = '<' + format + '>';
}
}
else {
typesNotInMapp.push(type);
}
return defaultValue === value;
});
if (found) {
return true;
}
found = typesNotInMapp.find((type) => {
let defaultValue;
defaultValue = '<' + type + (format ? ('-' + format) : '') + '>';
return defaultValue === value;
});
return found !== undefined;
}
/**
* Checks if value is the representation of its type like:
* "<integer>"
* Works in array types
* @param {string} value - Value to check for
* @param {*} schema - The schema portion used in validation
* @returns {Boolean} the value is the representation of its type
*/
function isTypeValue(value, schema) {
if (schema.hasOwnProperty('type') && !schema.hasOwnProperty('format')) {
return checkValueOnlyTypes(value, schema.type);
}
if (schema.hasOwnProperty('type') && schema.hasOwnProperty('format')) {
return checkValueTypesAndFormat(value, schema.type, schema.format);
}
}
/**
* returns the local $schema value
*

View File

@@ -3,6 +3,7 @@
*/
const parse = require('../parse.js'),
_ = require('lodash'),
typesMap = {
integer: {
int32: '<integer>',
@@ -20,6 +21,16 @@ const parse = require('../parse.js'),
password: '<password>'
},
boolean: '<boolean>'
},
/* eslint-disable arrow-body-style */
schemaTypeToJsValidator = {
'string': (d) => typeof d === 'string',
'number': (d) => !isNaN(d),
'integer': (d) => !isNaN(d) && Number.isInteger(Number(d)),
'boolean': (d) => _.isBoolean(d) || d === 'true' || d === 'false',
'array': (d) => Array.isArray(d),
'object': (d) => typeof d === 'object' && !Array.isArray(d)
};
/**
@@ -44,6 +55,152 @@ function removeTypeFromLastPosition(schemaPath) {
return splittedDataPath.join('/');
}
/**
* Checks if value is the representation of its type like:
* "<integer>"
*
* @param {string} value - Value to check for
* @param {string} type - The type in the schemna
* @returns {Boolean} the value is the representation of its type
*/
function compareType(value, type) {
return value === '<' + type + '>';
}
/**
* Checks if value is the representation of its type like:
* "<integer>"
* Works in array types
* @param {string} value - Value to check for
* @param {*} types - The types in the schemna
* @returns {Boolean} the value is the representation of its type
*/
function isTypeValueArrayCheck(value, types) {
return types.find((type) => {
return compareType(value, type);
}) !== undefined;
}
/**
* Checks if value is the representation of its type like:
* "<integer>"
* Works in array types
* @param {string} value - Value to check for
* @param {*} types - The types in the schemna
* @returns {Boolean} the value is the representation of its type
*/
function checkValueOnlyTypes(value, types) {
return Array.isArray(types) ? isTypeValueArrayCheck(value, types) : compareType(value, types);
}
/**
* Checks if value is postman variable or not
*
* @param {string} type - type to look for
* @param {string} format - format from schema
* @returns {Boolean} postman variable or not
*/
function getDefaultFromTypeAndFormat(type, format) {
return typesMap[type][format];
}
/**
* Checks if value is the representation of its type like:
* "<integer>"
* Works in array types
* @param {string} value - Value to check for
* @param {*} types - The types in the schema
* @param {*} format - format from the schema
* @returns {Boolean} the value is the representation of its type
*/
function checkValueTypesAndFormat(value, types, format) {
let typesNotInMapp = [],
typesArray = Array.isArray(types) ? types : [types],
found = typesArray.find((type) => {
let defaultValue;
if (typesMap.hasOwnProperty(type)) {
defaultValue = getDefaultFromTypeAndFormat(type, format);
// in case the format is a custom format (email, hostname etc.)
// https://swagger.io/docs/specification/data-models/data-types/#string
if (!defaultValue && format) {
defaultValue = '<' + format + '>';
}
}
else {
typesNotInMapp.push(type);
}
return defaultValue === value;
});
if (found) {
return true;
}
found = typesNotInMapp.find((type) => {
let defaultValue;
defaultValue = '<' + type + (format ? ('-' + format) : '') + '>';
return defaultValue === value;
});
return found !== undefined;
}
/**
* Checks if value is equal to the defined default
* Works in array types
* @param {string} value - Value to check for
* @param {*} definedDefault - The defined default value of the schema
* @returns {Boolean} wheter value is equal to the defined or not
*/
function checkValueEqualsDefault(value, definedDefault) {
return value === definedDefault;
}
/**
* Checks if value is the representation of its type like:
* "<integer>"
* Works in array types
* @param {string} value - Value to check for
* @param {*} schema - The schema portion used in validation
* @returns {Boolean} the value is the representation of its type
*/
function isTypeValue(value, schema) {
if (schema.hasOwnProperty('type') && schema.hasOwnProperty('default')) {
const isDefault = checkValueEqualsDefault(value, schema.default);
if (isDefault) {
return true;
}
}
if (schema.hasOwnProperty('type') && !schema.hasOwnProperty('format')) {
return checkValueOnlyTypes(value, schema.type);
}
if (schema.hasOwnProperty('type') && schema.hasOwnProperty('format')) {
return checkValueTypesAndFormat(value, schema.type, schema.format);
}
}
/**
* Checks if value is correcta according to schema
* If the value should be numeric, it tries to convert and then validate
* also validates if the value is a correct representation in the form of
* <long> or <integer> etc for integers format 32 or format 64
* @param {string} value - Value to check for
* @param {*} schema - The schema portion used in validation
* @returns {Boolean} the value is the representation of its type
*/
function checkIsCorrectType(value, schema) {
if (schema.hasOwnProperty('type') &&
typeof schemaTypeToJsValidator[schema.type] === 'function') {
const isCorrectType = schemaTypeToJsValidator[schema.type](value);
if (isCorrectType) {
return true;
}
}
return isTypeValue(value, schema);
}
module.exports = {
/**
@@ -158,5 +315,31 @@ module.exports = {
return removeTypeFromLastPosition(removeSharpAndSlashFromFirstPosition(schemaPath));
},
typesMap
typesMap,
/**
* Checks if value is the representation of its type like:
* "<integer>"
* Works in array types
* @param {string} value - Value to check for
* @param {*} schema - The schema portion used in validation
* @returns {Boolean} the value is the representation of its type
*/
isTypeValue,
/**
* Checks if value is correcta according to schema
* If the value should be numeric, it tries to convert and then validate
* also validates if the value is a correct representation in the form of
* <long> or <integer> etc for integers format 32 or format 64
* @param {string} value - Value to check for
* @param {*} schema - The schema portion used in validation
* @returns {Boolean} the value is the representation of its type
*/
checkIsCorrectType,
isKnownType: function(schema) {
return typeof schemaTypeToJsValidator[schema.type] === 'function';
}
};

View File

@@ -3,7 +3,7 @@
* utils.js contains other util functions
*/
const { formatDataPath } = require('./common/schemaUtilsCommon.js'),
const { formatDataPath, checkIsCorrectType, isKnownType } = require('./common/schemaUtilsCommon.js'),
{ getConcreteSchemaUtils } = require('./common/versionUtils.js'),
async = require('async'),
sdk = require('postman-collection'),
@@ -85,15 +85,6 @@ const { formatDataPath } = require('./common/schemaUtilsCommon.js'),
'authorization'
],
/* eslint-disable arrow-body-style */
schemaTypeToJsValidator = {
'string': (d) => typeof d === 'string',
'number': (d) => !isNaN(d),
'integer': (d) => !isNaN(d) && Number.isInteger(Number(d)),
'boolean': (d) => _.isBoolean(d) || d === 'true' || d === 'false',
'array': (d) => Array.isArray(d),
'object': (d) => typeof d === 'object' && !Array.isArray(d)
},
crypto = require('crypto'),
DEFAULT_SCHEMA_UTILS = require('./30XUtils/schemaUtils30X');
/* eslint-enable */
@@ -3088,7 +3079,7 @@ module.exports = {
}
// When processing a reference, schema.type could also be undefined
else if (schema && schema.type) {
if (typeof schemaTypeToJsValidator[schema.type] === 'function') {
if (isKnownType(schema)) {
let isCorrectType;
// Treat unresolved postman collection/environment variable as correct type
@@ -3096,7 +3087,7 @@ module.exports = {
isCorrectType = true;
}
else {
isCorrectType = schemaTypeToJsValidator[schema.type](valueToUse);
isCorrectType = checkIsCorrectType(valueToUse, schema);
}
if (!isCorrectType) {

View File

@@ -0,0 +1,97 @@
openapi: "3.0.0"
info:
version: 1.0.0
title: Swagger Petstore
license:
name: MIT
servers:
- url: http://petstore.swagger.io/v1
paths:
/pets:
get:
summary: List all pets
operationId: listPets
tags:
- pets
parameters:
- name: limit
in: query
description: How many items to return at one time (max 100)
required: false
schema:
type: integer
format: int32
- name: header-1
in: header
required: false
schema:
type: integer
format: int64
responses:
'200':
description: A paged array of pets
headers:
x-next:
description: A link to the next page of responses
schema:
type: integer
format: int64
content:
application/json:
schema:
$ref: "#/components/schemas/Pets"
/pets2/{pathVar}:
get:
summary: List all pets
operationId: listPets
tags:
- pets
parameters:
- name: pathVar
in: path
required: false
schema:
type: integer
format: int32
responses:
'200':
description: A paged array of pets
headers:
x-next:
description: A link to the next page of responses
schema:
type: string
content:
application/json:
schema:
$ref: "#/components/schemas/Pets"
components:
schemas:
Pet:
type: object
required:
- id
- name
properties:
id:
type: integer
format: int64
name:
type: string
tag:
type: string
Pets:
type: array
items:
$ref: "#/components/schemas/Pet"
Error:
type: object
required:
- code
- message
properties:
code:
type: integer
format: int32
message:
type: string

View File

@@ -1,5 +1,7 @@
const schemaUtilsCommon = require('../../lib/common/schemaUtilsCommon'),
{ formatDataPath, formatSchemaPathFromAJVErrorToConvertToDataPath } = require('../../lib/common/schemaUtilsCommon'),
{ formatDataPath,
formatSchemaPathFromAJVErrorToConvertToDataPath,
isTypeValue } = require('../../lib/common/schemaUtilsCommon'),
expect = require('chai').expect;
describe('formatData method', function() {
@@ -165,3 +167,100 @@ describe('formatSchemaPathFromAJVErrorToConvertToDataPath method', function () {
expect(result).to.equal('properties/automatic/items/properties/configs/items');
});
});
describe('isTypeValue method', function () {
it('should return true when value is <integer> and type is integer', function () {
const result = isTypeValue('<integer>', {
type: [
'integer'
]
});
expect(result).to.be.true;
});
it('should return true when input is <long> type integer and format int64', function () {
const result = isTypeValue('<long>', {
format: 'int64',
type: ['integer']
});
expect(result).to.be.true;
});
it('should return true when value is <uuid> type is string format is uuid', function () {
const result = isTypeValue('<uuid>', {
format: 'uuid',
type: ['string']
});
expect(result).to.be.true;
});
it('should return true when value is <otherType> type is otherType and there is not format', function () {
const result = isTypeValue('<otherType>', {
type: ['otherType']
});
expect(result).to.be.true;
});
it('should return true value is <otherType-otherFormat> type is otherType and format is otherFormat', function () {
const result = isTypeValue('<otherType-otherFormat>', {
format: 'otherFormat',
type: ['otherType']
});
expect(result).to.be.true;
});
it('should return false when value is <integer> and type is boolean', function () {
const result = isTypeValue('<integer>', {
type: ['boolean']
});
expect(result).to.be.false;
});
it('should return true when value is <string> and type is string', function () {
const result = isTypeValue('<string>', {
type: ['string']
});
expect(result).to.be.true;
});
it('should return true when value is <integer> and type is ["boolean", "integer"]', function () {
const result = isTypeValue('<integer>', {
type: ['boolean', 'integer']
});
expect(result).to.be.true;
});
it('should return true when value is <integer> and type is integer not array', function () {
const result = isTypeValue('<integer>', {
type: 'integer'
});
expect(result).to.be.true;
});
it('should return true when value is <integer> and type is integer not array format int64', function () {
const result = isTypeValue('<long>', {
format: 'int64',
type: 'integer'
});
expect(result).to.be.true;
});
it('should return true when value is <long> and type is integer, format not present default is <long>', function () {
const result = isTypeValue('<long>', {
default: '<long>',
type: 'integer'
});
expect(result).to.be.true;
});
it('should return false when value is <long> and type is integer,' +
' there is no format default is <nlong>', function () {
const result = isTypeValue('<nlong>', {
default: '<long>',
type: 'integer'
});
expect(result).to.be.false;
});
});

View File

@@ -1,7 +1,6 @@
const { getLocalDraft,
getAjvValidator,
validateSchema,
isTypeValue,
getDraftToUse } = require('../../lib/ajValidation/ajvValidation'),
{ validateSchemaAJVDraft04 } = require('../../lib/ajValidation/ajvValidatorDraft04'),
expect = require('chai').expect;
@@ -433,83 +432,3 @@ describe('getDraftToUse', function() {
expect(draftToUse).to.equal(undefined);
});
});
describe('isTypeValue method', function () {
it('should return true when value is <integer> and type is integer', function () {
const result = isTypeValue('<integer>', {
type: [
'integer'
]
});
expect(result).to.be.true;
});
it('should return true when input is <long> type integer and format int64', function () {
const result = isTypeValue('<long>', {
format: 'int64',
type: ['integer']
});
expect(result).to.be.true;
});
it('should return true when value is <uuid> type is string format is uuid', function () {
const result = isTypeValue('<uuid>', {
format: 'uuid',
type: ['string']
});
expect(result).to.be.true;
});
it('should return true when value is <otherType> type is otherType and there is not format', function () {
const result = isTypeValue('<otherType>', {
type: ['otherType']
});
expect(result).to.be.true;
});
it('should return true value is <otherType-otherFormat> type is otherType and format is otherFormat', function () {
const result = isTypeValue('<otherType-otherFormat>', {
format: 'otherFormat',
type: ['otherType']
});
expect(result).to.be.true;
});
it('should return false when value is <integer> and type is boolean', function () {
const result = isTypeValue('<integer>', {
type: ['boolean']
});
expect(result).to.be.false;
});
it('should return true when value is <string> and type is string', function () {
const result = isTypeValue('<string>', {
type: ['string']
});
expect(result).to.be.true;
});
it('should return true when value is <integer> and type is ["boolean", "integer"]', function () {
const result = isTypeValue('<integer>', {
type: ['boolean', 'integer']
});
expect(result).to.be.true;
});
it('should return true when value is <integer> and type is integer not array', function () {
const result = isTypeValue('<integer>', {
type: 'integer'
});
expect(result).to.be.true;
});
it('should return true when value is <integer> and type is integer not array format int64', function () {
const result = isTypeValue('<long>', {
format: 'int64',
type: 'integer'
});
expect(result).to.be.true;
});
});