Skip to content

Commit bb7b3d3

Browse files
authored
Merge pull request #1132 from python-openapi/feature/additional-properties-policy
Additional properties policy
2 parents cbdb69f + f2e7c71 commit bb7b3d3

File tree

17 files changed

+115
-116
lines changed

17 files changed

+115
-116
lines changed

docs/configuration.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ result = openapi.unmarshal_response(request, response)
136136

137137
By default, OpenAPI follows JSON Schema behavior: when an object schema omits `additionalProperties`, extra keys are allowed.
138138

139-
If you want stricter behavior, enable `strict_additional_properties`. In this mode, omitted `additionalProperties` is treated as `false`.
139+
If you want stricter behavior, change `additional_properties_default_policy` to `forbid`. In this mode, omitted `additionalProperties` is treated as `false`.
140140

141141
This mode is particularly useful for:
142142
- **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
148148
from openapi_core import OpenAPI
149149

150150
config = Config(
151-
strict_additional_properties=True,
151+
additional_properties_default_policy="forbid",
152152
)
153153
openapi = OpenAPI.from_file_path('openapi.json', config=config)
154154
```

openapi_core/app.py

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -427,7 +427,8 @@ def request_validator(self) -> RequestValidator:
427427
spec_validator_cls=self.config.spec_validator_cls,
428428
extra_format_validators=self.config.extra_format_validators,
429429
extra_media_type_deserializers=self.config.extra_media_type_deserializers,
430-
strict_additional_properties=self.config.strict_additional_properties,
430+
forbid_unspecified_additional_properties=self.config.additional_properties_default_policy
431+
== "forbid",
431432
security_provider_factory=self.config.security_provider_factory,
432433
)
433434

@@ -446,7 +447,8 @@ def response_validator(self) -> ResponseValidator:
446447
spec_validator_cls=self.config.spec_validator_cls,
447448
extra_format_validators=self.config.extra_format_validators,
448449
extra_media_type_deserializers=self.config.extra_media_type_deserializers,
449-
strict_additional_properties=self.config.strict_additional_properties,
450+
forbid_unspecified_additional_properties=self.config.additional_properties_default_policy
451+
== "forbid",
450452
enforce_properties_required=self.config.response_properties_default_policy
451453
== "required",
452454
)
@@ -466,7 +468,8 @@ def webhook_request_validator(self) -> WebhookRequestValidator:
466468
spec_validator_cls=self.config.spec_validator_cls,
467469
extra_format_validators=self.config.extra_format_validators,
468470
extra_media_type_deserializers=self.config.extra_media_type_deserializers,
469-
strict_additional_properties=self.config.strict_additional_properties,
471+
forbid_unspecified_additional_properties=self.config.additional_properties_default_policy
472+
== "forbid",
470473
security_provider_factory=self.config.security_provider_factory,
471474
)
472475

@@ -485,7 +488,8 @@ def webhook_response_validator(self) -> WebhookResponseValidator:
485488
spec_validator_cls=self.config.spec_validator_cls,
486489
extra_format_validators=self.config.extra_format_validators,
487490
extra_media_type_deserializers=self.config.extra_media_type_deserializers,
488-
strict_additional_properties=self.config.strict_additional_properties,
491+
forbid_unspecified_additional_properties=self.config.additional_properties_default_policy
492+
== "forbid",
489493
enforce_properties_required=self.config.response_properties_default_policy
490494
== "required",
491495
)
@@ -505,7 +509,8 @@ def request_unmarshaller(self) -> RequestUnmarshaller:
505509
spec_validator_cls=self.config.spec_validator_cls,
506510
extra_format_validators=self.config.extra_format_validators,
507511
extra_media_type_deserializers=self.config.extra_media_type_deserializers,
508-
strict_additional_properties=self.config.strict_additional_properties,
512+
forbid_unspecified_additional_properties=self.config.additional_properties_default_policy
513+
== "forbid",
509514
security_provider_factory=self.config.security_provider_factory,
510515
schema_unmarshallers_factory=self.config.schema_unmarshallers_factory,
511516
extra_format_unmarshallers=self.config.extra_format_unmarshallers,
@@ -526,7 +531,8 @@ def response_unmarshaller(self) -> ResponseUnmarshaller:
526531
spec_validator_cls=self.config.spec_validator_cls,
527532
extra_format_validators=self.config.extra_format_validators,
528533
extra_media_type_deserializers=self.config.extra_media_type_deserializers,
529-
strict_additional_properties=self.config.strict_additional_properties,
534+
forbid_unspecified_additional_properties=self.config.additional_properties_default_policy
535+
== "forbid",
530536
enforce_properties_required=self.config.response_properties_default_policy
531537
== "required",
532538
schema_unmarshallers_factory=self.config.schema_unmarshallers_factory,
@@ -548,7 +554,8 @@ def webhook_request_unmarshaller(self) -> WebhookRequestUnmarshaller:
548554
spec_validator_cls=self.config.spec_validator_cls,
549555
extra_format_validators=self.config.extra_format_validators,
550556
extra_media_type_deserializers=self.config.extra_media_type_deserializers,
551-
strict_additional_properties=self.config.strict_additional_properties,
557+
forbid_unspecified_additional_properties=self.config.additional_properties_default_policy
558+
== "forbid",
552559
security_provider_factory=self.config.security_provider_factory,
553560
schema_unmarshallers_factory=self.config.schema_unmarshallers_factory,
554561
extra_format_unmarshallers=self.config.extra_format_unmarshallers,
@@ -569,7 +576,8 @@ def webhook_response_unmarshaller(self) -> WebhookResponseUnmarshaller:
569576
spec_validator_cls=self.config.spec_validator_cls,
570577
extra_format_validators=self.config.extra_format_validators,
571578
extra_media_type_deserializers=self.config.extra_media_type_deserializers,
572-
strict_additional_properties=self.config.strict_additional_properties,
579+
forbid_unspecified_additional_properties=self.config.additional_properties_default_policy
580+
== "forbid",
573581
enforce_properties_required=self.config.response_properties_default_policy
574582
== "required",
575583
schema_unmarshallers_factory=self.config.schema_unmarshallers_factory,

openapi_core/unmarshalling/request/protocols.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ def __init__(
5555
MediaTypeDeserializersDict
5656
] = None,
5757
security_provider_factory: SecurityProviderFactory = security_provider_factory,
58-
strict_additional_properties: bool = False,
58+
forbid_unspecified_additional_properties: bool = False,
5959
schema_unmarshallers_factory: Optional[
6060
SchemaUnmarshallersFactory
6161
] = None,
@@ -91,7 +91,7 @@ def __init__(
9191
MediaTypeDeserializersDict
9292
] = None,
9393
security_provider_factory: SecurityProviderFactory = security_provider_factory,
94-
strict_additional_properties: bool = False,
94+
forbid_unspecified_additional_properties: bool = False,
9595
schema_unmarshallers_factory: Optional[
9696
SchemaUnmarshallersFactory
9797
] = None,

openapi_core/unmarshalling/request/unmarshallers.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ def __init__(
120120
MediaTypeDeserializersDict
121121
] = None,
122122
security_provider_factory: SecurityProviderFactory = security_provider_factory,
123-
strict_additional_properties: bool = False,
123+
forbid_unspecified_additional_properties: bool = False,
124124
schema_unmarshallers_factory: Optional[
125125
SchemaUnmarshallersFactory
126126
] = None,
@@ -140,7 +140,7 @@ def __init__(
140140
format_validators=format_validators,
141141
extra_format_validators=extra_format_validators,
142142
extra_media_type_deserializers=extra_media_type_deserializers,
143-
strict_additional_properties=strict_additional_properties,
143+
forbid_unspecified_additional_properties=forbid_unspecified_additional_properties,
144144
schema_unmarshallers_factory=schema_unmarshallers_factory,
145145
format_unmarshallers=format_unmarshallers,
146146
extra_format_unmarshallers=extra_format_unmarshallers,
@@ -159,7 +159,7 @@ def __init__(
159159
extra_format_validators=extra_format_validators,
160160
extra_media_type_deserializers=extra_media_type_deserializers,
161161
security_provider_factory=security_provider_factory,
162-
strict_additional_properties=strict_additional_properties,
162+
forbid_unspecified_additional_properties=forbid_unspecified_additional_properties,
163163
)
164164

165165
def _unmarshal(

openapi_core/unmarshalling/response/protocols.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ def __init__(
5555
extra_media_type_deserializers: Optional[
5656
MediaTypeDeserializersDict
5757
] = None,
58-
strict_additional_properties: bool = False,
58+
forbid_unspecified_additional_properties: bool = False,
5959
enforce_properties_required: bool = False,
6060
schema_unmarshallers_factory: Optional[
6161
SchemaUnmarshallersFactory
@@ -92,7 +92,7 @@ def __init__(
9292
extra_media_type_deserializers: Optional[
9393
MediaTypeDeserializersDict
9494
] = None,
95-
strict_additional_properties: bool = False,
95+
forbid_unspecified_additional_properties: bool = False,
9696
enforce_properties_required: bool = False,
9797
schema_unmarshallers_factory: Optional[
9898
SchemaUnmarshallersFactory

openapi_core/unmarshalling/schemas/factories.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ def create(
3838
format_unmarshallers: Optional[FormatUnmarshallersDict] = None,
3939
extra_format_validators: Optional[FormatValidatorsDict] = None,
4040
extra_format_unmarshallers: Optional[FormatUnmarshallersDict] = None,
41-
strict_additional_properties: bool = False,
41+
forbid_unspecified_additional_properties: bool = False,
4242
enforce_properties_required: bool = False,
4343
) -> SchemaUnmarshaller:
4444
"""Create unmarshaller from the schema."""
@@ -54,7 +54,7 @@ def create(
5454
schema,
5555
format_validators=format_validators,
5656
extra_format_validators=extra_format_validators,
57-
strict_additional_properties=strict_additional_properties,
57+
forbid_unspecified_additional_properties=forbid_unspecified_additional_properties,
5858
enforce_properties_required=enforce_properties_required,
5959
)
6060

openapi_core/unmarshalling/unmarshallers.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ def __init__(
5050
extra_media_type_deserializers: Optional[
5151
MediaTypeDeserializersDict
5252
] = None,
53-
strict_additional_properties: bool = False,
53+
forbid_unspecified_additional_properties: bool = False,
5454
enforce_properties_required: bool = False,
5555
schema_unmarshallers_factory: Optional[
5656
SchemaUnmarshallersFactory
@@ -75,7 +75,7 @@ def __init__(
7575
format_validators=format_validators,
7676
extra_format_validators=extra_format_validators,
7777
extra_media_type_deserializers=extra_media_type_deserializers,
78-
strict_additional_properties=strict_additional_properties,
78+
forbid_unspecified_additional_properties=forbid_unspecified_additional_properties,
7979
enforce_properties_required=enforce_properties_required,
8080
)
8181
self.schema_unmarshallers_factory = (
@@ -93,7 +93,7 @@ def _unmarshal_schema(self, schema: SchemaPath, value: Any) -> Any:
9393
schema,
9494
format_validators=self.format_validators,
9595
extra_format_validators=self.extra_format_validators,
96-
strict_additional_properties=self.strict_additional_properties,
96+
forbid_unspecified_additional_properties=self.forbid_unspecified_additional_properties,
9797
enforce_properties_required=self.enforce_properties_required,
9898
format_unmarshallers=self.format_unmarshallers,
9999
extra_format_unmarshallers=self.extra_format_unmarshallers,

openapi_core/validation/configurations.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,8 @@ class ValidatorConfig:
4444
Extra media type deserializers.
4545
security_provider_factory
4646
Security providers factory.
47-
strict_additional_properties
48-
If true, treat schemas that omit additionalProperties as if
47+
additional_properties_default_policy
48+
If forbid, treat schemas that omit additionalProperties as if
4949
additionalProperties: false.
5050
response_properties_default_policy
5151
If true, response schema properties are treated as required during
@@ -70,7 +70,7 @@ class ValidatorConfig:
7070
security_provider_factory: SecurityProviderFactory = (
7171
security_provider_factory
7272
)
73-
strict_additional_properties: bool = False
73+
additional_properties_default_policy: Literal["allow", "forbid"] = "allow"
7474
response_properties_default_policy: Literal["optional", "required"] = (
7575
"optional"
7676
)

openapi_core/validation/request/protocols.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ def __init__(
4949
MediaTypeDeserializersDict
5050
] = None,
5151
security_provider_factory: SecurityProviderFactory = security_provider_factory,
52-
strict_additional_properties: bool = False,
52+
forbid_unspecified_additional_properties: bool = False,
5353
): ...
5454

5555
def iter_errors(
@@ -85,7 +85,7 @@ def __init__(
8585
MediaTypeDeserializersDict
8686
] = None,
8787
security_provider_factory: SecurityProviderFactory = security_provider_factory,
88-
strict_additional_properties: bool = False,
88+
forbid_unspecified_additional_properties: bool = False,
8989
): ...
9090

9191
def iter_errors(

openapi_core/validation/request/validators.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ def __init__(
8585
MediaTypeDeserializersDict
8686
] = None,
8787
security_provider_factory: SecurityProviderFactory = security_provider_factory,
88-
strict_additional_properties: bool = False,
88+
forbid_unspecified_additional_properties: bool = False,
8989
):
9090

9191
BaseValidator.__init__(
@@ -101,7 +101,7 @@ def __init__(
101101
format_validators=format_validators,
102102
extra_format_validators=extra_format_validators,
103103
extra_media_type_deserializers=extra_media_type_deserializers,
104-
strict_additional_properties=strict_additional_properties,
104+
forbid_unspecified_additional_properties=forbid_unspecified_additional_properties,
105105
)
106106
self.security_provider_factory = security_provider_factory
107107

0 commit comments

Comments
 (0)