From 92143221296e8541f9de8f32f43742a800b0eea3 Mon Sep 17 00:00:00 2001 From: Timothy Flynn Date: Wed, 19 Mar 2025 17:15:53 -0400 Subject: [PATCH] UI/Qt: Do not make assumptions about autocomplete response types For example, we expect a JSON array from Google. Let's not crash if we get a JSON object or some other unexpected type. --- UI/Qt/AutoComplete.cpp | 82 ++++++++++++++++++++++++------------------ UI/Qt/AutoComplete.h | 6 ++-- 2 files changed, 50 insertions(+), 38 deletions(-) diff --git a/UI/Qt/AutoComplete.cpp b/UI/Qt/AutoComplete.cpp index aa3ff3e5d83..02e3f724bc6 100644 --- a/UI/Qt/AutoComplete.cpp +++ b/UI/Qt/AutoComplete.cpp @@ -37,67 +37,79 @@ AutoComplete::AutoComplete(QWidget* parent) }); } -ErrorOr> AutoComplete::parse_google_autocomplete(Vector const& json) +ErrorOr> AutoComplete::parse_google_autocomplete(JsonValue const& json) { - if (json.size() != 5) - return Error::from_string_literal("Invalid JSON, expected 5 elements in array"); + if (!json.is_array()) + return Error::from_string_literal("Expected Google autocomplete response to be a JSON array"); - if (!json[0].is_string()) - return Error::from_string_literal("Invalid JSON, expected first element to be a string"); - auto query = json[0].as_string(); + auto const& values = json.as_array(); - if (!json[1].is_array()) - return Error::from_string_literal("Invalid JSON, expected second element to be an array"); - auto suggestions_array = json[1].as_array().values(); + if (values.size() != 5) + return Error::from_string_literal("Invalid Google autocomplete response, expected 5 elements in array"); + if (!values[0].is_string()) + return Error::from_string_literal("Invalid Google autocomplete response, expected first element to be a string"); + + auto const& query = values[0].as_string(); if (query != m_query) - return Error::from_string_literal("Invalid JSON, query does not match"); + return Error::from_string_literal("Invalid Google autocomplete response, query does not match"); + + if (!values[1].is_array()) + return Error::from_string_literal("Invalid Google autocomplete response, expected second element to be an array"); + auto const& suggestions_array = values[1].as_array().values(); Vector results; results.ensure_capacity(suggestions_array.size()); - for (auto& suggestion : suggestions_array) + for (auto const& suggestion : suggestions_array) results.unchecked_append(suggestion.as_string()); return results; } -ErrorOr> AutoComplete::parse_duckduckgo_autocomplete(Vector const& json) +ErrorOr> AutoComplete::parse_duckduckgo_autocomplete(JsonValue const& json) { - Vector results; + if (!json.is_array()) + return Error::from_string_literal("Expected DuckDuckGo autocomplete response to be a JSON array"); - for (auto const& suggestion : json) { + Vector results; + results.ensure_capacity(json.as_array().size()); + + for (auto const& suggestion : json.as_array().values()) { if (!suggestion.is_object()) - return Error::from_string_literal("Invalid JSON, expected value to be an object"); + return Error::from_string_literal("Invalid DuckDuckGo autocomplete response, expected value to be an object"); if (auto value = suggestion.as_object().get_string("phrase"sv); value.has_value()) - results.append(*value); + results.unchecked_append(*value); } return results; } -ErrorOr> AutoComplete::parse_yahoo_autocomplete(JsonObject const& json) +ErrorOr> AutoComplete::parse_yahoo_autocomplete(JsonValue const& json) { - auto query = json.get_string("q"sv); - if (!query.has_value()) - return Error::from_string_view("Invalid JSON, expected \"q\" to be a string"sv); - if (query != m_query) - return Error::from_string_literal("Invalid JSON, query does not match"); + if (!json.is_object()) + return Error::from_string_literal("Expected Yahoo autocomplete response to be a JSON array"); - auto suggestions = json.get_array("r"sv); + auto query = json.as_object().get_string("q"sv); + if (!query.has_value()) + return Error::from_string_literal("Invalid Yahoo autocomplete response, expected \"q\" to be a string"); + if (query != m_query) + return Error::from_string_literal("Invalid Yahoo autocomplete response, query does not match"); + + auto suggestions = json.as_object().get_array("r"sv); if (!suggestions.has_value()) - return Error::from_string_view("Invalid JSON, expected \"r\" to be an object"sv); + return Error::from_string_literal("Invalid Yahoo autocomplete response, expected \"r\" to be an object"); Vector results; results.ensure_capacity(suggestions->size()); for (auto const& suggestion : suggestions->values()) { if (!suggestion.is_object()) - return Error::from_string_literal("Invalid JSON, expected value to be an object"); + return Error::from_string_literal("Invalid Yahoo autocomplete response, expected value to be an object"); auto result = suggestion.as_object().get_string("k"sv); if (!result.has_value()) - return Error::from_string_view("Invalid JSON, expected \"k\" to be a string"sv); + return Error::from_string_literal("Invalid Yahoo autocomplete response, expected \"k\" to be a string"); results.unchecked_append(*result); } @@ -113,17 +125,17 @@ ErrorOr AutoComplete::got_network_response(QNetworkReply* reply) auto reply_data = ak_string_from_qstring(reply->readAll()); auto json = TRY(JsonValue::from_string(reply_data)); - auto engine_name = Settings::the()->autocomplete_engine().name; + auto const& engine_name = Settings::the()->autocomplete_engine().name; + Vector results; - if (engine_name == "Google") { - results = TRY(parse_google_autocomplete(json.as_array().values())); - } else if (engine_name == "DuckDuckGo") { - results = TRY(parse_duckduckgo_autocomplete(json.as_array().values())); - } else if (engine_name == "Yahoo") - results = TRY(parse_yahoo_autocomplete(json.as_object())); - else { + if (engine_name == "Google") + results = TRY(parse_google_autocomplete(json)); + else if (engine_name == "DuckDuckGo") + results = TRY(parse_duckduckgo_autocomplete(json)); + else if (engine_name == "Yahoo") + results = TRY(parse_yahoo_autocomplete(json)); + else return Error::from_string_literal("Invalid engine name"); - } constexpr size_t MAX_AUTOCOMPLETE_RESULTS = 6; if (results.is_empty()) { diff --git a/UI/Qt/AutoComplete.h b/UI/Qt/AutoComplete.h index 580cba59342..c655bee5afa 100644 --- a/UI/Qt/AutoComplete.h +++ b/UI/Qt/AutoComplete.h @@ -79,9 +79,9 @@ private: ErrorOr got_network_response(QNetworkReply* reply); - ErrorOr> parse_google_autocomplete(Vector const&); - ErrorOr> parse_duckduckgo_autocomplete(Vector const&); - ErrorOr> parse_yahoo_autocomplete(JsonObject const&); + ErrorOr> parse_google_autocomplete(JsonValue const&); + ErrorOr> parse_duckduckgo_autocomplete(JsonValue const&); + ErrorOr> parse_yahoo_autocomplete(JsonValue const&); QNetworkAccessManager* m_manager; AutoCompleteModel* m_auto_complete_model;