diff --git a/src/google/protobuf/text_format.cc b/src/google/protobuf/text_format.cc index 1e528f0cda5e9..9c0bebd435e0f 100644 --- a/src/google/protobuf/text_format.cc +++ b/src/google/protobuf/text_format.cc @@ -3222,6 +3222,10 @@ TextFormat::RedactionState TextFormat::IsOptionSensitive( : reflection->GetEnumValue(opts, option); const EnumValueDescriptor* option_value = option->enum_type()->FindValueByNumber(enum_val); + if (option_value == nullptr) { + // Ignore values we don't know about. + continue; + } if (option_value->options().debug_redact()) { return TextFormat::RedactionState{true, false}; } diff --git a/src/google/protobuf/text_format_unittest.cc b/src/google/protobuf/text_format_unittest.cc index 7fe18eb904e55..d9393a9732948 100644 --- a/src/google/protobuf/text_format_unittest.cc +++ b/src/google/protobuf/text_format_unittest.cc @@ -40,6 +40,7 @@ #include "absl/strings/str_replace.h" #include "absl/strings/substitute.h" #include "google/protobuf/descriptor.h" +#include "google/protobuf/dynamic_message.h" #include "google/protobuf/io/tokenizer.h" #include "google/protobuf/io/zero_copy_stream_impl.h" #include "google/protobuf/io/zero_copy_stream_impl_lite.h" @@ -295,6 +296,41 @@ TEST_F(TextFormatTest, ShortFormat) { value_replacement, kTextMarkerRegex))); } +TEST_F(TextFormatTest, RedactionWithUndeclaredEnumOption) { + FileDescriptorProto file; + file.set_name("evil.proto"); + file.set_package("evil"); + file.set_edition(Edition::EDITION_2024); + + DescriptorProto* msg = file.add_message_type(); + msg->set_name("M"); + + FieldDescriptorProto* fld = msg->add_field(); + fld->set_name("x"); + fld->set_number(1); + fld->set_type(FieldDescriptorProto::TYPE_INT32); + + // Set the compiled-in message-typed extension on the field's options, + // with an undeclared open-enum value. + FieldOptions* opts = fld->mutable_options(); + proto2_unittest::TestNestedMessageEnum* nested_enum = + opts->MutableExtension(proto2_unittest::test_nested_message_enum); + nested_enum->add_direct_enum( + static_cast(999)); + + // Use the generated pool underlay so it can load the extension. + DescriptorPool pool(DescriptorPool::generated_pool()); + const FileDescriptor* fd = pool.BuildFile(file); + ASSERT_NE(fd, nullptr); + const Descriptor* d = fd->message_type(0); + + DynamicMessageFactory factory(&pool); + std::unique_ptr m(factory.GetPrototype(d)->New()); + ASSERT_TRUE(TextFormat::ParseFromString("x: 42", m.get())); + // The field should be printed fine. + EXPECT_THAT(m->DebugString(), HasSubstr("x: 42")); +} + TEST_F(TextFormatTest, Utf8Format) { unittest::RedactedFields proto; unittest::TestNestedMessageRedaction redacted_nested_proto;