oneOf and anyOf validations violate additionalProperties constraints due to over application of additionalProperties

Issue #336 resolved
Ben Yarger created an issue

I believe I have identified an issue that causes anyOf and oneOf cases to fail due to the application of additonalProperties: false in the schema checks. In the below description I focus on the oneOf case, however, the findings apply the same to anyOf. I am fully aware of the documented limitations of additionalProperties checking with composite models, however, I believe this specific issues falls outside that concern.

OpenAPI Schema – for one of testing:

openapi: 3.0.0
info:
  title: Enum Validation
  description: >-
  version: 1.0.0
servers:
  - url: 'http://test.com'
    description: stage
paths:
  /test:
    get:
      summary: placeholder summary
      responses:
        '200':
          description: Success
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/test'
components:
  schemas:
    test:
      oneOf:
        - $ref: '#/components/schemas/typeA'
        - $ref: '#/components/schemas/typeB'
    typeA:
      type: object
      properties:
        typeAValueA:
          type: string
    typeB:
      type: object
      properties:
        typeBValueB:
          type: number

Test response – we would expect this to pass when evaluated against the above schema:

{
  "typeAValueA": "good"

Configuration of the swagger validator:

ParseOptions parseOptions = new ParseOptions();
parseOptions.setResolveFully(true);
parseOptions.setResolveCombinators(true);

OpenApiInteractionValidator validator = OpenApiInteractionValidator.createFor(schemaResourcePath).withParseOptions(parseOptions).build();
ValidationReport validationReport = validator.validateResponse(schemaPath, requestMethod, response);    

Documentation of the issue:

When the com.atlassian.oai.validator.schema.SchemaValidator converts the yaml to JSON schema at line 145 the resulting jsonSchema.schema.baseNode shown below has additionalProperties: false applied to non JSON object types (shown below). This doesn’t make sense for any type other than a JSON object where properties are defined.

When the validation runs on this JSON schema, we end up with a the com.github.fge.jsonschema.keyword.validator.common.AdditionalPropertiesValidator getting initialized, along with the oneOf validator, with an object without any properties or patternProperties. This would make sense given the schema root is a one of that also has additionalProperties: false, however, the additonalProperties has no relevance at this level in the parsed schema.

The subsequence call to AdditionalPropertiesValidator with the test response (above) causes a validation failure, because, the fields contain ‘typeValueA’ but there aren’t any properties in the additionalProperties validator so typeValueA isn’t removed and thus fails the validation check. I suspect if the conversion from OpenAPI yaml to JSON schema didn’t over apply additionalProperties then we wouldn’t have this issue.

{
  "exampleSetFlag": false,
  "oneOf": [
    {
      "type": "object",
      "properties": {
        "typeAValueA": {
          "type": "string",
          "exampleSetFlag": false,
          "additionalProperties": false
        }
      },
      "exampleSetFlag": false,
      "additionalProperties": false
    },
    {
      "type": "object",
      "properties": {
        "typeBValueB": {
          "type": "number",
          "exampleSetFlag": false,
          "additionalProperties": false
        }
      },
      "exampleSetFlag": false,
      "additionalProperties": false
    }
  ],
  "components": {
    "schemas": {
      "test": {
        "exampleSetFlag": false,
        "oneOf": [
          {
            "type": "object",
            "properties": {
              "typeAValueA": {
                "type": "string",
                "exampleSetFlag": false,
                "additionalProperties": false
              }
            },
            "exampleSetFlag": false,
            "additionalProperties": false
          },
          {
            "type": "object",
            "properties": {
              "typeBValueB": {
                "type": "number",
                "exampleSetFlag": false,
                "additionalProperties": false
              }
            },
            "exampleSetFlag": false,
            "additionalProperties": false
          }
        ],
        "additionalProperties": false
      },
      "typeA": {
        "type": "object",
        "properties": {
          "typeAValueA": {
            "type": "string",
            "exampleSetFlag": false,
            "additionalProperties": false
          }
        },
        "exampleSetFlag": false,
        "additionalProperties": false
      },
      "typeB": {
        "type": "object",
        "properties": {
          "typeBValueB": {
            "type": "number",
            "exampleSetFlag": false,
            "additionalProperties": false
          }
        },
        "exampleSetFlag": false,
        "additionalProperties": false
      }
    }
  },
  "$schema": "https://openapis.org/specification/versions/2.0#",
  "additionalProperties": false
}

Proposed Solution:

Updating the AdditionalPropertiesInjectionTransformer.java to apply additionalProperties: false only when properties are defined for the current model will ensure additionalProperties is only applied where relevant. This will eliminate the additionalProperties validation failures due to additionalProperties checks being applied to the wrong models when validating.

Comments (8)

  1. Ben Yarger reporter

    PR submitted to apply additionalProperties only when properties are present. Also corrected 'object' type field assignment for non applicable cases.

  2. James Navin

    Thanks for raising the PR Ben. I have returned from leave this week - will take a look at it by end of week.

    Cheers.

  3. Full Name

    This still won’t work if the allOf has properties:

        DatiRiferimentoMixed:
          type: object
          allOf:
            - $ref: '#/components/schemas/Dati'
            - type: object      
              required:
                - allegati
              properties:
                 allegati:
                   type: array
                   items:
                      $ref: '#/components/schemas/AllegatoRiferimentoMixed' 
    

    [ERROR][REQUEST][POST http://petstore.swagger.io/api/documenti/mixed/send @body] Instance failed to match all required schemas (matched only 0 out of 2)    
    * /allOf/0: Object instance has properties which are not allowed by the schema: ["allegati"]
    * /allOf/1: Object instance has properties which are not allowed by the schema: ["destinatario","mittente","procedimento"]
    

    The validation of the first allOf element doesn’t pass because of the additionalProperties injected by the trasformer.

  4. Log in to comment