1
0
mirror of https://github.com/postgres/postgres.git synced 2025-12-19 17:02:53 +03:00

oauth: Limit JSON parsing depth in the client

Check the ctx->nested level as we go, to prevent a server from running
the client out of stack space.

The limit we choose when communicating with authorization servers can't
be overly strict, since those servers will continue to add extensions in
their JSON documents which we need to correctly ignore. For the SASL
communication, we can be more conservative, since there are no defined
extensions (and the peer is probably more Postgres code).

Reviewed-by: Aleksander Alekseev <aleksander@timescale.com>
Discussion: https://postgr.es/m/CAOYmi%2Bm71aRUEi0oQE9ciBnBS8xVtMn3CifaPu2kmJzUfhOZgA%40mail.gmail.com
This commit is contained in:
Jacob Champion
2025-05-23 13:05:33 -07:00
parent 1ca583f6c0
commit cbc8fd0c9a
4 changed files with 97 additions and 7 deletions

View File

@@ -295,6 +295,26 @@ $node->connect_fails(
expected_stderr =>
qr/failed to obtain access token: response is too large/);
my $nesting_limit = 16;
$node->connect_ok(
connstr(
stage => 'device',
nested_array => $nesting_limit,
nested_object => $nesting_limit),
"nested arrays and objects, up to parse limit",
expected_stderr =>
qr@Visit https://example\.com/ and enter the code: postgresuser@);
$node->connect_fails(
connstr(stage => 'device', nested_array => $nesting_limit + 1),
"bad discovery response: overly nested JSON array",
expected_stderr =>
qr/failed to parse device authorization: JSON is too deeply nested/);
$node->connect_fails(
connstr(stage => 'device', nested_object => $nesting_limit + 1),
"bad discovery response: overly nested JSON object",
expected_stderr =>
qr/failed to parse device authorization: JSON is too deeply nested/);
$node->connect_fails(
connstr(stage => 'device', content_type => 'text/plain'),
"bad device authz response: wrong content type",

View File

@@ -7,6 +7,7 @@
#
import base64
import functools
import http.server
import json
import os
@@ -213,14 +214,32 @@ class OAuthHandler(http.server.BaseHTTPRequestHandler):
@property
def _response_padding(self):
"""
If the huge_response test parameter is set to True, returns a dict
containing a gigantic string value, which can then be folded into a JSON
response.
"""
if not self._get_param("huge_response", False):
return dict()
Returns a dict with any additional entries that should be folded into a
JSON response, as determined by test parameters provided by the client:
return {"_pad_": "x" * 1024 * 1024}
- huge_response: if set to True, the dict will contain a gigantic string
value
- nested_array: if set to nonzero, the dict will contain a deeply nested
array so that the top-level object has the given depth
- nested_object: if set to nonzero, the dict will contain a deeply
nested JSON object so that the top-level object has the given depth
"""
ret = dict()
if self._get_param("huge_response", False):
ret["_pad_"] = "x" * 1024 * 1024
depth = self._get_param("nested_array", 0)
if depth:
ret["_arr_"] = functools.reduce(lambda x, _: [x], range(depth))
depth = self._get_param("nested_object", 0)
if depth:
ret["_obj_"] = functools.reduce(lambda x, _: {"": x}, range(depth))
return ret
@property
def _access_token(self):