Expressions
APIs that require clients to specify expressions MUST use the Common Expression Language (CEL). Other expression languages SHOULD NOT be supported except to adhere to a domain-specific industry precedent.
Exception: APIs MAY support the extended filtering language for collections.
Validation
For robustness, client-specified expressions MUST be parsed and checked with one of the following CEL implementations [1]:
Additionally, each client-specified expression MUST be statically checked verbatim (without any canonicalization) for:
- Maximum length (at least 100 characters, and at most 1000 characters)
- Expected result type
- Time and space complexity (which must be assessed in the context in which the expression will be evaluated)
Client-specified expressions that pass these checks MUST be accepted and used verbatim. By extension, for expressions that are being stored for later evaluation, stylistic choices made by the client (including leading whitespace or embedded comments) MUST be preserved.
If the client-specified expression is not evaluated with one of the CEL implementations (e.g., because it's translated to an existing domain-native expression language prior to evaluation), the native expression language MUST meet the CEL language requirements of 32 nested expressions and 32 repetitions of self-recursive or repetitive rules, unless other constraints (such as the maximum expression length) preclude reaching those limits.
As a safeguard, any CEL expression that takes longer than 500 milliseconds to execute MUST be aborted, and treated as if it evaluated to false
. Aborted CEL expressions SHOULD be communicated to the customer (for instance, through
a customer-facing log entry or by transitioning the resource to a degraded health state, with an accompanying health reason).
500 milliseconds is the maximum; evaluation MAY be aborted earlier.
Minimum support
To provide consistency and familiarity, CEL environments MUST support a minimum subset of the language definition. The minimum support depends on the types of values that can be included in the expression:
Types | Concept | Operators / Functions | Example |
---|---|---|---|
any | equality | == , != |
x == y |
any | precedence | () |
x && (y || z) |
any | conditional | ?: |
x > y ? x : y |
boolean | logic | && , || , ! |
x || !y |
list, map | indexing | [] |
l[0] == l[size(l)-1] |
list, map | membership | in |
i in [1, 2, 3] |
number[2] | arithmetic | + , - , * , / , % |
i + j % k |
number | negation | - |
-i |
number, string, bytes, time | comparison | < , <= , > , >= |
i >= j |
string | matching | contains() , startsWith() , endsWith() |
s.contains(t) |
string, bytes, list | concatenation | + |
s + t |
string, bytes, list, map | counting | size() |
size(s) < 100 |
time[3] | arithmetic | + , - |
t2 - t1 |
In addition:
- If a given type can be included in an expression, support for type literals MUST be provided as per the CEL language definition. For example, if floating-point values are supported, then
8.0
,8.
,8
, and8e0
MUST all be recognized as8.0
. - Standard CEL casting functions MUST be supported between all supported types. For example, if both numbers and strings are supported, it MUST be possible to cast from any numeric type to a string, and vice-versa.
Additional functions
The following additional functions are standardized and MUST be used when applicable:
Function | Type | Description |
---|---|---|
ipInRange(ip, cidr) |
(string , string ) -> bool |
True if ip is in cidr |
CEL environments MAY define additional functions. To adhere to CEL naming conventions, these functions MUST be named in lower camel case with any initialisms treated as words (e.g.,
isAscii()
, not isASCII()
). Additionally, these functions MUST NOT use dynamic return types.
Variables
Any context-specific data MUST be provided in variables which MUST be named in lower snake case. The following additional variables are standardized and MUST be used when applicable:
Variable | Type | Description |
---|---|---|
destination.ip |
string | Destination IP address for a packet |
destination.port |
number | Destination port for a packet |
request.body |
bytes | HTTP request body |
request.time |
time | HTTP request timestamp |
response.body |
bytes | HTTP or DNS response body |
response.time |
time | HTTP or DNS response timestamp |
source.ip |
string | Source IP address for a packet |
source.port |
number | Source port for a packet |
When CEL is used in contexts for which there is an implied resource scope (such as collection filtering), CEL variables MUST adhere to the graph fragment
pattern. For example, a collection expression that allows a resource to be filtered by its type
property values MUST provide a CEL variable named type
for use in those filter expressions.
Exception: If the property name is a CEL reserved
identifier, it MUST be surrounded by double-underscores. For example, the property namespace
(which is reserved in CEL) MUST have the CEL variable name __namespace__
.
APIs MAY support the extended filtering language for collections.
Functions and variables use separate namespaces in CEL. Therefore, a variable named size
would not collide with the CEL size()
function.
Versioning
When CEL is used in contexts that imply a resource scope, an API version MUST accompany the CEL expression, which MUST be used when the expression is evaluated. This ensures that as properties within a resource schema evolve (such as by renaming, removing, or refactoring), the CEL variables that refer to those properties remain interpreted according to the client's original intent. This also ensures that CEL expressions do not need to be transformed by the server (e.g., to put it in the "version" of the requesting client). As a result, if these CEL expressions need to be stored for subsequent evaluation, the client's API version MUST be stored alongside the expression. Further, before removing support for an older API version, a transition plan MUST be provided to enable customers to refresh any persisted CEL expressions to a newer API version.