From d30768c9b9816957af2d981d26b6376c18a0eab6 Mon Sep 17 00:00:00 2001 From: Minh Vu Date: Sat, 25 Jul 2026 17:15:49 +0200 Subject: [PATCH 1/2] fix: reject trailing characters in commit properties --- src/iceberg/test/table_metadata_builder_test.cc | 16 ++++++++++++++++ src/iceberg/util/property_util.cc | 7 +++++-- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/iceberg/test/table_metadata_builder_test.cc b/src/iceberg/test/table_metadata_builder_test.cc index 5fd970d62..0d10722bb 100644 --- a/src/iceberg/test/table_metadata_builder_test.cc +++ b/src/iceberg/test/table_metadata_builder_test.cc @@ -200,6 +200,22 @@ TEST(TableMetadataTest, InvalidProperties) { "Table property {} must have non negative integer value, but got {}", TableProperties::kCommitNumRetries.key(), -1))); } + + { + // Commit properties must contain only an integer, not a valid integer prefix. + ICEBERG_UNWRAP_OR_FAIL(auto schema, CreateDisorderedSchema()); + for (const auto& value : {"4x", "1.5"}) { + std::unordered_map invalid_commit_properties = { + {TableProperties::kCommitNumRetries.key(), value}}; + + auto res = TableMetadata::Make(*schema, *spec, *order, "s3://bucket/test", + invalid_commit_properties); + EXPECT_THAT(res, IsError(ErrorKind::kValidationFailed)); + EXPECT_THAT(res, HasErrorMessage(std::format( + "Table property {} must have integer value, but got {}", + TableProperties::kCommitNumRetries.key(), value))); + } + } } // test construction of TableMetadataBuilder diff --git a/src/iceberg/util/property_util.cc b/src/iceberg/util/property_util.cc index 636083fdd..bf0b4fc87 100644 --- a/src/iceberg/util/property_util.cc +++ b/src/iceberg/util/property_util.cc @@ -30,14 +30,17 @@ Status PropertyUtil::ValidateCommitProperties( for (const auto& property : TableProperties::commit_properties()) { if (auto it = properties.find(property); it != properties.end()) { int32_t parsed; - auto [ptr, ec] = std::from_chars(it->second.data(), - it->second.data() + it->second.size(), parsed); + const auto* end = it->second.data() + it->second.size(); + auto [ptr, ec] = std::from_chars(it->second.data(), end, parsed); if (ec == std::errc::invalid_argument) { return ValidationFailed("Table property {} must have integer value, but got {}", property, it->second); } else if (ec == std::errc::result_out_of_range) { return ValidationFailed("Table property {} value out of range {}", property, it->second); + } else if (ptr != end) { + return ValidationFailed("Table property {} must have integer value, but got {}", + property, it->second); } if (parsed < 0) { return ValidationFailed( From 9a1bd91838da00e30e3d8a795f3ee23d2e8ccdaf Mon Sep 17 00:00:00 2001 From: Minh Vu Date: Wed, 29 Jul 2026 12:14:06 +0200 Subject: [PATCH 2/2] refactor: use StringUtils for commit property parsing --- src/iceberg/util/property_util.cc | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/src/iceberg/util/property_util.cc b/src/iceberg/util/property_util.cc index bf0b4fc87..32d9e3ba1 100644 --- a/src/iceberg/util/property_util.cc +++ b/src/iceberg/util/property_util.cc @@ -19,9 +19,10 @@ #include "iceberg/util/property_util.h" -#include +#include #include "iceberg/table_properties.h" +#include "iceberg/util/string_util.h" namespace iceberg { @@ -29,19 +30,12 @@ Status PropertyUtil::ValidateCommitProperties( const std::unordered_map& properties) { for (const auto& property : TableProperties::commit_properties()) { if (auto it = properties.find(property); it != properties.end()) { - int32_t parsed; - const auto* end = it->second.data() + it->second.size(); - auto [ptr, ec] = std::from_chars(it->second.data(), end, parsed); - if (ec == std::errc::invalid_argument) { - return ValidationFailed("Table property {} must have integer value, but got {}", - property, it->second); - } else if (ec == std::errc::result_out_of_range) { - return ValidationFailed("Table property {} value out of range {}", property, - it->second); - } else if (ptr != end) { + auto parsed_result = StringUtils::ParseNumber(it->second); + if (!parsed_result) { return ValidationFailed("Table property {} must have integer value, but got {}", property, it->second); } + const auto parsed = *parsed_result; if (parsed < 0) { return ValidationFailed( "Table property {} must have non negative integer value, but got {}",