diff --git a/docs/configuration.md b/docs/configuration.md index 37d0d4f9..6489c2d6 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -136,7 +136,7 @@ result = openapi.unmarshal_response(request, response) By default, OpenAPI follows JSON Schema behavior: when an object schema omits `additionalProperties`, extra keys are allowed. -If you want stricter behavior, enable `strict_additional_properties`. In this mode, omitted `additionalProperties` is treated as `false`. +If you want stricter behavior, change `additional_properties_default_policy` to `forbid`. In this mode, omitted `additionalProperties` is treated as `false`. This mode is particularly useful for: - **Preventing data leaks**: Ensuring your API doesn't accidentally expose internal or sensitive fields in responses that aren't explicitly documented. @@ -148,7 +148,7 @@ from openapi_core import Config from openapi_core import OpenAPI config = Config( - strict_additional_properties=True, + additional_properties_default_policy="forbid", ) openapi = OpenAPI.from_file_path('openapi.json', config=config) ``` diff --git a/openapi_core/app.py b/openapi_core/app.py index f58a5682..0a7f7960 100644 --- a/openapi_core/app.py +++ b/openapi_core/app.py @@ -427,7 +427,8 @@ def request_validator(self) -> RequestValidator: spec_validator_cls=self.config.spec_validator_cls, extra_format_validators=self.config.extra_format_validators, extra_media_type_deserializers=self.config.extra_media_type_deserializers, - strict_additional_properties=self.config.strict_additional_properties, + forbid_unspecified_additional_properties=self.config.additional_properties_default_policy + == "forbid", security_provider_factory=self.config.security_provider_factory, ) @@ -446,7 +447,8 @@ def response_validator(self) -> ResponseValidator: spec_validator_cls=self.config.spec_validator_cls, extra_format_validators=self.config.extra_format_validators, extra_media_type_deserializers=self.config.extra_media_type_deserializers, - strict_additional_properties=self.config.strict_additional_properties, + forbid_unspecified_additional_properties=self.config.additional_properties_default_policy + == "forbid", enforce_properties_required=self.config.response_properties_default_policy == "required", ) @@ -466,7 +468,8 @@ def webhook_request_validator(self) -> WebhookRequestValidator: spec_validator_cls=self.config.spec_validator_cls, extra_format_validators=self.config.extra_format_validators, extra_media_type_deserializers=self.config.extra_media_type_deserializers, - strict_additional_properties=self.config.strict_additional_properties, + forbid_unspecified_additional_properties=self.config.additional_properties_default_policy + == "forbid", security_provider_factory=self.config.security_provider_factory, ) @@ -485,7 +488,8 @@ def webhook_response_validator(self) -> WebhookResponseValidator: spec_validator_cls=self.config.spec_validator_cls, extra_format_validators=self.config.extra_format_validators, extra_media_type_deserializers=self.config.extra_media_type_deserializers, - strict_additional_properties=self.config.strict_additional_properties, + forbid_unspecified_additional_properties=self.config.additional_properties_default_policy + == "forbid", enforce_properties_required=self.config.response_properties_default_policy == "required", ) @@ -505,7 +509,8 @@ def request_unmarshaller(self) -> RequestUnmarshaller: spec_validator_cls=self.config.spec_validator_cls, extra_format_validators=self.config.extra_format_validators, extra_media_type_deserializers=self.config.extra_media_type_deserializers, - strict_additional_properties=self.config.strict_additional_properties, + forbid_unspecified_additional_properties=self.config.additional_properties_default_policy + == "forbid", security_provider_factory=self.config.security_provider_factory, schema_unmarshallers_factory=self.config.schema_unmarshallers_factory, extra_format_unmarshallers=self.config.extra_format_unmarshallers, @@ -526,7 +531,8 @@ def response_unmarshaller(self) -> ResponseUnmarshaller: spec_validator_cls=self.config.spec_validator_cls, extra_format_validators=self.config.extra_format_validators, extra_media_type_deserializers=self.config.extra_media_type_deserializers, - strict_additional_properties=self.config.strict_additional_properties, + forbid_unspecified_additional_properties=self.config.additional_properties_default_policy + == "forbid", enforce_properties_required=self.config.response_properties_default_policy == "required", schema_unmarshallers_factory=self.config.schema_unmarshallers_factory, @@ -548,7 +554,8 @@ def webhook_request_unmarshaller(self) -> WebhookRequestUnmarshaller: spec_validator_cls=self.config.spec_validator_cls, extra_format_validators=self.config.extra_format_validators, extra_media_type_deserializers=self.config.extra_media_type_deserializers, - strict_additional_properties=self.config.strict_additional_properties, + forbid_unspecified_additional_properties=self.config.additional_properties_default_policy + == "forbid", security_provider_factory=self.config.security_provider_factory, schema_unmarshallers_factory=self.config.schema_unmarshallers_factory, extra_format_unmarshallers=self.config.extra_format_unmarshallers, @@ -569,7 +576,8 @@ def webhook_response_unmarshaller(self) -> WebhookResponseUnmarshaller: spec_validator_cls=self.config.spec_validator_cls, extra_format_validators=self.config.extra_format_validators, extra_media_type_deserializers=self.config.extra_media_type_deserializers, - strict_additional_properties=self.config.strict_additional_properties, + forbid_unspecified_additional_properties=self.config.additional_properties_default_policy + == "forbid", enforce_properties_required=self.config.response_properties_default_policy == "required", schema_unmarshallers_factory=self.config.schema_unmarshallers_factory, diff --git a/openapi_core/unmarshalling/request/protocols.py b/openapi_core/unmarshalling/request/protocols.py index eb329448..9289def5 100644 --- a/openapi_core/unmarshalling/request/protocols.py +++ b/openapi_core/unmarshalling/request/protocols.py @@ -55,7 +55,7 @@ def __init__( MediaTypeDeserializersDict ] = None, security_provider_factory: SecurityProviderFactory = security_provider_factory, - strict_additional_properties: bool = False, + forbid_unspecified_additional_properties: bool = False, schema_unmarshallers_factory: Optional[ SchemaUnmarshallersFactory ] = None, @@ -91,7 +91,7 @@ def __init__( MediaTypeDeserializersDict ] = None, security_provider_factory: SecurityProviderFactory = security_provider_factory, - strict_additional_properties: bool = False, + forbid_unspecified_additional_properties: bool = False, schema_unmarshallers_factory: Optional[ SchemaUnmarshallersFactory ] = None, diff --git a/openapi_core/unmarshalling/request/unmarshallers.py b/openapi_core/unmarshalling/request/unmarshallers.py index 0c8d5183..3be652d2 100644 --- a/openapi_core/unmarshalling/request/unmarshallers.py +++ b/openapi_core/unmarshalling/request/unmarshallers.py @@ -120,7 +120,7 @@ def __init__( MediaTypeDeserializersDict ] = None, security_provider_factory: SecurityProviderFactory = security_provider_factory, - strict_additional_properties: bool = False, + forbid_unspecified_additional_properties: bool = False, schema_unmarshallers_factory: Optional[ SchemaUnmarshallersFactory ] = None, @@ -140,7 +140,7 @@ def __init__( format_validators=format_validators, extra_format_validators=extra_format_validators, extra_media_type_deserializers=extra_media_type_deserializers, - strict_additional_properties=strict_additional_properties, + forbid_unspecified_additional_properties=forbid_unspecified_additional_properties, schema_unmarshallers_factory=schema_unmarshallers_factory, format_unmarshallers=format_unmarshallers, extra_format_unmarshallers=extra_format_unmarshallers, @@ -159,7 +159,7 @@ def __init__( extra_format_validators=extra_format_validators, extra_media_type_deserializers=extra_media_type_deserializers, security_provider_factory=security_provider_factory, - strict_additional_properties=strict_additional_properties, + forbid_unspecified_additional_properties=forbid_unspecified_additional_properties, ) def _unmarshal( diff --git a/openapi_core/unmarshalling/response/protocols.py b/openapi_core/unmarshalling/response/protocols.py index 5f38ca3a..fb3c494c 100644 --- a/openapi_core/unmarshalling/response/protocols.py +++ b/openapi_core/unmarshalling/response/protocols.py @@ -55,7 +55,7 @@ def __init__( extra_media_type_deserializers: Optional[ MediaTypeDeserializersDict ] = None, - strict_additional_properties: bool = False, + forbid_unspecified_additional_properties: bool = False, enforce_properties_required: bool = False, schema_unmarshallers_factory: Optional[ SchemaUnmarshallersFactory @@ -92,7 +92,7 @@ def __init__( extra_media_type_deserializers: Optional[ MediaTypeDeserializersDict ] = None, - strict_additional_properties: bool = False, + forbid_unspecified_additional_properties: bool = False, enforce_properties_required: bool = False, schema_unmarshallers_factory: Optional[ SchemaUnmarshallersFactory diff --git a/openapi_core/unmarshalling/schemas/factories.py b/openapi_core/unmarshalling/schemas/factories.py index 9de5a927..3e0f144b 100644 --- a/openapi_core/unmarshalling/schemas/factories.py +++ b/openapi_core/unmarshalling/schemas/factories.py @@ -38,7 +38,7 @@ def create( format_unmarshallers: Optional[FormatUnmarshallersDict] = None, extra_format_validators: Optional[FormatValidatorsDict] = None, extra_format_unmarshallers: Optional[FormatUnmarshallersDict] = None, - strict_additional_properties: bool = False, + forbid_unspecified_additional_properties: bool = False, enforce_properties_required: bool = False, ) -> SchemaUnmarshaller: """Create unmarshaller from the schema.""" @@ -54,7 +54,7 @@ def create( schema, format_validators=format_validators, extra_format_validators=extra_format_validators, - strict_additional_properties=strict_additional_properties, + forbid_unspecified_additional_properties=forbid_unspecified_additional_properties, enforce_properties_required=enforce_properties_required, ) diff --git a/openapi_core/unmarshalling/unmarshallers.py b/openapi_core/unmarshalling/unmarshallers.py index d1c766fb..bf1a3420 100644 --- a/openapi_core/unmarshalling/unmarshallers.py +++ b/openapi_core/unmarshalling/unmarshallers.py @@ -50,7 +50,7 @@ def __init__( extra_media_type_deserializers: Optional[ MediaTypeDeserializersDict ] = None, - strict_additional_properties: bool = False, + forbid_unspecified_additional_properties: bool = False, enforce_properties_required: bool = False, schema_unmarshallers_factory: Optional[ SchemaUnmarshallersFactory @@ -75,7 +75,7 @@ def __init__( format_validators=format_validators, extra_format_validators=extra_format_validators, extra_media_type_deserializers=extra_media_type_deserializers, - strict_additional_properties=strict_additional_properties, + forbid_unspecified_additional_properties=forbid_unspecified_additional_properties, enforce_properties_required=enforce_properties_required, ) self.schema_unmarshallers_factory = ( @@ -93,7 +93,7 @@ def _unmarshal_schema(self, schema: SchemaPath, value: Any) -> Any: schema, format_validators=self.format_validators, extra_format_validators=self.extra_format_validators, - strict_additional_properties=self.strict_additional_properties, + forbid_unspecified_additional_properties=self.forbid_unspecified_additional_properties, enforce_properties_required=self.enforce_properties_required, format_unmarshallers=self.format_unmarshallers, extra_format_unmarshallers=self.extra_format_unmarshallers, diff --git a/openapi_core/validation/configurations.py b/openapi_core/validation/configurations.py index fdcb1f83..afdfbb1a 100644 --- a/openapi_core/validation/configurations.py +++ b/openapi_core/validation/configurations.py @@ -44,8 +44,8 @@ class ValidatorConfig: Extra media type deserializers. security_provider_factory Security providers factory. - strict_additional_properties - If true, treat schemas that omit additionalProperties as if + additional_properties_default_policy + If forbid, treat schemas that omit additionalProperties as if additionalProperties: false. response_properties_default_policy If true, response schema properties are treated as required during @@ -70,7 +70,7 @@ class ValidatorConfig: security_provider_factory: SecurityProviderFactory = ( security_provider_factory ) - strict_additional_properties: bool = False + additional_properties_default_policy: Literal["allow", "forbid"] = "allow" response_properties_default_policy: Literal["optional", "required"] = ( "optional" ) diff --git a/openapi_core/validation/request/protocols.py b/openapi_core/validation/request/protocols.py index 4d972e2e..d57f68e4 100644 --- a/openapi_core/validation/request/protocols.py +++ b/openapi_core/validation/request/protocols.py @@ -49,7 +49,7 @@ def __init__( MediaTypeDeserializersDict ] = None, security_provider_factory: SecurityProviderFactory = security_provider_factory, - strict_additional_properties: bool = False, + forbid_unspecified_additional_properties: bool = False, ): ... def iter_errors( @@ -85,7 +85,7 @@ def __init__( MediaTypeDeserializersDict ] = None, security_provider_factory: SecurityProviderFactory = security_provider_factory, - strict_additional_properties: bool = False, + forbid_unspecified_additional_properties: bool = False, ): ... def iter_errors( diff --git a/openapi_core/validation/request/validators.py b/openapi_core/validation/request/validators.py index c0050f87..808d1b5b 100644 --- a/openapi_core/validation/request/validators.py +++ b/openapi_core/validation/request/validators.py @@ -85,7 +85,7 @@ def __init__( MediaTypeDeserializersDict ] = None, security_provider_factory: SecurityProviderFactory = security_provider_factory, - strict_additional_properties: bool = False, + forbid_unspecified_additional_properties: bool = False, ): BaseValidator.__init__( @@ -101,7 +101,7 @@ def __init__( format_validators=format_validators, extra_format_validators=extra_format_validators, extra_media_type_deserializers=extra_media_type_deserializers, - strict_additional_properties=strict_additional_properties, + forbid_unspecified_additional_properties=forbid_unspecified_additional_properties, ) self.security_provider_factory = security_provider_factory diff --git a/openapi_core/validation/response/protocols.py b/openapi_core/validation/response/protocols.py index 1e324c2a..fb3ea64c 100644 --- a/openapi_core/validation/response/protocols.py +++ b/openapi_core/validation/response/protocols.py @@ -47,7 +47,7 @@ def __init__( extra_media_type_deserializers: Optional[ MediaTypeDeserializersDict ] = None, - strict_additional_properties: bool = False, + forbid_unspecified_additional_properties: bool = False, enforce_properties_required: bool = False, ): ... @@ -85,7 +85,7 @@ def __init__( extra_media_type_deserializers: Optional[ MediaTypeDeserializersDict ] = None, - strict_additional_properties: bool = False, + forbid_unspecified_additional_properties: bool = False, enforce_properties_required: bool = False, ): ... diff --git a/openapi_core/validation/schemas/__init__.py b/openapi_core/validation/schemas/__init__.py index feb2fe28..5eec14e6 100644 --- a/openapi_core/validation/schemas/__init__.py +++ b/openapi_core/validation/schemas/__init__.py @@ -7,7 +7,7 @@ from openapi_schema_validator import OAS32Validator from openapi_core.validation.schemas._validators import ( - build_strict_additional_properties_validator, + build_forbid_unspecified_additional_properties_validator, ) from openapi_core.validation.schemas.factories import SchemaValidatorsFactory @@ -22,7 +22,8 @@ OAS30WriteValidator, Proxy( partial( - build_strict_additional_properties_validator, OAS30WriteValidator + build_forbid_unspecified_additional_properties_validator, + OAS30WriteValidator, ) ), ) @@ -31,7 +32,8 @@ OAS30ReadValidator, Proxy( partial( - build_strict_additional_properties_validator, OAS30ReadValidator + build_forbid_unspecified_additional_properties_validator, + OAS30ReadValidator, ) ), ) @@ -39,7 +41,10 @@ oas31_schema_validators_factory = SchemaValidatorsFactory( OAS31Validator, Proxy( - partial(build_strict_additional_properties_validator, OAS31Validator) + partial( + build_forbid_unspecified_additional_properties_validator, + OAS31Validator, + ) ), # NOTE: Intentionally use OAS 3.0 format checker for OAS 3.1 to preserve # backward compatibility for `byte`/`binary` formats. @@ -50,6 +55,9 @@ oas32_schema_validators_factory = SchemaValidatorsFactory( OAS32Validator, Proxy( - partial(build_strict_additional_properties_validator, OAS32Validator) + partial( + build_forbid_unspecified_additional_properties_validator, + OAS32Validator, + ) ), ) diff --git a/openapi_core/validation/schemas/_validators.py b/openapi_core/validation/schemas/_validators.py index b2e95f36..8fd5f753 100644 --- a/openapi_core/validation/schemas/_validators.py +++ b/openapi_core/validation/schemas/_validators.py @@ -10,7 +10,7 @@ from jsonschema.validators import extend -def build_strict_additional_properties_validator( +def build_forbid_unspecified_additional_properties_validator( validator_class: type[Validator], ) -> type[Validator]: properties_validator = validator_class.VALIDATORS.get("properties") @@ -81,3 +81,42 @@ def iter_missing_additional_properties_errors( if extras: error = "Additional properties are not allowed (%s %s unexpected)" yield ValidationError(error % extras_msg(extras)) + + +def build_enforce_properties_required_validator( + validator_class: type[Validator], +) -> type[Validator]: + properties_validator = validator_class.VALIDATORS.get("properties") + + def enforce_properties( + validator: Any, + properties: Any, + instance: Any, + schema: Mapping[str, Any], + ) -> Iterator[Any]: + if properties_validator is not None: + yield from properties_validator( + validator, properties, instance, schema + ) + + if not validator.is_type(instance, "object"): + return + + for prop_name, prop_schema in properties.items(): + if prop_name not in instance: + if ( + isinstance(prop_schema, dict) + and prop_schema.get("writeOnly") is True + ): + continue + yield ValidationError(f"'{prop_name}' is a required property") + + return cast( + type[Validator], + extend( + validator_class, + validators={ + "properties": enforce_properties, + }, + ), + ) diff --git a/openapi_core/validation/schemas/factories.py b/openapi_core/validation/schemas/factories.py index 562663d9..32082c38 100644 --- a/openapi_core/validation/schemas/factories.py +++ b/openapi_core/validation/schemas/factories.py @@ -1,5 +1,4 @@ from copy import deepcopy -from typing import Any from typing import Optional from typing import cast @@ -7,6 +6,9 @@ from jsonschema.protocols import Validator from jsonschema_path import SchemaPath +from openapi_core.validation.schemas._validators import ( + build_enforce_properties_required_validator, +) from openapi_core.validation.schemas.datatypes import FormatValidatorsDict from openapi_core.validation.schemas.validators import SchemaValidator @@ -58,91 +60,31 @@ def create( schema: SchemaPath, format_validators: Optional[FormatValidatorsDict] = None, extra_format_validators: Optional[FormatValidatorsDict] = None, - strict_additional_properties: bool = False, + forbid_unspecified_additional_properties: bool = False, enforce_properties_required: bool = False, ) -> SchemaValidator: validator_class: type[Validator] = self.schema_validator_class - if strict_additional_properties: + if forbid_unspecified_additional_properties: if self.strict_schema_validator_class is None: raise ValueError( "Strict additional properties validation is not supported " "by this factory." ) validator_class = self.strict_schema_validator_class + + if enforce_properties_required: + validator_class = build_enforce_properties_required_validator( + validator_class + ) + format_checker = self.get_format_checker( format_validators, extra_format_validators ) with schema.resolve() as resolved: - schema_value = resolved.contents - if enforce_properties_required: - schema_value = self._build_required_properties_schema( - schema_value - ) jsonschema_validator = validator_class( - schema_value, + resolved.contents, _resolver=resolved.resolver, format_checker=format_checker, ) return SchemaValidator(schema, jsonschema_validator) - - def _build_required_properties_schema(self, schema_value: Any) -> Any: - updated = deepcopy(schema_value) - self._set_required_properties(updated) - return updated - - def _set_required_properties(self, schema: Any) -> None: - if not isinstance(schema, dict): - return - - properties = schema.get("properties") - if isinstance(properties, dict) and properties: - schema["required"] = [ - property_name - for property_name, property_schema in properties.items() - if not self._is_write_only_property(property_schema) - ] - for property_schema in properties.values(): - self._set_required_properties(property_schema) - - for keyword in ( - "allOf", - "anyOf", - "oneOf", - "prefixItems", - ): - subschemas = schema.get(keyword) - if isinstance(subschemas, list): - for subschema in subschemas: - self._set_required_properties(subschema) - - for keyword in ( - "items", - "contains", - "if", - "then", - "else", - "not", - "propertyNames", - "additionalProperties", - "unevaluatedProperties", - "unevaluatedItems", - "contentSchema", - ): - self._set_required_properties(schema.get(keyword)) - - for keyword in ("$defs", "definitions", "patternProperties"): - subschemas_map = schema.get(keyword) - if isinstance(subschemas_map, dict): - for subschema in subschemas_map.values(): - self._set_required_properties(subschema) - - dependent_schemas = schema.get("dependentSchemas") - if isinstance(dependent_schemas, dict): - for subschema in dependent_schemas.values(): - self._set_required_properties(subschema) - - def _is_write_only_property(self, property_schema: Any) -> bool: - if not isinstance(property_schema, dict): - return False - return property_schema.get("writeOnly") is True diff --git a/openapi_core/validation/validators.py b/openapi_core/validation/validators.py index aeb5f5be..5d9ecb42 100644 --- a/openapi_core/validation/validators.py +++ b/openapi_core/validation/validators.py @@ -64,7 +64,7 @@ def __init__( extra_media_type_deserializers: Optional[ MediaTypeDeserializersDict ] = None, - strict_additional_properties: bool = False, + forbid_unspecified_additional_properties: bool = False, enforce_properties_required: bool = False, ): self.spec = spec @@ -103,7 +103,9 @@ def __init__( self.format_validators = format_validators self.extra_format_validators = extra_format_validators self.extra_media_type_deserializers = extra_media_type_deserializers - self.strict_additional_properties = strict_additional_properties + self.forbid_unspecified_additional_properties = ( + forbid_unspecified_additional_properties + ) self.enforce_properties_required = enforce_properties_required @cached_property @@ -144,7 +146,7 @@ def _deserialise_media_type( schema, format_validators=self.format_validators, extra_format_validators=self.extra_format_validators, - strict_additional_properties=self.strict_additional_properties, + forbid_unspecified_additional_properties=self.forbid_unspecified_additional_properties, enforce_properties_required=self.enforce_properties_required, ) deserializer = self.media_type_deserializers_factory.create( @@ -176,7 +178,7 @@ def _validate_schema(self, schema: SchemaPath, value: Any) -> None: schema, format_validators=self.format_validators, extra_format_validators=self.extra_format_validators, - strict_additional_properties=self.strict_additional_properties, + forbid_unspecified_additional_properties=self.forbid_unspecified_additional_properties, enforce_properties_required=self.enforce_properties_required, ) validator.validate(value) diff --git a/tests/integration/validation/test_strict_additional_properties.py b/tests/integration/validation/test_additional_properties_default_policy.py similarity index 95% rename from tests/integration/validation/test_strict_additional_properties.py rename to tests/integration/validation/test_additional_properties_default_policy.py index 043a200a..abb54576 100644 --- a/tests/integration/validation/test_strict_additional_properties.py +++ b/tests/integration/validation/test_additional_properties_default_policy.py @@ -79,7 +79,7 @@ def test_request_validation_default_allows_extra_properties(): def test_request_validation_strict_rejects_extra_properties(): - config = Config(strict_additional_properties=True) + config = Config(additional_properties_default_policy="forbid") openapi = OpenAPI.from_dict(_spec_dict(), config=config) request = MockRequest( "http://example.com", @@ -116,7 +116,7 @@ def test_response_validation_default_allows_extra_properties(): def test_response_validation_strict_rejects_extra_properties(): - config = Config(strict_additional_properties=True) + config = Config(additional_properties_default_policy="forbid") openapi = OpenAPI.from_dict(_spec_dict(), config=config) request = MockRequest("http://example.com", "get", "/tags") response = MockResponse( @@ -138,7 +138,7 @@ def test_response_validation_strict_allows_explicit_additional_properties_true() spec_dict = _spec_dict() spec_dict["components"]["schemas"]["Tag"]["additionalProperties"] = True - config = Config(strict_additional_properties=True) + config = Config(additional_properties_default_policy="forbid") openapi = OpenAPI.from_dict(spec_dict, config=config) request = MockRequest("http://example.com", "get", "/tags") response = MockResponse( diff --git a/tests/unit/validation/test_schema_validators.py b/tests/unit/validation/test_schema_validators.py index 0c4df25f..9b9c83a8 100644 --- a/tests/unit/validation/test_schema_validators.py +++ b/tests/unit/validation/test_schema_validators.py @@ -251,7 +251,7 @@ def test_additional_properties_omitted_strict_rejects_extra(self): with pytest.raises(InvalidSchemaValue): oas30_write_schema_validators_factory.create( spec, - strict_additional_properties=True, + forbid_unspecified_additional_properties=True, ).validate(value) def test_additional_properties_true_strict_allows_extra(self): @@ -271,7 +271,7 @@ def test_additional_properties_true_strict_allows_extra(self): result = oas30_write_schema_validators_factory.create( spec, - strict_additional_properties=True, + forbid_unspecified_additional_properties=True, ).validate(value) assert result is None