Coverage Report

Created: 2026-05-27 07:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/proc/self/cwd/extensions/protobuf/internal/qualify.cc
Line
Count
Source
1
// Copyright 2024 Google LLC
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
//     https://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 "extensions/protobuf/internal/qualify.h"
16
17
#include <string>
18
#include <utility>
19
20
#include "absl/functional/overload.h"
21
#include "absl/log/absl_check.h"
22
#include "absl/status/status.h"
23
#include "absl/status/statusor.h"
24
#include "absl/strings/str_cat.h"
25
#include "absl/types/optional.h"
26
#include "absl/types/variant.h"
27
#include "base/attribute.h"
28
#include "base/builtins.h"
29
#include "common/kind.h"
30
#include "common/memory.h"
31
#include "extensions/protobuf/internal/map_reflection.h"
32
#include "internal/status_macros.h"
33
#include "runtime/internal/errors.h"
34
#include "runtime/runtime_options.h"
35
#include "google/protobuf/descriptor.h"
36
#include "google/protobuf/map_field.h"
37
#include "google/protobuf/message.h"
38
#include "google/protobuf/reflection.h"
39
40
#undef GetMessage
41
42
namespace cel::extensions::protobuf_internal {
43
44
namespace {
45
46
const google::protobuf::FieldDescriptor* GetNormalizedFieldByNumber(
47
    const google::protobuf::Descriptor* descriptor, const google::protobuf::Reflection* reflection,
48
0
    int field_number) {
49
0
  const google::protobuf::FieldDescriptor* field_desc =
50
0
      descriptor->FindFieldByNumber(field_number);
51
0
  if (field_desc == nullptr && reflection != nullptr) {
52
0
    field_desc = reflection->FindKnownExtensionByNumber(field_number);
53
0
  }
54
0
  return field_desc;
55
0
}
56
57
// JSON container types and Any have special unpacking rules.
58
//
59
// Not considered for qualify traversal for simplicity, but
60
// could be supported in a follow-up if needed.
61
0
bool IsUnsupportedQualifyType(const google::protobuf::Descriptor& desc) {
62
0
  switch (desc.well_known_type()) {
63
0
    case google::protobuf::Descriptor::WELLKNOWNTYPE_ANY:
64
0
    case google::protobuf::Descriptor::WELLKNOWNTYPE_STRUCT:
65
0
    case google::protobuf::Descriptor::WELLKNOWNTYPE_VALUE:
66
0
    case google::protobuf::Descriptor::WELLKNOWNTYPE_LISTVALUE:
67
0
      return true;
68
0
    default:
69
0
      return false;
70
0
  }
71
0
}
72
73
constexpr int kKeyTag = 1;
74
constexpr int kValueTag = 2;
75
76
bool MatchesMapKeyType(const google::protobuf::FieldDescriptor* key_desc,
77
0
                       const cel::AttributeQualifier& key) {
78
0
  switch (key_desc->cpp_type()) {
79
0
    case google::protobuf::FieldDescriptor::CPPTYPE_BOOL:
80
0
      return key.kind() == cel::Kind::kBool;
81
0
    case google::protobuf::FieldDescriptor::CPPTYPE_INT32:
82
      // fall through
83
0
    case google::protobuf::FieldDescriptor::CPPTYPE_INT64:
84
0
      return key.kind() == cel::Kind::kInt64;
85
0
    case google::protobuf::FieldDescriptor::CPPTYPE_UINT32:
86
      // fall through
87
0
    case google::protobuf::FieldDescriptor::CPPTYPE_UINT64:
88
0
      return key.kind() == cel::Kind::kUint64;
89
0
    case google::protobuf::FieldDescriptor::CPPTYPE_STRING:
90
0
      return key.kind() == cel::Kind::kString;
91
92
0
    default:
93
0
      return false;
94
0
  }
95
0
}
96
97
absl::StatusOr<absl::optional<google::protobuf::MapValueConstRef>> LookupMapValue(
98
    const google::protobuf::Message* message, const google::protobuf::Reflection* reflection,
99
    const google::protobuf::FieldDescriptor* field_desc,
100
    const google::protobuf::FieldDescriptor* key_desc,
101
0
    const cel::AttributeQualifier& key) {
102
0
  if (!MatchesMapKeyType(key_desc, key)) {
103
0
    return runtime_internal::CreateInvalidMapKeyTypeError(
104
0
        key_desc->cpp_type_name());
105
0
  }
106
107
0
  std::string proto_key_string;
108
0
  google::protobuf::MapKey proto_key;
109
0
  switch (key_desc->cpp_type()) {
110
0
    case google::protobuf::FieldDescriptor::CPPTYPE_BOOL:
111
0
      proto_key.SetBoolValue(*key.GetBoolKey());
112
0
      break;
113
0
    case google::protobuf::FieldDescriptor::CPPTYPE_INT32: {
114
0
      int64_t key_value = *key.GetInt64Key();
115
0
      if (key_value > std::numeric_limits<int32_t>::max() ||
116
0
          key_value < std::numeric_limits<int32_t>::lowest()) {
117
0
        return absl::OutOfRangeError("integer overflow");
118
0
      }
119
0
      proto_key.SetInt32Value(key_value);
120
0
    } break;
121
0
    case google::protobuf::FieldDescriptor::CPPTYPE_INT64:
122
0
      proto_key.SetInt64Value(*key.GetInt64Key());
123
0
      break;
124
0
    case google::protobuf::FieldDescriptor::CPPTYPE_STRING: {
125
0
      proto_key_string = std::string(*key.GetStringKey());
126
0
      proto_key.SetStringValue(proto_key_string);
127
0
    } break;
128
0
    case google::protobuf::FieldDescriptor::CPPTYPE_UINT32: {
129
0
      uint64_t key_value = *key.GetUint64Key();
130
0
      if (key_value > std::numeric_limits<uint32_t>::max()) {
131
0
        return absl::OutOfRangeError("unsigned integer overflow");
132
0
      }
133
0
      proto_key.SetUInt32Value(key_value);
134
0
    } break;
135
0
    case google::protobuf::FieldDescriptor::CPPTYPE_UINT64: {
136
0
      proto_key.SetUInt64Value(*key.GetUint64Key());
137
0
    } break;
138
0
    default:
139
0
      return runtime_internal::CreateInvalidMapKeyTypeError(
140
0
          key_desc->cpp_type_name());
141
0
  }
142
143
  // Look the value up
144
0
  google::protobuf::MapValueConstRef value_ref;
145
0
  bool found = cel::extensions::protobuf_internal::LookupMapValue(
146
0
      *reflection, *message, *field_desc, proto_key, &value_ref);
147
0
  if (!found) {
148
0
    return absl::nullopt;
149
0
  }
150
0
  return value_ref;
151
0
}
152
153
bool FieldIsPresent(const google::protobuf::Message* message,
154
                    const google::protobuf::FieldDescriptor* field_desc,
155
0
                    const google::protobuf::Reflection* reflection) {
156
0
  if (field_desc->is_map()) {
157
    // When the map field appears in a has(msg.map_field) expression, the map
158
    // is considered 'present' when it is non-empty. Since maps are repeated
159
    // fields they don't participate with standard proto presence testing
160
    // since the repeated field is always at least empty.
161
0
    return reflection->FieldSize(*message, field_desc) != 0;
162
0
  }
163
164
0
  if (field_desc->is_repeated()) {
165
    // When the list field appears in a has(msg.list_field) expression, the
166
    // list is considered 'present' when it is non-empty.
167
0
    return reflection->FieldSize(*message, field_desc) != 0;
168
0
  }
169
170
  // Standard proto presence test for non-repeated fields.
171
0
  return reflection->HasField(*message, field_desc);
172
0
}
173
174
}  // namespace
175
176
absl::Status ProtoQualifyState::ApplySelectQualifier(
177
0
    const cel::SelectQualifier& qualifier, MemoryManagerRef memory_manager) {
178
0
  return absl::visit(
179
0
      absl::Overload(
180
0
          [&](const cel::AttributeQualifier& qualifier) -> absl::Status {
181
0
            if (repeated_field_desc_ == nullptr) {
182
0
              return absl::UnimplementedError(
183
0
                  "dynamic field access on message not supported");
184
0
            }
185
0
            return ApplyAttributeQualifer(qualifier, memory_manager);
186
0
          },
187
0
          [&](const cel::FieldSpecifier& field_specifier) -> absl::Status {
188
0
            if (repeated_field_desc_ != nullptr) {
189
0
              return absl::UnimplementedError(
190
0
                  "strong field access on container not supported");
191
0
            }
192
0
            return ApplyFieldSpecifier(field_specifier, memory_manager);
193
0
          }),
194
0
      qualifier);
195
0
}
196
197
absl::Status ProtoQualifyState::ApplyLastQualifierHas(
198
0
    const cel::SelectQualifier& qualifier, MemoryManagerRef memory_manager) {
199
0
  const cel::FieldSpecifier* specifier =
200
0
      absl::get_if<cel::FieldSpecifier>(&qualifier);
201
0
  return absl::visit(
202
0
      absl::Overload(
203
0
          [&](const cel::AttributeQualifier& qualifier) mutable
204
0
          -> absl::Status {
205
0
            if (qualifier.kind() != cel::Kind::kString ||
206
0
                repeated_field_desc_ == nullptr ||
207
0
                !repeated_field_desc_->is_map()) {
208
0
              SetResultFromError(
209
0
                  runtime_internal::CreateNoMatchingOverloadError("has"),
210
0
                  memory_manager);
211
0
              return absl::OkStatus();
212
0
            }
213
0
            return MapHas(qualifier, memory_manager);
214
0
          },
215
0
          [&](const cel::FieldSpecifier& field_specifier) mutable
216
0
          -> absl::Status {
217
0
            const auto* field_desc = GetNormalizedFieldByNumber(
218
0
                descriptor_, reflection_, specifier->number);
219
0
            if (field_desc == nullptr) {
220
0
              SetResultFromError(
221
0
                  runtime_internal::CreateNoSuchFieldError(specifier->name),
222
0
                  memory_manager);
223
0
              return absl::OkStatus();
224
0
            }
225
0
            SetResultFromBool(
226
0
                FieldIsPresent(message_, field_desc, reflection_));
227
0
            return absl::OkStatus();
228
0
          }),
229
0
      qualifier);
230
0
}
231
232
absl::Status ProtoQualifyState::ApplyLastQualifierGet(
233
0
    const cel::SelectQualifier& qualifier, MemoryManagerRef memory_manager) {
234
0
  return absl::visit(
235
0
      absl::Overload(
236
0
          [&](const cel::AttributeQualifier& attr_qualifier) mutable
237
0
          -> absl::Status {
238
0
            if (repeated_field_desc_ == nullptr) {
239
0
              return absl::UnimplementedError(
240
0
                  "dynamic field access on message not supported");
241
0
            }
242
0
            if (repeated_field_desc_->is_map()) {
243
0
              return ApplyLastQualifierGetMap(attr_qualifier, memory_manager);
244
0
            }
245
0
            return ApplyLastQualifierGetList(attr_qualifier, memory_manager);
246
0
          },
247
0
          [&](const cel::FieldSpecifier& specifier) mutable -> absl::Status {
248
0
            if (repeated_field_desc_ != nullptr) {
249
0
              return absl::UnimplementedError(
250
0
                  "strong field access on container not supported");
251
0
            }
252
0
            return ApplyLastQualifierMessageGet(specifier, memory_manager);
253
0
          }),
254
0
      qualifier);
255
0
}
256
257
absl::Status ProtoQualifyState::ApplyFieldSpecifier(
258
    const cel::FieldSpecifier& field_specifier,
259
0
    MemoryManagerRef memory_manager) {
260
0
  const google::protobuf::FieldDescriptor* field_desc = GetNormalizedFieldByNumber(
261
0
      descriptor_, reflection_, field_specifier.number);
262
0
  if (field_desc == nullptr) {
263
0
    SetResultFromError(
264
0
        runtime_internal::CreateNoSuchFieldError(field_specifier.name),
265
0
        memory_manager);
266
0
    return absl::OkStatus();
267
0
  }
268
269
0
  if (field_desc->is_repeated()) {
270
0
    repeated_field_desc_ = field_desc;
271
0
    return absl::OkStatus();
272
0
  }
273
274
0
  if (field_desc->cpp_type() != google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE ||
275
0
      IsUnsupportedQualifyType(*field_desc->message_type())) {
276
0
    CEL_RETURN_IF_ERROR(SetResultFromField(message_, field_desc,
277
0
                                           ProtoWrapperTypeOptions::kUnsetNull,
278
0
                                           memory_manager));
279
0
    return absl::OkStatus();
280
0
  }
281
282
0
  message_ = &reflection_->GetMessage(*message_, field_desc);
283
0
  descriptor_ = message_->GetDescriptor();
284
0
  reflection_ = message_->GetReflection();
285
0
  return absl::OkStatus();
286
0
}
287
288
absl::StatusOr<int> ProtoQualifyState::CheckListIndex(
289
0
    const cel::AttributeQualifier& qualifier) const {
290
0
  if (qualifier.kind() != cel::Kind::kInt64) {
291
0
    return runtime_internal::CreateNoMatchingOverloadError(
292
0
        cel::builtin::kIndex);
293
0
  }
294
295
0
  int index = *qualifier.GetInt64Key();
296
0
  int size = reflection_->FieldSize(*message_, repeated_field_desc_);
297
0
  if (index < 0 || index >= size) {
298
0
    return absl::InvalidArgumentError(
299
0
        absl::StrCat("index out of bounds: index=", index, " size=", size));
300
0
  }
301
0
  return index;
302
0
}
303
304
absl::Status ProtoQualifyState::ApplyAttributeQualifierList(
305
0
    const cel::AttributeQualifier& qualifier, MemoryManagerRef memory_manager) {
306
0
  ABSL_DCHECK_NE(repeated_field_desc_, nullptr);
307
0
  ABSL_DCHECK(!repeated_field_desc_->is_map());
308
0
  ABSL_DCHECK_EQ(repeated_field_desc_->cpp_type(),
309
0
                 google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE);
310
311
0
  auto index_or = CheckListIndex(qualifier);
312
0
  if (!index_or.ok()) {
313
0
    SetResultFromError(std::move(index_or).status(), memory_manager);
314
0
    return absl::OkStatus();
315
0
  }
316
317
0
  if (IsUnsupportedQualifyType(*repeated_field_desc_->message_type())) {
318
0
    CEL_RETURN_IF_ERROR(SetResultFromRepeatedField(
319
0
        message_, repeated_field_desc_, *index_or, memory_manager));
320
0
    return absl::OkStatus();
321
0
  }
322
323
0
  message_ = &reflection_->GetRepeatedMessage(*message_, repeated_field_desc_,
324
0
                                              *index_or);
325
0
  descriptor_ = message_->GetDescriptor();
326
0
  reflection_ = message_->GetReflection();
327
0
  repeated_field_desc_ = nullptr;
328
0
  return absl::OkStatus();
329
0
}
330
331
absl::StatusOr<google::protobuf::MapValueConstRef> ProtoQualifyState::CheckMapIndex(
332
0
    const cel::AttributeQualifier& qualifier) const {
333
0
  const auto* key_desc =
334
0
      repeated_field_desc_->message_type()->FindFieldByNumber(kKeyTag);
335
336
0
  CEL_ASSIGN_OR_RETURN(
337
0
      absl::optional<google::protobuf::MapValueConstRef> value_ref,
338
0
      LookupMapValue(message_, reflection_, repeated_field_desc_, key_desc,
339
0
                     qualifier));
340
341
0
  if (!value_ref.has_value()) {
342
0
    std::string key_string;
343
0
    absl::StatusOr<std::string> key_string_or = qualifier.AsString();
344
0
    if (key_string_or.ok()) {
345
0
      key_string = *key_string_or;
346
0
    }
347
0
    return runtime_internal::CreateNoSuchKeyError(key_string);
348
0
  }
349
0
  return std::move(value_ref).value();
350
0
}
351
352
absl::Status ProtoQualifyState::ApplyAttributeQualifierMap(
353
0
    const cel::AttributeQualifier& qualifier, MemoryManagerRef memory_manager) {
354
0
  ABSL_DCHECK_NE(repeated_field_desc_, nullptr);
355
0
  ABSL_DCHECK(repeated_field_desc_->is_map());
356
0
  ABSL_DCHECK_EQ(repeated_field_desc_->cpp_type(),
357
0
                 google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE);
358
359
0
  absl::StatusOr<google::protobuf::MapValueConstRef> value_ref = CheckMapIndex(qualifier);
360
0
  if (!value_ref.ok()) {
361
0
    SetResultFromError(std::move(value_ref).status(), memory_manager);
362
0
    return absl::OkStatus();
363
0
  }
364
365
0
  const auto* value_desc =
366
0
      repeated_field_desc_->message_type()->FindFieldByNumber(kValueTag);
367
368
0
  if (value_desc->cpp_type() != google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE ||
369
0
      IsUnsupportedQualifyType(*value_desc->message_type())) {
370
0
    CEL_RETURN_IF_ERROR(SetResultFromMapField(message_, value_desc, *value_ref,
371
0
                                              memory_manager));
372
0
    return absl::OkStatus();
373
0
  }
374
375
0
  message_ = &(value_ref->GetMessageValue());
376
0
  descriptor_ = message_->GetDescriptor();
377
0
  reflection_ = message_->GetReflection();
378
0
  repeated_field_desc_ = nullptr;
379
0
  return absl::OkStatus();
380
0
}
381
382
absl::Status ProtoQualifyState::ApplyAttributeQualifer(
383
0
    const cel::AttributeQualifier& qualifier, MemoryManagerRef memory_manager) {
384
0
  ABSL_DCHECK_NE(repeated_field_desc_, nullptr);
385
0
  if (repeated_field_desc_->cpp_type() !=
386
0
      google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE) {
387
0
    return absl::InternalError("Unexpected qualify intermediate type");
388
0
  }
389
0
  if (repeated_field_desc_->is_map()) {
390
0
    return ApplyAttributeQualifierMap(qualifier, memory_manager);
391
0
  }  // else simple repeated
392
0
  return ApplyAttributeQualifierList(qualifier, memory_manager);
393
0
}
394
395
absl::Status ProtoQualifyState::MapHas(const cel::AttributeQualifier& key,
396
0
                                       MemoryManagerRef memory_manager) {
397
0
  const auto* key_desc =
398
0
      repeated_field_desc_->message_type()->FindFieldByNumber(kKeyTag);
399
400
0
  absl::StatusOr<absl::optional<google::protobuf::MapValueConstRef>> value_ref =
401
0
      LookupMapValue(message_, reflection_, repeated_field_desc_, key_desc,
402
0
                     key);
403
404
0
  if (!value_ref.ok()) {
405
0
    SetResultFromError(std::move(value_ref).status(), memory_manager);
406
0
    return absl::OkStatus();
407
0
  }
408
409
0
  SetResultFromBool(value_ref->has_value());
410
0
  return absl::OkStatus();
411
0
}
412
413
absl::Status ProtoQualifyState::ApplyLastQualifierMessageGet(
414
0
    const cel::FieldSpecifier& specifier, MemoryManagerRef memory_manager) {
415
0
  const auto* field_desc =
416
0
      GetNormalizedFieldByNumber(descriptor_, reflection_, specifier.number);
417
0
  if (field_desc == nullptr) {
418
0
    SetResultFromError(runtime_internal::CreateNoSuchFieldError(specifier.name),
419
0
                       memory_manager);
420
0
    return absl::OkStatus();
421
0
  }
422
0
  return SetResultFromField(message_, field_desc,
423
0
                            ProtoWrapperTypeOptions::kUnsetNull,
424
0
                            memory_manager);
425
0
}
426
427
absl::Status ProtoQualifyState::ApplyLastQualifierGetList(
428
0
    const cel::AttributeQualifier& qualifier, MemoryManagerRef memory_manager) {
429
0
  ABSL_DCHECK(!repeated_field_desc_->is_map());
430
431
0
  absl::StatusOr<int> index = CheckListIndex(qualifier);
432
0
  if (!index.ok()) {
433
0
    SetResultFromError(std::move(index).status(), memory_manager);
434
0
    return absl::OkStatus();
435
0
  }
436
0
  return SetResultFromRepeatedField(message_, repeated_field_desc_, *index,
437
0
                                    memory_manager);
438
0
}
439
440
absl::Status ProtoQualifyState::ApplyLastQualifierGetMap(
441
0
    const cel::AttributeQualifier& qualifier, MemoryManagerRef memory_manager) {
442
0
  ABSL_DCHECK(repeated_field_desc_->is_map());
443
444
0
  absl::StatusOr<google::protobuf::MapValueConstRef> value_ref = CheckMapIndex(qualifier);
445
446
0
  if (!value_ref.ok()) {
447
0
    SetResultFromError(std::move(value_ref).status(), memory_manager);
448
0
    return absl::OkStatus();
449
0
  }
450
451
0
  const auto* value_desc =
452
0
      repeated_field_desc_->message_type()->FindFieldByNumber(kValueTag);
453
0
  return SetResultFromMapField(message_, value_desc, *value_ref,
454
0
                               memory_manager);
455
0
}
456
457
}  // namespace cel::extensions::protobuf_internal