bej_decoder: Check decoded string length

We need to check the bejString value lengths to prevent heap
buffer overflow

Tested:
Unit tested

Change-Id: Ie6a014fbffeb31f111bfbae331db197b3fb2f2ca
Signed-off-by: Kasun Athukorala <kasunath@google.com>
diff --git a/include/libbej/bej_decoder_core.h b/include/libbej/bej_decoder_core.h
index 919c396..758fde0 100644
--- a/include/libbej/bej_decoder_core.h
+++ b/include/libbej/bej_decoder_core.h
@@ -109,7 +109,7 @@
      * @brief Calls when a String property is found.
      */
     int (*callbackString)(const char* propertyName, const char* value,
-                          void* dataPtr);
+                          size_t length, void* dataPtr);
 
     /**
      * @brief Calls when a Real value property is found.
diff --git a/src/bej_decoder_core.c b/src/bej_decoder_core.c
index e240d99..2536ed1 100644
--- a/src/bej_decoder_core.c
+++ b/src/bej_decoder_core.c
@@ -546,9 +546,10 @@
     }
     else
     {
-        RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackString,
-                                  propName, (const char*)(params->sflv.value),
-                                  params->callbacksDataPtr);
+        RETURN_IF_CALLBACK_IERROR(
+            params->decodedCallback->callbackString, propName,
+            (const char*)(params->sflv.value), params->sflv.valueLength,
+            params->callbacksDataPtr);
     }
     params->state.encodedStreamOffset = params->sflv.valueEndOffset;
     return bejProcessEnding(params, /*canBeEmpty=*/false);
diff --git a/src/bej_decoder_json.cpp b/src/bej_decoder_json.cpp
index 9fceeba..95e241f 100644
--- a/src/bej_decoder_json.cpp
+++ b/src/bej_decoder_json.cpp
@@ -1,5 +1,9 @@
 #include "bej_decoder_json.hpp"
 
+#include <string.h>
+
+#define MAX_BEJ_STRING_LEN 65536
+
 namespace libbej
 {
 
@@ -172,17 +176,29 @@
  *
  * @param[in] propertyName - a NULL terminated string.
  * @param[in] value - a NULL terminated string.
+ * @param[in] length - length of the string.
  * @param[in] dataPtr - pointing to a valid BejJsonParam struct.
  * @return 0 if successful.
  */
 static int callbackString(const char* propertyName, const char* value,
-                          void* dataPtr)
+                          size_t length, void* dataPtr)
 {
+    if ((length > MAX_BEJ_STRING_LEN) ||
+        (strnlen(value, length) != (length - 1)))
+    {
+        fprintf(stderr,
+                "Incorrect BEJ string length %zu or it exceeds maximum %u.\n",
+                (length - 1), MAX_BEJ_STRING_LEN);
+        return bejErrorInvalidSize;
+    }
     struct BejJsonParam* params =
         reinterpret_cast<struct BejJsonParam*>(dataPtr);
     addPropertyNameToOutput(params, propertyName);
     params->output->push_back('\"');
-    params->output->append(value);
+    if (length > 0)
+    {
+        params->output->append(value, length - 1);
+    }
     params->output->push_back('\"');
     *params->isPrevAnnotated = false;
     return 0;
diff --git a/test/bej_decoder_test.cpp b/test/bej_decoder_test.cpp
index 19cdb92..7869dfb 100644
--- a/test/bej_decoder_test.cpp
+++ b/test/bej_decoder_test.cpp
@@ -202,6 +202,36 @@
                 bejErrorInvalidSize);
 }
 
+TEST(BejDecoderSecurityTest, StringTooLong)
+{
+    auto inputsOrErr = loadInputs(dummySimpleTestFiles);
+    ASSERT_TRUE(inputsOrErr);
+
+    BejDictionaries dictionaries = {
+        .schemaDictionary = inputsOrErr->schemaDictionary,
+        .annotationDictionary = inputsOrErr->annotationDictionary,
+        .errorDictionary = inputsOrErr->errorDictionary,
+    };
+
+    auto root = std::make_unique<RedfishPropertyParent>();
+    bejTreeInitSet(root.get(), "DummySimple");
+
+    // Create a string with a length greater than MAX_BEJ_STRING_LEN (65536).
+    std::string longString(65537, 'A');
+
+    auto stringProp = std::make_unique<RedfishPropertyLeafString>();
+    bejTreeAddString(root.get(), stringProp.get(), "Id", longString.c_str());
+
+    libbej::BejEncoderJson encoder;
+    encoder.encode(&dictionaries, bejMajorSchemaClass, root.get());
+    std::vector<uint8_t> outputBuffer = encoder.getOutput();
+
+    // The decoder should return an error because the string is too long.
+    BejDecoderJson decoder;
+    EXPECT_THAT(decoder.decode(dictionaries, std::span(outputBuffer)),
+                bejErrorInvalidSize);
+}
+
 /**
  * TODO: Add more test cases.
  * - Test Enums inside array elemets