Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions packages/google-api-core/google/api_core/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -510,6 +510,8 @@ def format_http_response_error(
:class:`GoogleAPICallError`, with the message and errors populated
from the response.
"""
if isinstance(payload, list):
payload = next((item for item in payload if isinstance(item, dict)), {})
payload = {} if not payload else payload
Comment on lines +513 to 515

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

To ensure robust defensive programming against untrusted or malformed REST payloads, we should guarantee that payload is a dictionary before calling .get() on it. If the payload is a string, number, or other non-dictionary type, the current implementation will raise an AttributeError. Replacing the falsy check with an explicit isinstance(payload, dict) check prevents potential crashes.

Suggested change
if isinstance(payload, list):
payload = next((item for item in payload if isinstance(item, dict)), {})
payload = {} if not payload else payload
if isinstance(payload, list):
payload = next((item for item in payload if isinstance(item, dict)), {})
if not isinstance(payload, dict):
payload = {}

error_message = payload.get("error", {}).get("message", "unknown error")
errors = payload.get("error", {}).get("errors", ())
Expand Down
25 changes: 25 additions & 0 deletions packages/google-api-core/tests/unit/test_exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,31 @@ def test_from_http_response_json_content():
assert exception.errors == ["1", "2"]


def test_from_http_response_json_list_content():
response = make_response(
json.dumps(
[{"error": {"message": "json message", "errors": ["1", "2"]}}]
).encode("utf-8")
)

exception = exceptions.from_http_response(response)

assert isinstance(exception, exceptions.NotFound)
assert exception.code == http.client.NOT_FOUND
assert exception.message == "POST https://example.com/: json message"
assert exception.errors == ["1", "2"]


def test_from_http_response_json_list_content_without_error_dict():
response = make_response(json.dumps(["error message"]).encode("utf-8"))

exception = exceptions.from_http_response(response)

assert isinstance(exception, exceptions.NotFound)
assert exception.code == http.client.NOT_FOUND
assert exception.message == "POST https://example.com/: unknown error"


def test_from_http_response_bad_json_content():
response = make_response(json.dumps({"meep": "moop"}).encode("utf-8"))

Expand Down
Loading