33#include " envoy/common/exception.h"
44#include " envoy/singleton/manager.h"
55
6+ #include " source/common/protobuf/utility.h"
67#include " source/common/runtime/runtime_features.h"
78
89#include " extensions/regex_functions.h"
10+ #include " extensions/strings.h"
911
1012#include " cel/expr/syntax.pb.h"
1113#include " eval/public/builtin_func_registrar.h"
@@ -101,25 +103,35 @@ ActivationPtr createActivation(const LocalInfo::LocalInfo* local_info,
101103 response_trailers);
102104}
103105
104- BuilderInstanceSharedPtr createBuilder (Protobuf::Arena* arena) {
106+ BuilderConstPtr createBuilder (OptRef<const envoy::config::core::v3::CelExpressionConfig> config,
107+ Protobuf::Arena* arena) {
105108 ASSERT_IS_MAIN_OR_TEST_THREAD ();
106109 google::api::expr::runtime::InterpreterOptions options;
107110
108- // Security-oriented defaults
111+ // Security-oriented defaults.
109112 options.enable_comprehension = false ;
110113 options.enable_regex = true ;
111114 options.regex_max_program_size = 100 ;
112115 options.enable_qualified_identifier_rewrites = true ;
113- options.enable_string_conversion = false ;
114- options.enable_string_concat = false ;
116+
117+ // Resolve options from configuration or fall back to security-oriented defaults.
118+ bool enable_string_functions = false ;
119+ if (config.has_value ()) {
120+ options.enable_string_conversion = config->enable_string_conversion ();
121+ options.enable_string_concat = config->enable_string_concat ();
122+ enable_string_functions = config->enable_string_functions ();
123+ } else {
124+ options.enable_string_conversion = false ;
125+ options.enable_string_concat = false ;
126+ }
115127 options.enable_list_concat = false ;
116128
117- // Performance-oriented defaults
129+ // Performance-oriented defaults.
118130 if (Runtime::runtimeFeatureEnabled (" envoy.reloadable_features.enable_cel_regex_precompilation" )) {
119131 options.enable_regex_precompilation = true ;
120132 }
121133
122- // Enable constant folding (performance optimization)
134+ // Enable constant folding with arena if provided for RBAC backward compatibility optimization.
123135 if (arena != nullptr ) {
124136 options.constant_folding = true ;
125137 options.constant_arena = arena;
@@ -138,14 +150,61 @@ BuilderInstanceSharedPtr createBuilder(Protobuf::Arena* arena) {
138150 throw CelException (absl::StrCat (" failed to register extension regex functions: " ,
139151 ext_register_status.message ()));
140152 }
141- return std::make_shared<BuilderInstance>(std::move (builder));
153+ // Register string extension functions only if enabled in configuration.
154+ if (enable_string_functions) {
155+ auto string_register_status =
156+ cel::extensions::RegisterStringsFunctions (builder->GetRegistry (), options);
157+ if (!string_register_status.ok ()) {
158+ throw CelException (absl::StrCat (" failed to register extension string functions: " ,
159+ string_register_status.message ()));
160+ }
161+ }
162+ return builder;
163+ }
164+
165+ BuilderInstanceSharedConstPtr BuilderCache::getOrCreateBuilder (
166+ OptRef<const envoy::config::core::v3::CelExpressionConfig> config) {
167+ ASSERT_IS_MAIN_OR_TEST_THREAD ();
168+
169+ ConfigHash hash = 0 ;
170+ if (config.has_value ()) {
171+ // Use MessageUtil::hash for proto hashing.
172+ hash = MessageUtil::hash (config.ref ());
173+ }
174+
175+ auto it = builders_.find (hash);
176+ if (it != builders_.end ()) {
177+ auto locked_builder = it->second .lock ();
178+ if (locked_builder) {
179+ return locked_builder;
180+ }
181+ }
182+
183+ // Create new builder with the configuration.
184+ auto builder = createBuilder (config);
185+ auto instance = std::make_shared<BuilderInstance>(std::move (builder), shared_from_this ());
186+ // Store as weak_ptr to allow release after xDS unload.
187+ builders_[hash] = instance;
188+ return instance;
142189}
143190
144- SINGLETON_MANAGER_REGISTRATION (expression_builder);
191+ SINGLETON_MANAGER_REGISTRATION (builder_cache);
192+
193+ BuilderInstanceSharedConstPtr
194+ getBuilder (Server::Configuration::CommonFactoryContext& context,
195+ OptRef<const envoy::config::core::v3::CelExpressionConfig> config) {
196+ auto cache = context.singletonManager ().getTyped <BuilderCache>(
197+ SINGLETON_MANAGER_REGISTERED_NAME (builder_cache),
198+ [] { return std::make_shared<BuilderCache>(); });
199+ return cache->getOrCreateBuilder (config);
200+ }
145201
146- BuilderInstanceSharedConstPtr getBuilder (Server::Configuration::CommonFactoryContext& context) {
147- return context.singletonManager ().getTyped <BuilderInstance>(
148- SINGLETON_MANAGER_REGISTERED_NAME (expression_builder), [] { return createBuilder (nullptr ); });
202+ absl::StatusOr<CompiledExpression>
203+ CompiledExpression::Create (Server::Configuration::CommonFactoryContext& context,
204+ const cel::expr::Expr& expr,
205+ OptRef<const envoy::config::core::v3::CelExpressionConfig> config) {
206+ auto builder = getBuilder (context, config);
207+ return Create (builder, expr);
149208}
150209
151210absl::StatusOr<CompiledExpression>
@@ -165,13 +224,13 @@ CompiledExpression::Create(const BuilderInstanceSharedConstPtr& builder,
165224absl::StatusOr<CompiledExpression>
166225CompiledExpression::Create (const BuilderInstanceSharedConstPtr& builder,
167226 const xds::type::v3::CelExpression& xds_expr) {
168- // First try to get expression from the new CEL canonical format
227+ // First try to get expression from the new CEL canonical format.
169228 if (xds_expr.has_cel_expr_checked ()) {
170229 return Create (builder, xds_expr.cel_expr_checked ().expr ());
171230 } else if (xds_expr.has_cel_expr_parsed ()) {
172231 return Create (builder, xds_expr.cel_expr_parsed ().expr ());
173232 }
174- // Fallback to handling legacy formats for backward compatibility
233+ // Fallback to handling legacy formats for backward compatibility.
175234 switch (xds_expr.expr_specifier_case ()) {
176235 case xds::type::v3::CelExpression::ExprSpecifierCase::kParsedExpr :
177236 return Create (builder, xds_expr.parsed_expr ().expr ());
0 commit comments