userver: /data/code/userver/libraries/protobuf/tests/json/debug_string_any_test.cpp Source File
Loading...
Searching...
No Matches
debug_string_any_test.cpp
1#include <string>
2#include <string_view>
3
4#include <google/protobuf/any.pb.h>
5#include <gtest/gtest.h>
6
7#include <protobuf/json/impl/proto_message_visitor.hpp>
8#include <protobuf/json/impl/string_writer.hpp>
9
10#include <userver/formats/json/serialize.hpp>
11#include <userver/protobuf/json/convert.hpp>
12#include <userver/utest/assert_macros.hpp>
13
14#include "proto_json/messages.pb.h"
15#include "utils.hpp"
16
17USERVER_NAMESPACE_BEGIN
18
19namespace protobuf::json::tests {
20
21namespace {
22
23constexpr std::string_view kSecret = "sensitive-payload";
24constexpr std::string_view kSecretBase64 = "c2Vuc2l0aXZlLXBheWxvYWQ=";
25constexpr std::size_t kLimit = 1 << 20;
26
27void ExpectNoSecret(std::string_view text) {
28 EXPECT_EQ(text.find(kSecret), std::string_view::npos);
29 EXPECT_EQ(text.find(kSecretBase64), std::string_view::npos);
30}
31
32::google::protobuf::Any MakeUnresolvedAny(
33 std::string_view type_url = "type.googleapis.com/testing.unregistered.Message"
34) {
35 ::google::protobuf::Any any;
36 any.set_type_url(std::string{type_url});
37 any.set_value(std::string{kSecret});
38 return any;
39}
40
41} // namespace
42
43TEST(DebugStringAny, RawAnyOmitsValueWhenRedacted) {
44 protobuf::json::impl::StringWriter string_writer{kLimit};
45 protobuf::json::impl::ProtoMessageVisitor visitor{string_writer};
46 visitor.SetPreserveProtoFieldNames(true);
47 visitor.SetExpandAny(false);
48 visitor.SetRedactDebugString(true);
49 visitor(MakeUnresolvedAny());
50
51 const auto json = string_writer.GetString();
52 EXPECT_EQ(json, R"({"type_url":"type.googleapis.com/testing.unregistered.Message"})");
53 ExpectNoSecret(json);
54}
55
56TEST(DebugStringAny, UnknownTypeOmitsValue) {
57 const auto any = MakeUnresolvedAny();
58 const auto json = MessageToDebugString(any, kLimit);
59
60 EXPECT_EQ(json, R"({"@type":"type.googleapis.com/testing.unregistered.Message","@error":"unresolved_any_type"})");
61 ExpectNoSecret(json);
62}
63
64TEST(DebugStringAny, KnownTypeIsExpanded) {
65 proto_json::messages::Int32Message payload;
66 payload.set_field1(1);
67 payload.set_field2(2);
68 payload.set_field3(3);
69
70 ::google::protobuf::Any any;
71 any.PackFrom(payload);
72
73 const auto json = MessageToDebugString(any, kLimit);
74 EXPECT_EQ(
75 json,
76 R"({"@type":"type.googleapis.com/proto_json.messages.Int32Message","field1":1,"field2":2,"field3":3})"
77 );
78}
79
80TEST(DebugStringAny, EmptyAny) {
81 const ::google::protobuf::Any any;
82 EXPECT_EQ(MessageToDebugString(any, kLimit), "{}");
83}
84
85TEST(DebugStringAny, NestedUnresolvedAny) {
86 ::google::protobuf::Any outer;
87 outer.PackFrom(MakeUnresolvedAny());
88
89 const auto json = MessageToDebugString(outer, kLimit);
90 EXPECT_EQ(
91 json,
92 R"({"@type":"type.googleapis.com/google.protobuf.Any","value":{"@type":"type.googleapis.com/testing.unregistered.Message","@error":"unresolved_any_type"}})"
93 );
94 ExpectNoSecret(json);
95}
96
97TEST(DebugStringAny, NeighborsRepeatedAndMap) {
98 proto_json::messages::AnyContainer message;
99 message.set_keep("keep");
100 *message.mutable_any() = MakeUnresolvedAny();
101
102 *message.add_items() = MakeUnresolvedAny("example.com/Unknown");
103
104 proto_json::messages::Int32Message known;
105 known.set_field1(1);
106 known.set_field2(2);
107 known.set_field3(3);
108 message.add_items()->PackFrom(known);
109
110 (*message.mutable_entries())["unknown"] = MakeUnresolvedAny("example.com/Unknown");
111
112 const auto json = MessageToDebugString(message, kLimit);
113 EXPECT_EQ(
114 json,
115 R"({"keep":"keep","any":{"@type":"type.googleapis.com/testing.unregistered.Message","@error":"unresolved_any_type"},"items":[{"@type":"example.com/Unknown","@error":"unresolved_any_type"},{"@type":"type.googleapis.com/proto_json.messages.Int32Message","field1":1,"field2":2,"field3":3}],"entries":{"unknown":{"@type":"example.com/Unknown","@error":"unresolved_any_type"}}})"
116 );
117 ExpectNoSecret(json);
118}
119
120TEST(DebugStringAny, EscapedTypeUrl) {
121 const std::string type_url = "example.com/\"quoted\"\nMessage";
122 const auto json = formats::json::FromString(MessageToDebugString(MakeUnresolvedAny(type_url), kLimit));
123
124 EXPECT_EQ(json["@type"].As<std::string>(), type_url);
125 EXPECT_EQ(json["@error"].As<std::string>(), "unresolved_any_type");
126 EXPECT_FALSE(json.HasMember("value"));
127}
128
129TEST(DebugStringAny, MissingTypeUrlDoesNotLeakValue) {
130 proto_json::messages::AnyMessage message;
131 message.mutable_field1()->set_value(std::string{kSecret});
132
133 try {
134 const auto json = MessageToDebugString(message, kLimit);
135 ADD_FAILURE() << "Should throw 'PrintError', got: " << json;
136 } catch (const PrintError& error) {
137 EXPECT_EQ(error.GetErrorInfo().GetCode(), PrintErrorCode::kInvalidValue);
138 EXPECT_EQ(error.GetErrorInfo().GetPath(), "field1");
139 ExpectNoSecret(error.what());
140 }
141}
142
143TEST(DebugStringAny, CorruptKnownPayloadDoesNotLeakValue) {
144 proto_json::messages::AnyMessage message;
145 message.mutable_field1()->set_type_url("type.googleapis.com/proto_json.messages.Int32Message");
146 message.mutable_field1()->set_value(std::string{"\x80"} + std::string{kSecret});
147
148 const auto json = MessageToDebugString(message, kLimit);
149 EXPECT_EQ(
150 json,
151 R"({"field1":{"@type":"type.googleapis.com/proto_json.messages.Int32Message","@error":"invalid_payload"}})"
152 );
153 ExpectNoSecret(json);
154}
155
156TEST(DebugStringAny, StrictJsonStillFailsOnCorruptPayload) {
157 proto_json::messages::AnyMessage message;
158 message.mutable_field1()->set_type_url("type.googleapis.com/proto_json.messages.Int32Message");
159 message.mutable_field1()->set_value(std::string{"\x80"} + std::string{kSecret});
160
161 EXPECT_PRINT_ERROR((void)MessageToJson(message, {}), PrintErrorCode::kInvalidValue, "field1");
162}
163
164TEST(DebugStringAny, TruncationDoesNotEmitValue) {
165 const auto json = MessageToDebugString(MakeUnresolvedAny(), 16);
166 ExpectNoSecret(json);
167}
168
169TEST(DebugStringAny, StrictJsonStillFails) {
170 proto_json::messages::AnyMessage message;
171 *message.mutable_field1() = MakeUnresolvedAny();
172
173 EXPECT_PRINT_ERROR((void)MessageToJson(message, {}), PrintErrorCode::kInvalidValue, "field1");
174}
175
176} // namespace protobuf::json::tests
177
178USERVER_NAMESPACE_END