1 // Copyright 2021 gRPC authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "src/core/lib/transport/metadata_batch.h"
16
17 #include <grpc/support/port_platform.h>
18 #include <string.h>
19
20 #include <algorithm>
21 #include <string>
22
23 #include "absl/base/no_destructor.h"
24 #include "absl/container/flat_hash_set.h"
25 #include "absl/strings/escaping.h"
26 #include "absl/strings/match.h"
27 #include "absl/strings/str_cat.h"
28 #include "absl/strings/str_format.h"
29 #include "src/core/lib/transport/timeout_encoding.h"
30
31 namespace grpc_core {
32 namespace metadata_detail {
33
Add(absl::string_view key,absl::string_view value)34 void DebugStringBuilder::Add(absl::string_view key, absl::string_view value) {
35 if (!out_.empty()) out_.append(", ");
36 absl::StrAppend(&out_, absl::CEscape(key), ": ", absl::CEscape(value));
37 }
38
AddAfterRedaction(absl::string_view key,absl::string_view value)39 void DebugStringBuilder::AddAfterRedaction(absl::string_view key,
40 absl::string_view value) {
41 if (IsAllowListed(key)) {
42 Add(key, value);
43 } else {
44 // If the type of metadata is not in the allow list, we want to prevent it
45 // from getting logged. Custom metadata types may have sensitive information
46 // that should never be logged. Programatically, we have no way to know
47 // which data is sensitive and which is not. So we redact all values which
48 // are not in the allow list.
49 Add(key,
50 absl::StrCat(value.size(), " bytes redacted for security reasons."));
51 }
52 }
53
IsAllowListed(const absl::string_view key) const54 bool DebugStringBuilder::IsAllowListed(const absl::string_view key) const {
55 // We have intentionally not allowed for any way to add to the allow list at
56 // run time, (using a flag or some other setting) because such workarounds
57 // may lead to security issues.
58 static const absl::NoDestructor<absl::flat_hash_set<std::string>> allow_list(
59 [] {
60 absl::flat_hash_set<std::string> allow_list;
61 // go/keep-sorted start
62 allow_list.insert(std::string(ContentTypeMetadata::key()));
63 allow_list.insert(std::string(EndpointLoadMetricsBinMetadata::key()));
64 allow_list.insert(std::string(GrpcAcceptEncodingMetadata::key()));
65 allow_list.insert(std::string(GrpcEncodingMetadata::key()));
66 allow_list.insert(std::string(GrpcInternalEncodingRequest::key()));
67 allow_list.insert(std::string(GrpcLbClientStatsMetadata::key()));
68 allow_list.insert(std::string(GrpcMessageMetadata::key()));
69 allow_list.insert(std::string(GrpcPreviousRpcAttemptsMetadata::key()));
70 allow_list.insert(std::string(GrpcRetryPushbackMsMetadata::key()));
71 allow_list.insert(std::string(GrpcServerStatsBinMetadata::key()));
72 allow_list.insert(std::string(GrpcStatusMetadata::key()));
73 allow_list.insert(std::string(GrpcTagsBinMetadata::key()));
74 allow_list.insert(std::string(GrpcTimeoutMetadata::key()));
75 allow_list.insert(std::string(GrpcTraceBinMetadata::key()));
76 allow_list.insert(std::string(HostMetadata::key()));
77 allow_list.insert(std::string(HttpAuthorityMetadata::key()));
78 allow_list.insert(std::string(HttpMethodMetadata::key()));
79 allow_list.insert(std::string(HttpPathMetadata::key()));
80 allow_list.insert(std::string(HttpSchemeMetadata::key()));
81 allow_list.insert(std::string(HttpStatusMetadata::key()));
82 allow_list.insert(std::string(LbCostBinMetadata::key()));
83 allow_list.insert(std::string(LbTokenMetadata::key()));
84 allow_list.insert(std::string(TeMetadata::key()));
85 allow_list.insert(std::string(UserAgentMetadata::key()));
86 allow_list.insert(std::string(W3CTraceParentMetadata::key()));
87 allow_list.insert(std::string(XEnvoyPeerMetadata::key()));
88 // go/keep-sorted end
89 // go/keep-sorted start
90 allow_list.insert(std::string(GrpcCallWasCancelled::DebugKey()));
91 allow_list.insert(std::string(GrpcRegisteredMethod::DebugKey()));
92 allow_list.insert(std::string(GrpcStatusContext::DebugKey()));
93 allow_list.insert(std::string(GrpcStatusFromWire::DebugKey()));
94 allow_list.insert(std::string(GrpcStreamNetworkState::DebugKey()));
95 allow_list.insert(std::string(GrpcTarPit::DebugKey()));
96 allow_list.insert(std::string(GrpcTrailersOnly::DebugKey()));
97 allow_list.insert(std::string(PeerString::DebugKey()));
98 allow_list.insert(std::string(WaitForReady::DebugKey()));
99 // go/keep-sorted end
100 return allow_list;
101 }());
102 return allow_list->contains(key);
103 }
104
Append(absl::string_view key,Slice value)105 void UnknownMap::Append(absl::string_view key, Slice value) {
106 unknown_.emplace_back(Slice::FromCopiedString(key), value.Ref());
107 }
108
Remove(absl::string_view key)109 void UnknownMap::Remove(absl::string_view key) {
110 unknown_.erase(std::remove_if(unknown_.begin(), unknown_.end(),
111 [key](const std::pair<Slice, Slice>& p) {
112 return p.first.as_string_view() == key;
113 }),
114 unknown_.end());
115 }
116
GetStringValue(absl::string_view key,std::string * backing) const117 absl::optional<absl::string_view> UnknownMap::GetStringValue(
118 absl::string_view key, std::string* backing) const {
119 absl::optional<absl::string_view> out;
120 for (const auto& p : unknown_) {
121 if (p.first.as_string_view() == key) {
122 if (!out.has_value()) {
123 out = p.second.as_string_view();
124 } else {
125 out = *backing = absl::StrCat(*out, ",", p.second.as_string_view());
126 }
127 }
128 }
129 return out;
130 }
131
132 } // namespace metadata_detail
133
ParseMemento(Slice value,bool,MetadataParseErrorFn)134 ContentTypeMetadata::MementoType ContentTypeMetadata::ParseMemento(
135 Slice value, bool, MetadataParseErrorFn /*on_error*/) {
136 auto out = kInvalid;
137 auto value_string = value.as_string_view();
138 if (value_string == "application/grpc") {
139 out = kApplicationGrpc;
140 } else if (absl::StartsWith(value_string, "application/grpc;")) {
141 out = kApplicationGrpc;
142 } else if (absl::StartsWith(value_string, "application/grpc+")) {
143 out = kApplicationGrpc;
144 } else if (value_string.empty()) {
145 out = kEmpty;
146 } else {
147 // We are intentionally not invoking on_error here since the spec is not
148 // clear on what the behavior should be here, so to avoid breaking anyone,
149 // we should continue to accept this.
150 }
151 return out;
152 }
153
Encode(ValueType x)154 StaticSlice ContentTypeMetadata::Encode(ValueType x) {
155 switch (x) {
156 case kEmpty:
157 return StaticSlice::FromStaticString("");
158 case kApplicationGrpc:
159 return StaticSlice::FromStaticString("application/grpc");
160 case kInvalid:
161 return StaticSlice::FromStaticString("application/grpc+unknown");
162 }
163 GPR_UNREACHABLE_CODE(
164 return StaticSlice::FromStaticString("unrepresentable value"));
165 }
166
DisplayValue(ValueType content_type)167 const char* ContentTypeMetadata::DisplayValue(ValueType content_type) {
168 switch (content_type) {
169 case ValueType::kApplicationGrpc:
170 return "application/grpc";
171 case ValueType::kEmpty:
172 return "";
173 default:
174 return "<discarded-invalid-value>";
175 }
176 }
177
ParseMemento(Slice value,bool,MetadataParseErrorFn on_error)178 GrpcTimeoutMetadata::MementoType GrpcTimeoutMetadata::ParseMemento(
179 Slice value, bool, MetadataParseErrorFn on_error) {
180 auto timeout = ParseTimeout(value);
181 if (!timeout.has_value()) {
182 on_error("invalid value", value);
183 return Duration::Infinity();
184 }
185 return *timeout;
186 }
187
MementoToValue(MementoType timeout)188 GrpcTimeoutMetadata::ValueType GrpcTimeoutMetadata::MementoToValue(
189 MementoType timeout) {
190 if (timeout == Duration::Infinity()) {
191 return Timestamp::InfFuture();
192 }
193 return Timestamp::Now() + timeout;
194 }
195
Encode(ValueType x)196 Slice GrpcTimeoutMetadata::Encode(ValueType x) {
197 return Timeout::FromDuration(x - Timestamp::Now()).Encode();
198 }
199
ParseMemento(Slice value,bool,MetadataParseErrorFn on_error)200 TeMetadata::MementoType TeMetadata::ParseMemento(
201 Slice value, bool, MetadataParseErrorFn on_error) {
202 auto out = kInvalid;
203 if (value == "trailers") {
204 out = kTrailers;
205 } else {
206 on_error("invalid value", value);
207 }
208 return out;
209 }
210
DisplayValue(ValueType te)211 const char* TeMetadata::DisplayValue(ValueType te) {
212 switch (te) {
213 case ValueType::kTrailers:
214 return "trailers";
215 default:
216 return "<discarded-invalid-value>";
217 }
218 }
219
Parse(absl::string_view value,MetadataParseErrorFn on_error)220 HttpSchemeMetadata::ValueType HttpSchemeMetadata::Parse(
221 absl::string_view value, MetadataParseErrorFn on_error) {
222 if (value == "http") {
223 return kHttp;
224 } else if (value == "https") {
225 return kHttps;
226 }
227 on_error("invalid value", Slice::FromCopiedBuffer(value));
228 return kInvalid;
229 }
230
Encode(ValueType x)231 StaticSlice HttpSchemeMetadata::Encode(ValueType x) {
232 switch (x) {
233 case kHttp:
234 return StaticSlice::FromStaticString("http");
235 case kHttps:
236 return StaticSlice::FromStaticString("https");
237 default:
238 abort();
239 }
240 }
241
EncodedSizeOfKey(HttpSchemeMetadata,HttpSchemeMetadata::ValueType x)242 size_t EncodedSizeOfKey(HttpSchemeMetadata, HttpSchemeMetadata::ValueType x) {
243 switch (x) {
244 case HttpSchemeMetadata::kHttp:
245 return 4;
246 case HttpSchemeMetadata::kHttps:
247 return 5;
248 default:
249 return 0;
250 }
251 }
252
DisplayValue(ValueType content_type)253 const char* HttpSchemeMetadata::DisplayValue(ValueType content_type) {
254 switch (content_type) {
255 case kHttp:
256 return "http";
257 case kHttps:
258 return "https";
259 default:
260 return "<discarded-invalid-value>";
261 }
262 }
263
ParseMemento(Slice value,bool,MetadataParseErrorFn on_error)264 HttpMethodMetadata::MementoType HttpMethodMetadata::ParseMemento(
265 Slice value, bool, MetadataParseErrorFn on_error) {
266 auto out = kInvalid;
267 auto value_string = value.as_string_view();
268 if (value_string == "POST") {
269 out = kPost;
270 } else if (value_string == "PUT") {
271 out = kPut;
272 } else if (value_string == "GET") {
273 out = kGet;
274 } else {
275 on_error("invalid value", value);
276 }
277 return out;
278 }
279
Encode(ValueType x)280 StaticSlice HttpMethodMetadata::Encode(ValueType x) {
281 switch (x) {
282 case kPost:
283 return StaticSlice::FromStaticString("POST");
284 case kPut:
285 return StaticSlice::FromStaticString("PUT");
286 case kGet:
287 return StaticSlice::FromStaticString("GET");
288 default:
289 // TODO(ctiller): this should be an abort, we should split up the debug
290 // string generation from the encode string generation so that debug
291 // strings can always succeed and encode strings can crash.
292 return StaticSlice::FromStaticString("<<INVALID METHOD>>");
293 }
294 }
295
DisplayValue(ValueType content_type)296 const char* HttpMethodMetadata::DisplayValue(ValueType content_type) {
297 switch (content_type) {
298 case kPost:
299 return "POST";
300 case kGet:
301 return "GET";
302 case kPut:
303 return "PUT";
304 default:
305 return "<discarded-invalid-value>";
306 }
307 }
308
309 CompressionAlgorithmBasedMetadata::MementoType
ParseMemento(Slice value,bool,MetadataParseErrorFn on_error)310 CompressionAlgorithmBasedMetadata::ParseMemento(Slice value, bool,
311 MetadataParseErrorFn on_error) {
312 auto algorithm = ParseCompressionAlgorithm(value.as_string_view());
313 if (!algorithm.has_value()) {
314 on_error("invalid value", value);
315 return GRPC_COMPRESS_NONE;
316 }
317 return *algorithm;
318 }
319
ParseMemento(Slice value,bool,MetadataParseErrorFn on_error)320 Duration GrpcRetryPushbackMsMetadata::ParseMemento(
321 Slice value, bool, MetadataParseErrorFn on_error) {
322 int64_t out;
323 if (!absl::SimpleAtoi(value.as_string_view(), &out)) {
324 on_error("not an integer", value);
325 return Duration::NegativeInfinity();
326 }
327 return Duration::Milliseconds(out);
328 }
329
Encode(const ValueType & x)330 Slice LbCostBinMetadata::Encode(const ValueType& x) {
331 auto slice =
332 MutableSlice::CreateUninitialized(sizeof(double) + x.name.length());
333 memcpy(slice.data(), &x.cost, sizeof(double));
334 memcpy(slice.data() + sizeof(double), x.name.data(), x.name.length());
335 return Slice(std::move(slice));
336 }
337
DisplayValue(ValueType x)338 std::string LbCostBinMetadata::DisplayValue(ValueType x) {
339 return absl::StrCat(x.name, ":", x.cost);
340 }
341
ParseMemento(Slice value,bool,MetadataParseErrorFn on_error)342 LbCostBinMetadata::MementoType LbCostBinMetadata::ParseMemento(
343 Slice value, bool, MetadataParseErrorFn on_error) {
344 if (value.length() < sizeof(double)) {
345 on_error("too short", value);
346 return {0, ""};
347 }
348 MementoType out;
349 memcpy(&out.cost, value.data(), sizeof(double));
350 out.name =
351 std::string(reinterpret_cast<const char*>(value.data()) + sizeof(double),
352 value.length() - sizeof(double));
353 return out;
354 }
355
DisplayValue(ValueType x)356 std::string GrpcStreamNetworkState::DisplayValue(ValueType x) {
357 switch (x) {
358 case kNotSentOnWire:
359 return "not sent on wire";
360 case kNotSeenByServer:
361 return "not seen by server";
362 }
363 GPR_UNREACHABLE_CODE(return "unknown value");
364 }
365
DisplayValue(void * x)366 std::string GrpcRegisteredMethod::DisplayValue(void* x) {
367 return absl::StrFormat("%p", x);
368 }
369
DisplayValue(const ValueType & x)370 std::string PeerString::DisplayValue(const ValueType& x) {
371 return std::string(x.as_string_view());
372 }
373
DisplayValue(const std::string & x)374 const std::string& GrpcStatusContext::DisplayValue(const std::string& x) {
375 return x;
376 }
377
DisplayValue(ValueType x)378 std::string WaitForReady::DisplayValue(ValueType x) {
379 return absl::StrCat(x.value ? "true" : "false",
380 x.explicitly_set ? " (explicit)" : "");
381 }
382
383 } // namespace grpc_core
384