Skip to content
Open
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
3 changes: 3 additions & 0 deletions src/cpp/src/internal/error.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,9 @@ std::string_view MyErrorDomain::MessageFor(const score::result::ErrorCode& code)
case ErrorCode::InvalidValueType:
msg = "Invalid value type";
break;
case ErrorCode::KeyTooLong:
msg = "Key exceeds the maximum allowed length";
break;
default:
msg = "Unknown Error!";
break;
Expand Down
3 changes: 3 additions & 0 deletions src/cpp/src/internal/error.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,9 @@ enum class ErrorCode : score::result::ErrorCode

/* Invalid value type*/
InvalidValueType,

/* Key exceeds the maximum allowed length*/
KeyTooLong,
};

class MyErrorDomain final : public score::result::ErrorDomain
Expand Down
33 changes: 33 additions & 0 deletions src/cpp/src/kvs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,19 @@ using namespace std;
namespace score::mw::per::kvs
{

namespace
{

/* comp_req__kvs__key_length: single source of truth for the key-length rule,
shared by all paths that ingest keys (set_value, parse_json_data).
std::string_view::length() counts bytes, matching the byte-based requirement. */
bool is_key_length_valid(std::string_view key)
{
return key.length() <= KVS_MAX_KEY_LENGTH_BYTES;
}

} // namespace

/*********************** KVS Implementation *********************/
Kvs::Kvs()
: filesystem(std::make_unique<score::filesystem::Filesystem>(
Expand Down Expand Up @@ -108,6 +121,18 @@ score::Result<std::unordered_map<std::string, KvsValue>> Kvs::parse_json_data(co
auto sv = element.first.GetAsStringView();
std::string key(sv.data(), sv.size());

/* comp_req__kvs__key_length: a stored file must satisfy the key-length
limit; an over-length key means the file is invalid/corrupted. Halt
loading with an error rather than silently dropping data. */
if (!is_key_length_valid(key))
{
logger->LogError() << "Key length " << key.length() << " exceeds maximum allowed "
<< KVS_MAX_KEY_LENGTH_BYTES << " bytes while loading";
result = score::MakeUnexpected(ErrorCode::KeyTooLong);
error = true;
break;
}

auto conv = any_to_kvsvalue(element.second);
if (!conv)
{
Expand Down Expand Up @@ -432,6 +457,14 @@ score::Result<bool> Kvs::is_value_default(const std::string_view key) const
/* Set the value for a key*/
score::ResultBlank Kvs::set_value(const std::string_view key, const KvsValue& value)
{
/* comp_req__kvs__key_length: reject keys that exceed the maximum length. */
if (!is_key_length_valid(key))
{
logger->LogError() << "Key length " << key.length() << " exceeds maximum allowed "
<< KVS_MAX_KEY_LENGTH_BYTES << " bytes";
return score::MakeUnexpected(ErrorCode::KeyTooLong);
}

score::ResultBlank result = score::MakeUnexpected(ErrorCode::UnmappedError);
std::unique_lock<std::mutex> lock(kvs_mutex, std::try_to_lock);
if (lock.owns_lock())
Expand Down
10 changes: 9 additions & 1 deletion src/cpp/src/kvs.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@
namespace score::mw::per::kvs
{

/* comp_req__kvs__key_length: Maximum allowed key length in bytes. */
constexpr std::size_t KVS_MAX_KEY_LENGTH_BYTES = 32U;

struct InstanceId
{
size_t id;
Expand Down Expand Up @@ -262,13 +265,18 @@ class Kvs final
/**
* @brief Stores a key-value pair in the key-value store.
*
* Features:
* - comp_req__kvs__key_length: The key length is limited to
* KVS_MAX_KEY_LENGTH_BYTES (32) bytes.
*
* @param key The key associated with the value to be stored.
* It is represented as a string view to avoid unnecessary copying.
* @param value The value to be stored, represented as a KvsValue object.
*
* @return A score::Result object that indicates the success or failure of the operation.
* - On success: Returns a blank score::Result.
* - On failure: Returns an ErrorCode describing the error.
* - On failure: Returns ErrorCode::KeyTooLong if the key exceeds
* KVS_MAX_KEY_LENGTH_BYTES, or another ErrorCode describing the error.
*/
score::ResultBlank set_value(const std::string_view key, const KvsValue& value);

Expand Down
59 changes: 59 additions & 0 deletions src/cpp/tests/test_kvs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,45 @@ TEST(kvs_TEST, parse_json_data_failure)
cleanup_environment();
}

TEST(kvs_TEST, parse_json_data_rejects_over_length_key)
{
/* comp_req__kvs__key_length: an over-length key found while loading means the file
is invalid/corrupted; loading must halt with an error, not silently drop data. */
prepare_environment();

auto kvs =
Kvs::open(InstanceId(instance_id), OpenNeedDefaults::Optional, OpenNeedKvs::Optional, std::string(data_dir));
ASSERT_TRUE(kvs);

const std::string valid_key(KVS_MAX_KEY_LENGTH_BYTES, 'a');
const std::string too_long_key(KVS_MAX_KEY_LENGTH_BYTES + 1U, 'b');

auto mock_parser = std::make_unique<score::json::IJsonParserMock>();
score::json::Object obj;

score::json::Object valid_inner;
valid_inner.emplace("t", score::json::Any(std::string("i32")));
valid_inner.emplace("v", score::json::Any(42));
obj.emplace(valid_key, score::json::Any(std::move(valid_inner)));

score::json::Object long_inner;
long_inner.emplace("t", score::json::Any(std::string("i32")));
long_inner.emplace("v", score::json::Any(7));
obj.emplace(too_long_key, score::json::Any(std::move(long_inner)));

score::json::Any any_obj(std::move(obj));
EXPECT_CALL(*mock_parser, FromBuffer(::testing::_))
.WillOnce(::testing::Return(score::Result<score::json::Any>(std::move(any_obj))));

kvs->parser = std::move(mock_parser);

auto result = kvs->parse_json_data("data_not_used_in_mocking");
ASSERT_FALSE(result);
EXPECT_EQ(static_cast<ErrorCode>(*result.error()), ErrorCode::KeyTooLong);

cleanup_environment();
}

TEST(kvs_open_json, open_json_success)
{
prepare_environment();
Expand Down Expand Up @@ -552,6 +591,26 @@ TEST(kvs_set_value, set_value_success)
cleanup_environment();
}

TEST(kvs_set_value, set_value_key_too_long)
{
prepare_environment();
auto result = Kvs::open(instance_id, OpenNeedDefaults::Required, OpenNeedKvs::Required, std::string(data_dir));
ASSERT_TRUE(result);

/* A key of exactly KVS_MAX_KEY_LENGTH_BYTES bytes is accepted (inclusive limit) */
std::string boundary_key(KVS_MAX_KEY_LENGTH_BYTES, 'k');
auto boundary_result = result.value().set_value(boundary_key, KvsValue(3.14));
EXPECT_TRUE(boundary_result);

/* A key of KVS_MAX_KEY_LENGTH_BYTES + 1 bytes is rejected with KeyTooLong */
std::string too_long_key(KVS_MAX_KEY_LENGTH_BYTES + 1U, 'k');
auto set_value_result = result.value().set_value(too_long_key, KvsValue(3.14));
EXPECT_FALSE(set_value_result);
EXPECT_EQ(static_cast<ErrorCode>(*set_value_result.error()), ErrorCode::KeyTooLong);

cleanup_environment();
}

TEST(kvs_set_value, set_value_failure)
{
prepare_environment();
Expand Down
1 change: 1 addition & 0 deletions src/cpp/tests/test_kvs_error.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ TEST(kvs_MessageFor, MessageFor)
{ErrorCode::ConversionFailed, "Conversion failed"},
{ErrorCode::MutexLockFailed, "Mutex failed"},
{ErrorCode::InvalidValueType, "Invalid value type"},
{ErrorCode::KeyTooLong, "Key exceeds the maximum allowed length"},
};
for (const auto& test : test_cases)
{
Expand Down
Loading