From 079386b70777d9b2aac821958fb9de91a1137e48 Mon Sep 17 00:00:00 2001 From: alhudz Date: Wed, 10 Jun 2026 10:41:18 +0530 Subject: [PATCH 1/2] validate structuredQuery shape before json access in bundle decoder --- Firestore/core/src/bundle/bundle_serializer.cc | 11 +++++++---- .../test/unit/bundle/bundle_serializer_test.cc | 18 ++++++++++++++++++ 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/Firestore/core/src/bundle/bundle_serializer.cc b/Firestore/core/src/bundle/bundle_serializer.cc index 86942236791..6fd1a54d185 100644 --- a/Firestore/core/src/bundle/bundle_serializer.cc +++ b/Firestore/core/src/bundle/bundle_serializer.cc @@ -145,6 +145,10 @@ void DecodeCollectionSource(JsonReader& reader, const json& from_json, ResourcePath& parent, std::string& group) { + if (!from_json.is_array()) { + reader.Fail("'from' clause is not an array as expected."); + return; + } const auto& from = from_json.get_ref&>(); if (from.size() != 1) { reader.Fail( @@ -274,10 +278,9 @@ int32_t DecodeLimit(JsonReader& reader, const json& query) { // "limit" can be encoded as integer or "{"value": integer}". if (limit_object.is_number_integer()) { return limit_object.get(); - } else if (limit_object.is_object()) { - if (limit_object.at("value").is_number_integer()) { - return limit_object.at("value").get(); - } + } else if (limit_object.is_object() && limit_object.contains("value") && + limit_object.at("value").is_number_integer()) { + return limit_object.at("value").get(); } reader.Fail("'limit' is not encoded as a valid integer"); return limit; diff --git a/Firestore/core/test/unit/bundle/bundle_serializer_test.cc b/Firestore/core/test/unit/bundle/bundle_serializer_test.cc index 72d788c7832..5d903b649fb 100644 --- a/Firestore/core/test/unit/bundle/bundle_serializer_test.cc +++ b/Firestore/core/test/unit/bundle/bundle_serializer_test.cc @@ -698,6 +698,24 @@ TEST_F(BundleSerializerTest, DecodeQueriesFromOtherProjectsFails) { } } +TEST_F(BundleSerializerTest, DecodeFromClauseWithWrongTypeFails) { + std::string json_string = NamedQueryJsonString(testutil::Query("colls")); + json parsed = Parse(json_string); + parsed["bundledQuery"]["structuredQuery"]["from"] = "colls"; + JsonReader reader; + bundle_serializer.DecodeNamedQuery(reader, parsed); + EXPECT_NOT_OK(reader.status()); +} + +TEST_F(BundleSerializerTest, DecodeLimitObjectWithoutValueFails) { + std::string json_string = NamedQueryJsonString(testutil::Query("colls")); + json parsed = Parse(json_string); + parsed["bundledQuery"]["structuredQuery"]["limit"] = json::object(); + JsonReader reader; + bundle_serializer.DecodeNamedQuery(reader, parsed); + EXPECT_NOT_OK(reader.status()); +} + TEST_F(BundleSerializerTest, DecodesCollectionGroupQuery) { core::Query original = testutil::CollectionGroupQuery("bundles/docs/colls"); VerifyNamedQueryRoundtrip(original); From a9065bd7ce5d932c04c5bb90e2b901fffeea3733 Mon Sep 17 00:00:00 2001 From: Alhuda Khan Date: Wed, 8 Jul 2026 15:52:31 +0530 Subject: [PATCH 2/2] Use find to look up limit value in single pass --- Firestore/core/src/bundle/bundle_serializer.cc | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Firestore/core/src/bundle/bundle_serializer.cc b/Firestore/core/src/bundle/bundle_serializer.cc index 6fd1a54d185..edbdc44172c 100644 --- a/Firestore/core/src/bundle/bundle_serializer.cc +++ b/Firestore/core/src/bundle/bundle_serializer.cc @@ -278,9 +278,11 @@ int32_t DecodeLimit(JsonReader& reader, const json& query) { // "limit" can be encoded as integer or "{"value": integer}". if (limit_object.is_number_integer()) { return limit_object.get(); - } else if (limit_object.is_object() && limit_object.contains("value") && - limit_object.at("value").is_number_integer()) { - return limit_object.at("value").get(); + } else if (limit_object.is_object()) { + auto value = limit_object.find("value"); + if (value != limit_object.end() && value->is_number_integer()) { + return value->get(); + } } reader.Fail("'limit' is not encoded as a valid integer"); return limit;