Skip to content

Commit

Permalink
Reject non-str values in form data (#9292)
Browse files Browse the repository at this point in the history
  • Loading branch information
Dreamsorcerer authored Sep 26, 2024
1 parent 56aa261 commit d7cd061
Show file tree
Hide file tree
Showing 5 changed files with 22 additions and 5 deletions.
1 change: 1 addition & 0 deletions CHANGES/9292.breaking.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Started rejecting non string values in `FormData`, to avoid unexpected results -- by :user:`Dreamsorcerer`.
2 changes: 2 additions & 0 deletions aiohttp/formdata.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@ def _gen_form_urlencoded(self) -> payload.BytesPayload:
# form data (x-www-form-urlencoded)
data = []
for type_options, _, value in self._fields:
if not isinstance(value, str):
raise TypeError(f"expected str, got {value!r}")
data.append((type_options["name"], value))

charset = self._charset if self._charset is not None else "utf-8"
Expand Down
6 changes: 6 additions & 0 deletions tests/test_client_session.py
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,12 @@ async def test_merge_headers_with_list_of_tuples_duplicated_names(
]


@pytest.mark.parametrize("obj", (object(), None))
async def test_invalid_data(session: ClientSession, obj: object) -> None:
with pytest.raises(TypeError, match="expected str"):
await session.post("http://example.test/", data={"some": obj})


async def test_http_GET(session: ClientSession, params: _Params) -> None:
with mock.patch(
"aiohttp.client.ClientSession._request", autospec=True, spec_set=True
Expand Down
14 changes: 11 additions & 3 deletions tests/test_formdata.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,18 @@ def test_formdata_multipart(buf: bytearray) -> None:
assert form.is_multipart


def test_invalid_formdata_payload() -> None:
@pytest.mark.parametrize("obj", (object(), None))
def test_invalid_formdata_payload_multipart(obj: object) -> None:
form = FormData()
form.add_field("test", object(), filename="test.txt")
with pytest.raises(TypeError):
form.add_field("test", obj, filename="test.txt")
with pytest.raises(TypeError, match="Can not serialize value"):
form()


@pytest.mark.parametrize("obj", (object(), None))
def test_invalid_formdata_payload_urlencoded(obj: object) -> None:
form = FormData({"test": obj})
with pytest.raises(TypeError, match="expected str"):
form()


Expand Down
4 changes: 2 additions & 2 deletions tests/test_web_functional.py
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ async def handler(request: web.Request) -> web.Response:
app.router.add_post("/", handler)
client = await aiohttp_client(app)

resp = await client.post("/", data={"a": 1, "b": 2, "c": ""})
resp = await client.post("/", data={"a": "1", "b": "2", "c": ""})
assert 200 == resp.status
txt = await resp.text()
assert "OK" == txt
Expand Down Expand Up @@ -528,7 +528,7 @@ async def handler(request: web.Request) -> web.Response:
app.router.add_post("/", handler)
client = await aiohttp_client(app)

resp = await client.post("/", data=MultiDict([("a", 1), ("a", 2)]))
resp = await client.post("/", data=MultiDict([("a", "1"), ("a", "2")]))
assert 200 == resp.status

resp.release()
Expand Down

0 comments on commit d7cd061

Please sign in to comment.