From 1aece83cd08fac469f20e1fc43286db987c30465 Mon Sep 17 00:00:00 2001 From: Anders Kaseorg Date: Mon, 12 Sep 2022 21:32:11 -0700 Subject: [PATCH] Refuse to cast str or bytes to array Although str and bytes act as sequences in Python, they do not count as arrays according to OpenAPI, so we should not allow them to validate as arrays. Signed-off-by: Anders Kaseorg --- openapi_core/casting/schemas/casters.py | 4 ++++ openapi_core/casting/schemas/exceptions.py | 3 ++- tests/unit/casting/test_schema_casters.py | 10 ++++++---- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/openapi_core/casting/schemas/casters.py b/openapi_core/casting/schemas/casters.py index 14794067..d1cd9e00 100644 --- a/openapi_core/casting/schemas/casters.py +++ b/openapi_core/casting/schemas/casters.py @@ -54,6 +54,10 @@ def items_caster(self) -> BaseSchemaCaster: return self.casters_factory.create(self.schema / "items") def cast(self, value: Any) -> List[Any]: + # str and bytes are not arrays according to the OpenAPI spec + if isinstance(value, (str, bytes)): + raise CastError(value, self.schema["type"]) + try: return list(map(self.items_caster, value)) except (ValueError, TypeError): diff --git a/openapi_core/casting/schemas/exceptions.py b/openapi_core/casting/schemas/exceptions.py index 0c4d25b1..242288d2 100644 --- a/openapi_core/casting/schemas/exceptions.py +++ b/openapi_core/casting/schemas/exceptions.py @@ -1,4 +1,5 @@ from dataclasses import dataclass +from typing import Any from openapi_core.exceptions import OpenAPIError @@ -7,7 +8,7 @@ class CastError(OpenAPIError): """Schema cast operation error""" - value: str + value: Any type: str def __str__(self) -> str: diff --git a/tests/unit/casting/test_schema_casters.py b/tests/unit/casting/test_schema_casters.py index 64600bac..96cc522f 100644 --- a/tests/unit/casting/test_schema_casters.py +++ b/tests/unit/casting/test_schema_casters.py @@ -26,15 +26,17 @@ def test_array_invalid_type(self, caster_factory): with pytest.raises(CastError): caster_factory(schema)(value) - def test_array_invalid_value(self, caster_factory): + @pytest.mark.parametrize("value", [3.14, "foo", b"foo"]) + def test_array_invalid_value(self, value, caster_factory): spec = { "type": "array", "items": { - "type": "number", + "oneOf": [{"type": "number"}, {"type": "string"}], }, } schema = Spec.from_dict(spec) - value = 3.14 - with pytest.raises(CastError): + with pytest.raises( + CastError, match=f"Failed to cast value to array type: {value}" + ): caster_factory(schema)(value)