Coverage Report

Created: 2026-10-09 06:57

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
  google::protobuf::MapKey proto_key;
108
0
  switch (key_desc->cpp_type()) {
109
0
    case google::protobuf::FieldDescriptor::CPPTYPE_BOOL:
110
0
      proto_key.SetBoolValue(*key.GetBoolKey());
111
0
      break;
112
0
    case google::protobuf::FieldDescriptor::CPPTYPE_INT32: {
113
0
      int64_t key_value = *key.GetInt64Key();
114
0
      if (key_value > std::numeric_limits<int32_t>::max() ||
115
0
          key_value < std::numeric_limits<int32_t>::lowest()) {
116
0
        return absl::OutOfRangeError("integer overflow");
117
0
      }
118
0
      proto_key.SetInt32Value(key_value);
119
0
    } break;
120
0
    case google::protobuf::FieldDescriptor::CPPTYPE_INT64:
121
0
      proto_key.SetInt64Value(*key.GetInt64Key());
122
0
      break;
123
0
    case google::protobuf::FieldDescriptor::CPPTYPE_STRING: {
124
0
      proto_key.SetStringValue(*key.GetStringKey());
125
0
    } break;
126
0
    case google::protobuf::FieldDescriptor::CPPTYPE_UINT32: {
127
0
      uint64_t key_value = *key.GetUint64Key();
128
0
      if (key_value > std::numeric_limits<uint32_t>::max()) {
129
0
        return absl::OutOfRangeError("unsigned integer overflow");
130
0
      }
131
0
      proto_key.SetUInt32Value(key_value);
132
0
    } break;
133
0
    case google::protobuf::FieldDescriptor::CPPTYPE_UINT64: {
134
0
      proto_key.SetUInt64Value(*key.GetUint64Key());
135
0
    } break;
136
0
    default:
137
0
      return runtime_internal::CreateInvalidMapKeyTypeError(
138
0
          key_desc->cpp_type_name());
139
0
  }
140
141
  // Look the value up
142
0
  google::protobuf::MapValueConstRef value_ref;
143
0
  bool found = cel::extensions::protobuf_internal::LookupMapValue(
144
0
      *reflection, *message, *field_desc, proto_key, &value_ref);
145
0
  if (!found) {
146
0
    return std::nullopt;
147
0
  }
148
0
  return value_ref;
