Răsfoiți Sursa

fix(indexes): reject out-of-range limit instead of silently truncating

fszontagh 1 lună în urmă
părinte
comite
a2dea403f0
2 a modificat fișierele cu 30 adăugiri și 2 ștergeri
  1. 3 2
      src/handlers/indexes.cpp
  2. 27 0
      tests/test_api_integration.cpp

+ 3 - 2
src/handlers/indexes.cpp

@@ -97,9 +97,10 @@ void registerIndexRoutes(ApiServer& s) {
 
         uint32_t limit = 100;
         if (req.has_param("limit")) {
-            limit = (uint32_t)std::strtoul(req.get_param_value("limit").c_str(), nullptr, 10);
-            if (limit == 0 || limit > 1000)
+            unsigned long wide = std::strtoul(req.get_param_value("limit").c_str(), nullptr, 10);
+            if (wide == 0 || wide > 1000)
                 throw ApiError(ErrCode::Unprocessable, "validation", "limit must be 1..1000");
+            limit = (uint32_t)wide;
         }
         const bool ascending = !(req.has_param("order") && req.get_param_value("order") == "desc");
 

+ 27 - 0
tests/test_api_integration.cpp

@@ -962,6 +962,33 @@ TEST_F(ApiFixture, IndexValuesEmptyForUnindexedField) {
     EXPECT_TRUE(nlohmann::json::parse(r->body)["values"].empty());
 }
 
+TEST_F(ApiFixture, IndexValuesRejectsOutOfRangeLimit) {
+    auto c = admin();
+    std::string base = "/api/v1/projects/" + project_ + "/collections";
+    ASSERT_EQ(c.Post(base.c_str(), nlohmann::json{{"name","limitrange"},{"kind","json"}}.dump(),
+                     "application/json")->status, 201);
+    ASSERT_EQ(c.Post((base + "/limitrange/indexes").c_str(),
+                     nlohmann::json{{"field","status"}}.dump(), "application/json")->status, 201);
+
+    // 2^32 + 100: fits cleanly in the unsigned long strtoul returns, but is far
+    // out of the uint32_t range once narrowed. Must be rejected, not silently
+    // truncated down to a small in-range value.
+    auto overflow = c.Get((base + "/limitrange/indexes/status/values?limit=4294967396").c_str());
+    ASSERT_TRUE(overflow); EXPECT_EQ(overflow->status, 422);
+
+    // Existing behavior that must be preserved: non-numeric and empty-string
+    // limits still 422 (strtoul yields 0, caught by the ==0 check), and the
+    // default of 100 applies when the parameter is absent.
+    auto nonNumeric = c.Get((base + "/limitrange/indexes/status/values?limit=abc").c_str());
+    ASSERT_TRUE(nonNumeric); EXPECT_EQ(nonNumeric->status, 422);
+
+    auto empty = c.Get((base + "/limitrange/indexes/status/values?limit=").c_str());
+    ASSERT_TRUE(empty); EXPECT_EQ(empty->status, 422);
+
+    auto absent = c.Get((base + "/limitrange/indexes/status/values").c_str());
+    ASSERT_TRUE(absent); EXPECT_EQ(absent->status, 200);
+}
+
 TEST_F(ApiFixture, UniqueIndexRejectsExistingDuplicates) {
     auto c = admin();
     std::string base = "/api/v1/projects/" + project_ + "/collections";