openapi-enforcer
Advanced tools
Comparing version 1.12.5 to 1.12.6
@@ -7,2 +7,18 @@ # Change Log | ||
## 1.12.6 | ||
### Fixed | ||
- **ReadOnly and WriteOnly** | ||
The library was incorrectly not accounting for readOnly and writeOnly when using schema validation. This functionality has been added. | ||
## 1.12.5 | ||
### Changed | ||
- **Fixed Vulnerability** | ||
Updated Axios library to fix critical vulnerability. | ||
## 1.12.4 | ||
@@ -9,0 +25,0 @@ |
@@ -277,3 +277,3 @@ --- | ||
`Schema.prototype.validate ( value ) : EnforcerException | undefined` | ||
`Schema.prototype.validate ( value [, options ] ) : EnforcerException | undefined` | ||
@@ -284,4 +284,13 @@ Validate a deserialized value against the schema. | ||
- *value* - The deserialized value to validate | ||
| Parameter | Description | Type | Default | | ||
| --------- | ----------- | ---- | ------- | | ||
| **value** | The deserialized value to validate. | `any` | | | ||
| options | Configuration options. See below. | `object` | | | ||
**Options Parameter** | ||
| Property | Description | Type | Default | | ||
| --------- | ----------- | ---- | ------- | | ||
| readWriteMode | If this value is set to `read` then any properties in the value that are listed as `writeOnly: true` will produce an error. If the value is set to `"write"` then any properties in the value listed as `readOnly: true` will produce an error. | `string` | `undefined` | | ||
**Returns:** An [EnforcerException](../enforcer-exception.md) object an the value is not valid, otherwise `undefined`. | ||
@@ -288,0 +297,0 @@ |
@@ -0,0 +0,0 @@ ;(function () { |
{ | ||
"name": "openapi-enforcer", | ||
"version": "1.12.5", | ||
"version": "1.12.6", | ||
"description": "Library for validating, parsing, and formatting data against open api schemas.", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
@@ -207,3 +207,3 @@ /** | ||
: parameter.parse(input[key]); | ||
deserializeAndValidate(child.at(key), parameter.schema, data, v => output[key] = v); | ||
deserializeAndValidate(child.at(key), parameter.schema, data, '', v => output[key] = v); | ||
@@ -217,3 +217,3 @@ } else if (parameter.in === 'query' && parameter.style === 'form' && parameter.explode && type === 'object') { | ||
} else { | ||
deserializeAndValidate(child.at(key), parameter.schema, data, value => { | ||
deserializeAndValidate(child.at(key), parameter.schema, data, '', value => { | ||
Object.keys(value).forEach(key => util.arrayRemoveItem(unknownParameters, key)); | ||
@@ -231,3 +231,3 @@ output[key] = value; | ||
} else { | ||
deserializeAndValidate(child.at(key), parameter.schema, data, value => { | ||
deserializeAndValidate(child.at(key), parameter.schema, data, '', value => { | ||
Object.keys(value).forEach(k => util.arrayRemoveItem(unknownParameters, key + '[' + k + ']')); | ||
@@ -277,3 +277,3 @@ output[key] = value; | ||
value = primitiveBodyDeserialization(value, parameter.schema); | ||
deserializeAndValidate(exception.nest('In body'), parameter.schema, { value }, value => { | ||
deserializeAndValidate(exception.nest('In body'), parameter.schema, { value }, 'write',value => { | ||
result.body = Value.extract(value); | ||
@@ -298,3 +298,3 @@ }); | ||
value = primitiveBodyDeserialization(value, media.schema); | ||
deserializeAndValidate(child.nest('For Content-Type ' + mediaType), media.schema, { value }, value => { | ||
deserializeAndValidate(child.nest('For Content-Type ' + mediaType), media.schema, { value }, 'write', value => { | ||
result.body = Value.extract(value); | ||
@@ -651,5 +651,5 @@ passed = true; | ||
function deserializeAndValidate(exception, schema, data, success) { | ||
function deserializeAndValidate(exception, schema, data, readWriteMode, success) { | ||
if (!data.error) data = schema.deserialize(data.value); | ||
if (!data.error) data.error = schema.validate(data.value); | ||
if (!data.error) data.error = schema.validate(data.value, { readWriteMode }); | ||
if (data.error) { | ||
@@ -656,0 +656,0 @@ if (exception) exception.push(data.error); |
@@ -208,7 +208,10 @@ /** | ||
* @param {*} value | ||
* @param {object} [options] | ||
* @param {string} [options.readWriteMode] Can be undefined, "read", or "write" | ||
* @returns {EnforcerException|undefined} | ||
*/ | ||
validate: function(value) { | ||
validate: function(value, options) { | ||
const exception = Exception('Invalid value'); | ||
runValidate(exception, new Map(), this, value, {}); | ||
if (!options) options = {} | ||
runValidate(exception, new Map(), this, value, options); | ||
if (exception.hasException) return exception; | ||
@@ -215,0 +218,0 @@ } |
@@ -146,3 +146,13 @@ /** | ||
const properties = schema.properties || {}; | ||
const required = schema.required ? schema.required.concat() : []; | ||
const readWriteMode = options.readWriteMode; | ||
const readWriteOnly = []; | ||
const required = schema.required ? | ||
schema.required.filter(name => { | ||
if (!options.readWriteMode) return true | ||
const prop = properties[name] | ||
if (options.readWriteMode === 'write' && !prop.readOnly) return true | ||
if (options.readWriteMode === 'read' && !prop.writeOnly) return true | ||
return false | ||
}) | ||
: []; | ||
const keys = Object.keys(value); | ||
@@ -152,6 +162,10 @@ | ||
keys.forEach(key => { | ||
// remove item for required remaining array | ||
const index = required.indexOf(key); | ||
if (index !== -1) required.splice(index, 1); | ||
if (properties.hasOwnProperty(key)) { | ||
runValidate(exception.at(key), map, properties[key], value[key], options); | ||
const prop = properties[key] | ||
if ((readWriteMode === 'write' && prop.readOnly) || (readWriteMode === 'read' && prop.writeOnly)) readWriteOnly.push(key) | ||
runValidate(exception.at(key), map, prop, value[key], options); | ||
} else { | ||
@@ -161,2 +175,4 @@ if (schema.additionalProperties === false) { | ||
} else if (typeof schema.additionalProperties === 'object') { | ||
const prop = schema.additionalProperties | ||
if ((readWriteMode === 'write' && prop.readOnly) || (readWriteMode === 'read' && prop.writeOnly)) readWriteOnly.push(key) | ||
runValidate(exception.at(key), map, schema.additionalProperties, value[key], options); | ||
@@ -172,2 +188,11 @@ } | ||
// validate that we only have readable or writable properties | ||
if (readWriteOnly.length > 0) { | ||
if (readWriteMode === 'write') { | ||
exception.message('Cannot read from write only properties: ' + readWriteOnly.join(', ')); | ||
} else if (readWriteMode === 'read') { | ||
exception.message('Cannot write to read only properties: ' + readWriteOnly.join(', ')); | ||
} | ||
} | ||
// validate number of properties | ||
@@ -174,0 +199,0 @@ maxMin(exception, schema, 'object property count', 'maxProperties', 'minProperties', false, keys.length, schema.maxProperties, schema.minProperties); |
@@ -132,4 +132,57 @@ /** | ||
describe('required readOnly properties', () => { | ||
it('does not require readOnly properties when making a request', async () => { | ||
const definition = { | ||
openapi: '3.0.0', | ||
info: { title: '', version: '' }, | ||
paths: { | ||
'/': { | ||
post: { | ||
requestBody: { | ||
content: { | ||
'application/json': { | ||
schema: { | ||
$ref: '#/components/schemas/Pet' | ||
} | ||
} | ||
} | ||
}, | ||
responses: { | ||
200: { description: '' } | ||
} | ||
} | ||
} | ||
}, | ||
components: { | ||
schemas: { | ||
Pet: { | ||
type: 'object', | ||
required: ['id', 'name'], | ||
properties: { | ||
id: { | ||
type: 'string', | ||
readOnly: true | ||
}, | ||
name: { | ||
type: 'string' | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
const openapi = await Enforcer(definition) | ||
const result = openapi.request({ | ||
method: 'post', | ||
path: '/', | ||
body: { name: 'Bob' } | ||
}) | ||
console.log(result.error) | ||
expect(result.error).to.be.undefined | ||
}) | ||
}) | ||
}) | ||
}); |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
1495485
20288