[REQ-E003] - Removing properties from an object with additionalProperties set to False used as request parameter¶
Rationale¶
If the object is defined with additionalProperties set to False then the object will not allow presence of properties not defined on the properties section of the object definition. Removing a definition of an existing property makes objects sent from a client, that is using “old” Swagger specs, to the server be considered invalid by the backend.
Mitigation¶
A possible mitigation could be to not remove the property from the object schema, mark it as deprecated (mostly for documentation purposes) and make sure that your business logic fills the field with a placeholder.
Example¶
Old Swagger Specs¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | swagger: '2.0'
info:
title: Minimal Case of REQ-E003 Rule
version: '1.0'
paths:
/endpoint:
post:
parameters:
- in: body
name: body
required: true
schema:
additionalProperties: false
properties:
property_1:
type: string
property_2:
type: string
type: object
responses:
default:
description: ''
|
New Swagger Specs¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | swagger: '2.0'
info:
title: Minimal Case of REQ-E003 Rule
version: '1.0'
paths:
/endpoint:
post:
parameters:
- in: body
name: body
required: true
schema:
additionalProperties: false
properties:
property_1:
type: string
type: object
responses:
default:
description: ''
|
Backward Incompatibility¶
The following snippet triggers the incompatibility error.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | # -*- coding: utf-8 -*-
from __future__ import absolute_import
from __future__ import print_function
from __future__ import unicode_literals
from os.path import abspath
from bravado.client import SwaggerClient
from jsonschema import ValidationError
from six.moves.urllib.parse import urljoin
from six.moves.urllib.request import pathname2url
old_client = SwaggerClient.from_url(
spec_url=urljoin('file:', pathname2url(abspath('old.yaml'))),
)
new_client = SwaggerClient.from_url(
spec_url=urljoin('file:', pathname2url(abspath('new.yaml'))),
)
object_to_send = {'property_1': 'v1', 'property_2': 'v2'}
print('Calling the post endpoint with the old client: Succeeded')
old_client.endpoint.post_endpoint(body=object_to_send)
print('Calling the post endpoint with the old client: Failed')
try:
new_client.endpoint.post_endpoint(body=object_to_send)
raise RuntimeError('An error was expected')
except ValidationError:
pass
|
NOTE: The code is taking advantage of bravado