149
0
}
150
151
bool FieldIsPresent(const google::protobuf::Message* message,
152
                    const google::protobuf::FieldDescriptor* field_desc,
153
0
                    const google::protobuf::Reflection* reflection) {
154
0
  if (field_desc->is_map()) {
155
    // When the map field appears in a has(msg.map_field) expression, the map
156
    // is considered 'present' when it is non-empty. Since maps are repeated
157
    // fields they don't participate with standard proto presence testing
158
    // since the repeated field is always at least empty.
159
0
    return reflection->FieldSize(*message, field_desc) != 0;
160
0
  }
161
162
0
  if (field_desc->is_repeated()) {
163
    // When the list field appears in a has(msg.list_field) expression, the
164
    // list is considered 'present' when it is non-empty.
165
0
    return reflection->FieldSize(*message, field_desc) != 0;
166
0
  }
167
168
  // Standard proto presence test for non-repeated fields.
169
0
  return reflection->HasField(*message, field_desc);
170
0
}
171
172
}  // namespace
173
174
absl::Status ProtoQualifyState::ApplySelectQualifier(
175
0
    const cel::SelectQualifier& qualifier, MemoryManagerRef memory_manager) {
176
0
  return absl::visit(
177
0
      absl::Overload(
178
0
          [&](const cel::AttributeQualifier& qualifier) -> absl::Status {
179
0
            if (repeated_field_desc_ == nullptr) {
180
0
              return absl::UnimplementedError(
181
0
                  "dynamic field access on message not supported");
182
0
            }
183
0
            return ApplyAttributeQualifer(qualifier, memory_manager);
184
0
          },
185
0
          [&](const cel::FieldSpecifier& field_specifier) -> absl::Status {
186
0
            if (repeated_field_desc_ != nullptr) {
187
0
              return absl::UnimplementedError(
188
0
                  "strong field access on container not supported");
189
0
            }
190
0
            return ApplyFieldSpecifier(field_specifier, memory_manager);
191
0
          }),
192
0
      qualifier);
193
0
}
194
195
absl::Status ProtoQualifyState::ApplyLastQualifierHas(
196
0
    const cel::SelectQualifier& qualifier, MemoryManagerRef memory_manager) {
197
0
  const cel::FieldSpecifier* specifier =
198
0
      absl::get_if<cel::FieldSpecifier>(&qualifier);
199
0
  return absl::visit(
200
0
      absl::Overload(
201
0
          [&](const cel::AttributeQualifier& qualifier) mutable
202
0
          -> absl::Status {
203
0
            if (qualifier.kind() != cel::Kind::kString ||
204
0
                repeated_field_desc_ == nullptr ||
205
0
                !repeated_field_desc_->is_map()) {
206
0
              SetResultFromError(
207
0
                  runtime_internal::CreateNoMatchingOverloadError("has"),
208
0
                  memory_manager);
209
0
              return absl::OkStatus();
210
0
            }
211
0
            return MapHas(qualifier, memory_manager);
212
0
          },
213
0
          [&](const cel::FieldSpecifier& field_specifier) mutable
214
0
          -> absl::Status {
215
0
            const auto* field_desc = GetNormalizedFieldByNumber(
216
0
                descriptor_, reflection_, specifier->number);
217
0
            if (field_desc == nullptr) {
218
0
              SetResultFromError(
219
0
                  runtime_internal::CreateNoSuchFieldError(specifier->name),
220
0
                  memory_manager);
221
0
              return absl::OkStatus();
222
0
            }
223
0
            SetResultFromBool(
224
0
                FieldIsPresent(message_, field_desc, reflection_));
225
0
            return absl::OkStatus();
226
0
          }),
227
0
      qualifier);
228
0
}
229
230
absl::Status ProtoQualifyState::ApplyLastQualifierGet(
231
0
    const cel::SelectQualifier& qualifier, MemoryManagerRef memory_manager) {
232
0
  return absl::visit(
233
0
      absl::Overload(
234
0
          [&](const cel::AttributeQualifier& attr_qualifier) mutable
235
0
          -> absl::Status {
236
0
            if (repeated_field_desc_ == nullptr) {
237
0
              return absl::UnimplementedError(
238
0
                  "dynamic field access on message not supported");
239
0
            }
240
0
            if (repeated_field_desc_->is_map()) {
241
0
              return ApplyLastQualifierGetMap(attr_qualifier, memory_manager);
242
0
            }
243
0
            return ApplyLastQualifierGetList(attr_qualifier, memory_manager);
244
0
          },
245
0
          [&](const cel::FieldSpecifier& specifier) mutable -> absl::Status {
246
0
            if (repeated_field_desc_ != nullptr) {
247
0
              return absl::UnimplementedError(
248
0
                  "strong field access on container not supported");
249
0
            }
250
0
            return ApplyLastQualifierMessageGet(specifier, memory_manager);
251
0
          }),
252
0
      qualifier);
253
0
}
254
255
absl::Status ProtoQualifyState::ApplyFieldSpecifier(
256
    const cel::FieldSpecifier& field_specifier,
257
0
    MemoryManagerRef memory_manager) {
258
0
  const google::protobuf::FieldDescriptor* field_desc = GetNormalizedFieldByNumber(
259
0
      descriptor_, reflection_, field_specifier.number);
260
0
  if (field_desc == nullptr) {
261
0
    SetResultFromError(
262
0
        runtime_internal::CreateNoSuchFieldError(field_specifier.name),
263
0
        memory_manager);
264
0
    return absl::OkStatus();
265
0
  }
266
267
0
  if (field_desc->is_repeated()) {
268
0
    repeated_field_desc_ = field_desc;
269
0
    return absl::OkStatus();
270
0
  }
271
272
0
  if (field_desc->cpp_type() != google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE ||
273
0
      IsUnsupportedQualifyType(*field_desc->message_type())) {
274
0
    CEL_RETURN_IF_ERROR(SetResultFromField(message_, field_desc,
275
0
                                           ProtoWrapperTypeOptions::kUnsetNull,
276
0
                                           memory_manager));
277
0
    return absl::OkStatus();
278
0
  }
279
280
0
  message_ = &reflection_->GetMessage(*message_, field_desc);
281
0
  descriptor_ = message_->GetDescriptor();
282
0
  reflection_ = message_->GetReflection();
283
0
  return absl::OkStatus();
284
0
}
285
286
absl::StatusOr<int> ProtoQualifyState::CheckListIndex(
287
0
    const cel::AttributeQualifier& qualifier) const {
288
0
  if (qualifier.kind() != cel::Kind::kInt64) {
289
0
    return runtime_internal::CreateNoMatchingOverloadError(
290
0
        cel::builtin::kIndex);
291
0
  }
292
293
0
  int index = *qualifier.GetInt64Key();
294
0
  int size = reflection_->FieldSize(*message_, repeated_field_desc_);
295
0
  if (index < 0 || index >= size) {
296
0
    return absl::InvalidArgumentError(
297
0
        absl::StrCat("index out of bounds: index=", index, " size=", size));
298
0
  }
299
0
  return index;
300
0
}
301
302
absl::Status ProtoQualifyState::ApplyAttributeQualifierList(
303
0
    const cel::AttributeQualifier& qualifier, MemoryManagerRef memory_manager) {
304
0
  ABSL_DCHECK_NE(repeated_field_desc_, nullptr);
305
0
  ABSL_DCHECK(!repeated_field_desc_->is_map());
306
0
  ABSL_DCHECK_EQ(repeated_field_desc_->cpp_type(),
307
0
                 google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE);
308
309
0
  auto index_or = CheckListIndex(qualifier);
310
0
  if (!index_or.ok()) {
311
0
    SetResultFromError(std::move(index_or).status(), memory_manager);
312
0
    return absl::OkStatus();
313
0
  }
314
315
0
  if (IsUnsupportedQualifyType(*repeated_field_desc_->message_type())) {
316
0
    CEL_RETURN_IF_ERROR(SetResultFromRepeatedField(
317
0
        message_, repeated_field_desc_, *index_or, memory_manager));
318
0
    return absl::OkStatus();
319
0
  }
320
321
0
  message_ = &reflection_->GetRepeatedMessage(*message_, repeated_field_desc_,
322
0
                                              *index_or);
323
0
  descriptor_ = message_->GetDescriptor();
324
0
  reflection_ = message_->GetReflection();
325
0
  repeated_field_desc_ = nullptr;
326
0
  return absl::OkStatus();
327
0
}
328
329
absl::StatusOr<google::protobuf::MapValueConstRef> ProtoQualifyState::CheckMapIndex(
330
0
    const cel::AttributeQualifier& qualifier) const {
331
0
  const auto* key_desc =
332
0
      repeated_field_desc_->message_type()->FindFieldByNumber(kKeyTag);
333
334
0
  CEL_ASSIGN_OR_RETURN(
335
0
      absl::optional<google::protobuf::MapValueConstRef> value_ref,
336
0
      LookupMapValue(message_, reflection_, repeated_field_desc_, key_desc,
337
0
                     qualifier));
338
339
0
  if (!value_ref.has_value()) {
340
0
    std::string key_string = qualifier.ToString();
341
0
    return runtime_internal::CreateNoSuchKeyError(key_string);
342
0
  }
343
0
  return std::move(value_ref).value();
344
0
}
345
346
absl::Status ProtoQualifyState::ApplyAttributeQualifierMap(
347
0
    const cel::AttributeQualifier& qualifier, MemoryManagerRef memory_manager) {
348
0
  ABSL_DCHECK_NE(repeated_field_desc_, nullptr);
349
0
  ABSL_DCHECK(repeated_field_desc_->is_map());
350
0
  ABSL_DCHECK_EQ(repeated_field_desc_->cpp_type(),
351
0
                 google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE);
352
353
0
  absl::StatusOr<google::protobuf::MapValueConstRef> value_ref = CheckMapIndex(qualifier);
354
0
  if (!value_ref.ok()) {
355
0
    SetResultFromError(std::move(value_ref).status(), memory_manager);
356
0
    return absl::OkStatus();
357
0
  }
358
359
0
  const auto* value_desc =
360
0
      repeated_field_desc_->message_type()->FindFieldByNumber(kValueTag);
361
362
0
  if (value_desc->cpp_type() != google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE ||
363
0
      IsUnsupportedQualifyType(*value_desc->message_type())) {
364
0
    CEL_RETURN_IF_ERROR(SetResultFromMapField(message_, value_desc, *value_ref,
365
0
                                              memory_manager));
366
0
    return absl::OkStatus();
367
0
  }
368
369
0
  message_ = &(value_ref->GetMessageValue());
370
0
  descriptor_ = message_->GetDescriptor();
371
0
  reflection_ = message_->GetReflection();
372
0
  repeated_field_desc_ = nullptr;
373
0
  return absl::OkStatus();
374
0
}
375
376
absl::Status ProtoQualifyState::ApplyAttributeQualifer(
377
0
    const cel::AttributeQualifier& qualifier, MemoryManagerRef memory_manager) {
378
0
  ABSL_DCHECK_NE(repeated_field_desc_, nullptr);
379
0
  if (repeated_field_desc_->cpp_type() !=
380
0
      google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE) {
381
0
    return absl::InternalError("Unexpected qualify intermediate type");
382
0
  }
383
0
  if (repeated_field_desc_->is_map()) {
384
0
    return ApplyAttributeQualifierMap(qualifier, memory_manager);
385
0
  }  // else simple repeated
386
0
  return ApplyAttributeQualifierList(qualifier, memory_manager);
387
0
}
388
389
absl::Status ProtoQualifyState::MapHas(const cel::AttributeQualifier& key,
390
0
                                       MemoryManagerRef memory_manager) {
391
0
  const auto* key_desc =
392
0
      repeated_field_desc_->message_type()->FindFieldByNumber(kKeyTag);
393
394
0
  absl::StatusOr<absl::optional<google::protobuf::MapValueConstRef>> value_ref =
395
0
      LookupMapValue(message_, reflection_, repeated_field_desc_, key_desc,
396
0
                     key);
397
398
0
  if (!value_ref.ok()) {
399
0
    SetResultFromError(std::move(value_ref).status(), memory_manager);
400
0
    return absl::OkStatus();
401
0
  }
402
403
0
  SetResultFromBool(value_ref->has_value());
404
0
  return absl::OkStatus();
405
0
}
406
407
absl::Status ProtoQualifyState::ApplyLastQualifierMessageGet(
408
0
    const cel::FieldSpecifier& specifier, MemoryManagerRef memory_manager) {
409
0
  const auto* field_desc =
410
0
      GetNormalizedFieldByNumber(descriptor_, reflection_, specifier.number);
411
0
  if (field_desc == nullptr) {
412
0
    SetResultFromError(runtime_internal::CreateNoSuchFieldError(specifier.name),
413
0
                       memory_manager);
414
0
    return absl::OkStatus();
415
0
  }
416
0
  return SetResultFromField(message_, field_desc,
417
0
                            ProtoWrapperTypeOptions::kUnsetNull,
418
0
                            memory_manager);
419
0
}
420
421
absl::Status ProtoQualifyState::ApplyLastQualifierGetList(
422
0
    const cel::AttributeQualifier& qualifier, MemoryManagerRef memory_manager) {
423
0
  ABSL_DCHECK(!repeated_field_desc_->is_map());
424
425
0
  absl::StatusOr<int> index = CheckListIndex(qualifier);
426
0
  if (!index.ok()) {
427
0
    SetResultFromError(std::move(index).status(), memory_manager);
428
0
    return absl::OkStatus();
429
0
  }
430
0
  return SetResultFromRepeatedField(message_, repeated_field_desc_, *index,
431
0
                                    memory_manager);
432
0
}
433
434
absl::Status ProtoQualifyState::ApplyLastQualifierGetMap(
435
0
    const cel::AttributeQualifier& qualifier, MemoryManagerRef memory_manager) {
436
0
  ABSL_DCHECK(repeated_field_desc_->is_map());
437
438
0
  absl::StatusOr<google::protobuf::MapValueConstRef> value_ref = CheckMapIndex(qualifier);
439
440
0
  if (!value_ref.ok()) {
441
0
    SetResultFromError(std::move(value_ref).status(), memory_manager);
442
0
    return absl::OkStatus();
443
0
  }
444
445
0
  const auto* value_desc =
446
0
      repeated_field_desc_->message_type()->FindFieldByNumber(kValueTag);
447
0
  return SetResultFromMapField(message_, value_desc, *value_ref,
448
0
                               memory_manager);
449
0
}
450
451
}  // namespace cel::extensions::protobuf_internal