cel 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +21 -0
- data/README.md +1 -0
- data/lib/cel/ast/elements/invoke.rb +2 -2
- data/lib/cel/ast/elements/list.rb +1 -1
- data/lib/cel/ast/elements/protobuf.rb +2 -2
- data/lib/cel/ast/types.rb +1 -1
- data/lib/cel/checker.rb +27 -10
- data/lib/cel/context.rb +65 -1
- data/lib/cel/environment.rb +12 -5
- data/lib/cel/extensions/bind.rb +4 -0
- data/lib/cel/extensions/encoders.rb +4 -0
- data/lib/cel/extensions/math.rb +4 -0
- data/lib/cel/extensions/network.rb +220 -0
- data/lib/cel/extensions/string.rb +4 -0
- data/lib/cel/extensions.rb +3 -0
- data/lib/cel/parser.rb +1 -1
- data/lib/cel/program.rb +19 -7
- data/lib/cel/version.rb +1 -1
- data/lib/cel.rb +7 -7
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 1bf9b40df18d849d9a964e668c0f1aee0d82f02ccd2de536dcfa73c8d714c718
|
|
4
|
+
data.tar.gz: 0f348c6b1a4a09362d76db927aec94099fc2ac2b355ddcef963b72fa90549e07
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 936e0ea05567de1932de8a887dee32f3682cbdccc574999772283a98acef7ce47d4ad51446eb35a8bcb5195100dc0a3197ec7fb4a82f8ddc239aab47ba0f40c1
|
|
7
|
+
data.tar.gz: d346ef64b5f777b1e368f234a43ad69cb2759313134558ef9760e68933e61a2e1f0644b01ad46b85702017955777ceebe31bce28c410ddbbbca49639eeeb1120
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
## [0.6.0] - 2026-02-28
|
|
4
|
+
|
|
5
|
+
### Features
|
|
6
|
+
|
|
7
|
+
#### Network Extension
|
|
8
|
+
|
|
9
|
+
Support the network extension, as defined [here](https://pkg.go.dev/k8s.io/apiserver/pkg/cel/library#IP).
|
|
10
|
+
|
|
11
|
+
### Improvements
|
|
12
|
+
|
|
13
|
+
#### support top-level lookup (ex: '.y')
|
|
14
|
+
|
|
15
|
+
also, make sure than, on lookup, keys under the passed container take priority, except in the case where the lookup is top-level
|
|
16
|
+
(i.e. '.y').
|
|
17
|
+
|
|
18
|
+
at the same time, #merge is refactored in order to:
|
|
19
|
+
|
|
20
|
+
* normalize nested bindings into aggregated keys ({a:{b:1}} => {:"a.b"=> 1}
|
|
21
|
+
* override container-prefixed variables with the local binding
|
|
22
|
+
* deal with conflicts by moving them to top level.
|
|
23
|
+
|
|
3
24
|
## [0.5.0] - 2025-12-11
|
|
4
25
|
|
|
5
26
|
### Features
|
data/README.md
CHANGED
|
@@ -38,10 +38,10 @@ module Cel
|
|
|
38
38
|
if indexing?
|
|
39
39
|
"#{var}[#{args}]"
|
|
40
40
|
else
|
|
41
|
-
"#{var}.#{func}#{"(#{args.
|
|
41
|
+
"#{var}.#{func}#{"(#{args.join(", ")})" if args}"
|
|
42
42
|
end
|
|
43
43
|
else
|
|
44
|
-
"#{func}#{"(#{args.
|
|
44
|
+
"#{func}#{"(#{args.join(", ")})" if args}"
|
|
45
45
|
end
|
|
46
46
|
end
|
|
47
47
|
|
|
@@ -318,7 +318,7 @@ module Cel
|
|
|
318
318
|
protoclass = var.split(".").last
|
|
319
319
|
protoclass = Google::Protobuf.const_get(protoclass)
|
|
320
320
|
|
|
321
|
-
value = if args.nil? && protoclass.
|
|
321
|
+
value = if args.nil? && protoclass.const_defined?(func.to_sym)
|
|
322
322
|
protoclass.const_get(func)
|
|
323
323
|
else
|
|
324
324
|
protoclass.__send__(func, *args)
|
|
@@ -340,7 +340,7 @@ module Cel
|
|
|
340
340
|
when :repeated
|
|
341
341
|
case value
|
|
342
342
|
when Google::Protobuf::Map
|
|
343
|
-
value.to_h { |k, v| [k, convert_from_proto_field_type(field, v)] }
|
|
343
|
+
value.to_h { |k, v| [k, convert_from_proto_field_type(field, v)] }
|
|
344
344
|
else
|
|
345
345
|
value.map { |v| convert_from_proto_field_type(field, v) }
|
|
346
346
|
end
|
data/lib/cel/ast/types.rb
CHANGED
data/lib/cel/checker.rb
CHANGED
|
@@ -11,14 +11,14 @@ module Cel
|
|
|
11
11
|
BOOLABLE_OPERATORS = %w[&& || == != < <= >= >].freeze
|
|
12
12
|
|
|
13
13
|
CAST_ALLOWED_TYPES = {
|
|
14
|
-
int: %i[int uint double string timestamp], # TODO: enum
|
|
15
|
-
uint: %i[int uint double string],
|
|
16
|
-
string: %i[int uint double bytes bool bytes string timestamp duration],
|
|
17
|
-
double: %i[double int uint string],
|
|
18
|
-
bytes: %i[bytes string],
|
|
19
|
-
bool: %i[bool string],
|
|
20
|
-
duration: %i[string duration],
|
|
21
|
-
timestamp: %i[int string timestamp],
|
|
14
|
+
int: %i[int uint double string timestamp].freeze, # TODO: enum
|
|
15
|
+
uint: %i[int uint double string].freeze,
|
|
16
|
+
string: %i[int uint double bytes bool bytes string timestamp duration].freeze,
|
|
17
|
+
double: %i[double int uint string].freeze,
|
|
18
|
+
bytes: %i[bytes string].freeze,
|
|
19
|
+
bool: %i[bool string].freeze,
|
|
20
|
+
duration: %i[string duration].freeze,
|
|
21
|
+
timestamp: %i[int string timestamp].freeze,
|
|
22
22
|
}.freeze
|
|
23
23
|
|
|
24
24
|
attr_reader :environment, :declarations
|
|
@@ -26,6 +26,15 @@ module Cel
|
|
|
26
26
|
def initialize(environment, declarations = environment.declarations)
|
|
27
27
|
@environment = environment
|
|
28
28
|
@declarations = declarations
|
|
29
|
+
@cast_allowed_types = CAST_ALLOWED_TYPES.dup
|
|
30
|
+
@environment.extensions.each_value do |ext|
|
|
31
|
+
next unless ext.const_defined?(:CAST_ALLOWED_TYPES)
|
|
32
|
+
|
|
33
|
+
ext.const_get(:CAST_ALLOWED_TYPES).each do |type, allowed_types|
|
|
34
|
+
@cast_allowed_types[type] ||= []
|
|
35
|
+
@cast_allowed_types[type] += allowed_types
|
|
36
|
+
end
|
|
37
|
+
end
|
|
29
38
|
end
|
|
30
39
|
|
|
31
40
|
def check(ast)
|
|
@@ -66,6 +75,8 @@ module Cel
|
|
|
66
75
|
end
|
|
67
76
|
|
|
68
77
|
def check_arity(func, args, arity, op = :===)
|
|
78
|
+
return arity.zero? if args.nil?
|
|
79
|
+
|
|
69
80
|
return if arity.__send__(op, args.size)
|
|
70
81
|
|
|
71
82
|
raise CheckError, "`#{func}` invoked with wrong number of arguments (should be #{arity})"
|
|
@@ -388,6 +399,12 @@ module Cel
|
|
|
388
399
|
func = funcall.func
|
|
389
400
|
args = funcall.args
|
|
390
401
|
|
|
402
|
+
@environment.implicit_extensions.each do |ext|
|
|
403
|
+
if (typ = ext.__check(funcall, checker: self))
|
|
404
|
+
return typ
|
|
405
|
+
end
|
|
406
|
+
end
|
|
407
|
+
|
|
391
408
|
# check if protobuf conversion
|
|
392
409
|
if (proto_type = Protobuf.convert_to_proto_type(func.to_s, @environment.package))
|
|
393
410
|
# TODO: fixit
|
|
@@ -413,9 +430,9 @@ module Cel
|
|
|
413
430
|
|
|
414
431
|
arg = call(args.first)
|
|
415
432
|
return TYPES[:int] if find_match_all_types(%i[string bytes list map], arg)
|
|
416
|
-
when
|
|
433
|
+
when *@cast_allowed_types.keys
|
|
417
434
|
check_arity(func, args, 1)
|
|
418
|
-
allowed_types =
|
|
435
|
+
allowed_types = @cast_allowed_types[func]
|
|
419
436
|
|
|
420
437
|
arg = call(args.first)
|
|
421
438
|
|
data/lib/cel/context.rb
CHANGED
|
@@ -16,12 +16,46 @@ module Cel
|
|
|
16
16
|
def lookup(identifier)
|
|
17
17
|
raise EvaluateError, "no value in context for #{identifier}" unless @bindings
|
|
18
18
|
|
|
19
|
+
top_level_key = nil
|
|
19
20
|
id = identifier.to_s
|
|
20
21
|
|
|
21
22
|
lookup_keys = id.split(".")
|
|
22
23
|
|
|
23
24
|
val = @bindings
|
|
24
25
|
|
|
26
|
+
if lookup_keys.first.empty?
|
|
27
|
+
# key starts with ".", i.e. ".y"
|
|
28
|
+
val = val.dup
|
|
29
|
+
lookup_keys.shift
|
|
30
|
+
top_level_key = key = lookup_keys.first
|
|
31
|
+
|
|
32
|
+
if val.respond_to?(:key?)
|
|
33
|
+
val = val.dup
|
|
34
|
+
val.keys.select { |k| k.start_with?(".#{key}") }.each do |k|
|
|
35
|
+
val[k.to_s[1..-1].to_sym] = val.delete(k) # rubocop:disable Style/SlicingWithRange
|
|
36
|
+
end
|
|
37
|
+
# else do nothing
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
if (container = @environment.container)
|
|
42
|
+
# ex move data from "example.com.y" to "y"
|
|
43
|
+
container_keys = val.keys.select { |k| k.start_with?(container) }
|
|
44
|
+
|
|
45
|
+
unless container_keys.empty?
|
|
46
|
+
val = val.dup
|
|
47
|
+
container_keys.each do |key|
|
|
48
|
+
skey = key.to_s.delete_prefix("#{container}.")
|
|
49
|
+
|
|
50
|
+
# forego editing if this is a top level lookup and the key being
|
|
51
|
+
# overwritten would be one
|
|
52
|
+
next if top_level_key && skey.start_with?(top_level_key)
|
|
53
|
+
|
|
54
|
+
val[skey.to_sym] = val.delete(key)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
25
59
|
loop do
|
|
26
60
|
fetched = false
|
|
27
61
|
(0...lookup_keys.size).reverse_each do |idx|
|
|
@@ -66,8 +100,38 @@ module Cel
|
|
|
66
100
|
[val, lookup_keys.empty? ? nil : lookup_keys.join(".")]
|
|
67
101
|
end
|
|
68
102
|
|
|
103
|
+
NORMALIZE_KEYS = lambda do |k, v|
|
|
104
|
+
if v.is_a?(Hash) || v.is_a?(Map)
|
|
105
|
+
v.flat_map do |k2, v2|
|
|
106
|
+
NORMALIZE_KEYS.call(:"#{k}.#{String(k2)}", v2)
|
|
107
|
+
end
|
|
108
|
+
else
|
|
109
|
+
[k, v]
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
69
113
|
def merge(bindings)
|
|
70
|
-
|
|
114
|
+
bindings = bindings.to_h { |k, v| NORMALIZE_KEYS[k, v] }
|
|
115
|
+
if (old_bindings = @bindings)
|
|
116
|
+
if @environment.container
|
|
117
|
+
old_bindings = old_bindings.dup
|
|
118
|
+
|
|
119
|
+
bindings.each_key do |key|
|
|
120
|
+
container_key = :"#{@environment.container}.#{key}"
|
|
121
|
+
|
|
122
|
+
old_bindings.delete(container_key) if old_bindings.key?(container_key)
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
conflicts = []
|
|
126
|
+
bindings = old_bindings.merge(bindings) do |k, old, new|
|
|
127
|
+
conflicts << [k, old]
|
|
128
|
+
new
|
|
129
|
+
end
|
|
130
|
+
conflicts.each do |k, v|
|
|
131
|
+
bindings[:".#{k}"] = v
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
Context.new(@environment, bindings)
|
|
71
135
|
end
|
|
72
136
|
|
|
73
137
|
private
|
data/lib/cel/environment.rb
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
module Cel
|
|
4
4
|
class Environment
|
|
5
|
-
attr_reader :declarations, :extensions, :package
|
|
5
|
+
attr_reader :declarations, :extensions, :package, :container
|
|
6
6
|
|
|
7
7
|
def initialize(
|
|
8
8
|
declarations: nil,
|
|
@@ -13,11 +13,12 @@ module Cel
|
|
|
13
13
|
)
|
|
14
14
|
@disable_check = disable_check
|
|
15
15
|
@declarations = declarations
|
|
16
|
-
@container = container
|
|
17
16
|
@extensions = extensions ? EXTENSIONS.merge(extensions) : EXTENSIONS
|
|
18
|
-
@package = nil
|
|
17
|
+
@package = @container = nil
|
|
19
18
|
@checker = Checker.new(self)
|
|
20
19
|
|
|
20
|
+
@container = container if container && !container.empty?
|
|
21
|
+
|
|
21
22
|
if container && !container.empty?
|
|
22
23
|
|
|
23
24
|
module_dir = container.split(".")
|
|
@@ -38,6 +39,10 @@ module Cel
|
|
|
38
39
|
@parser = Parser.new(package, **kwargs)
|
|
39
40
|
end
|
|
40
41
|
|
|
42
|
+
def implicit_extensions
|
|
43
|
+
@implicit_extensions ||= @extensions.select { |_, ext| ext.implicit? }.values
|
|
44
|
+
end
|
|
45
|
+
|
|
41
46
|
def compile(expr)
|
|
42
47
|
ast = @parser.parse(expr)
|
|
43
48
|
@checker.check(ast)
|
|
@@ -88,8 +93,10 @@ module Cel
|
|
|
88
93
|
cbinding_keys.each do |k|
|
|
89
94
|
cbinding = k.to_s.delete_prefix(prefix).to_sym
|
|
90
95
|
|
|
91
|
-
|
|
92
|
-
|
|
96
|
+
unless bindings.key?(cbinding)
|
|
97
|
+
bindings[cbinding] = bindings.delete(k)
|
|
98
|
+
declarations[cbinding] = declarations.delete(k) if declarations.key?(k)
|
|
99
|
+
end
|
|
93
100
|
end
|
|
94
101
|
|
|
95
102
|
[declarations, bindings]
|
data/lib/cel/extensions/bind.rb
CHANGED
data/lib/cel/extensions/math.rb
CHANGED
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "resolv"
|
|
4
|
+
require "ipaddr"
|
|
5
|
+
|
|
6
|
+
module Cel
|
|
7
|
+
module Extensions
|
|
8
|
+
module Network
|
|
9
|
+
module_function
|
|
10
|
+
|
|
11
|
+
def implicit?
|
|
12
|
+
false
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def __check(funcall, checker:)
|
|
16
|
+
funcall.var
|
|
17
|
+
func = funcall.func
|
|
18
|
+
args = funcall.args
|
|
19
|
+
|
|
20
|
+
case func
|
|
21
|
+
when :IP, :CIDR
|
|
22
|
+
checker.check_arity(func, args, 0)
|
|
23
|
+
TYPES[:type]
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def IP(*) # rubocop:disable Naming/MethodName
|
|
28
|
+
TYPES[:ip]
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def CIDR(*) # rubocop:disable Naming/MethodName
|
|
32
|
+
TYPES[:cidr]
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
module IP
|
|
36
|
+
module_function
|
|
37
|
+
|
|
38
|
+
CAST_ALLOWED_TYPES = {
|
|
39
|
+
string: %i[ip cidr].freeze,
|
|
40
|
+
}.freeze
|
|
41
|
+
|
|
42
|
+
def implicit?
|
|
43
|
+
true
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def __check(funcall, checker:)
|
|
47
|
+
funcall.var
|
|
48
|
+
func = funcall.func
|
|
49
|
+
args = funcall.args
|
|
50
|
+
|
|
51
|
+
case func
|
|
52
|
+
when :ip, :cidr
|
|
53
|
+
checker.check_arity(func, args, 1)
|
|
54
|
+
arg = checker.call(args.first)
|
|
55
|
+
TYPES[func] if checker.find_match_all_types(%i[string], arg)
|
|
56
|
+
when :isIP, :isCanonical
|
|
57
|
+
checker.check_arity(func, args, 1)
|
|
58
|
+
arg = checker.call(args.first)
|
|
59
|
+
TYPES[:bool] if checker.find_match_all_types(%i[string], arg)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def __ip(value)
|
|
64
|
+
Cel::IP.new(value)
|
|
65
|
+
rescue IPAddr::InvalidAddressError
|
|
66
|
+
raise EvaluateError, "IP Address '#{value}' parse error during conversion from string"
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def __cidr(value)
|
|
70
|
+
Cel::CIDR.new(value)
|
|
71
|
+
rescue IPAddr::InvalidAddressError
|
|
72
|
+
raise EvaluateError, "IP Address '#{value}' parse error during conversion from string"
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def ip(str, program:)
|
|
76
|
+
value = program.call(str).value
|
|
77
|
+
|
|
78
|
+
__ip(value)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def cidr(str, program:)
|
|
82
|
+
value = program.call(str).value
|
|
83
|
+
|
|
84
|
+
__cidr(value)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def isIP(str, program:) # rubocop:disable Naming/MethodName
|
|
88
|
+
value = program.call(str).value
|
|
89
|
+
|
|
90
|
+
begin
|
|
91
|
+
__ip(value)
|
|
92
|
+
Bool.new(true)
|
|
93
|
+
rescue EvaluateError
|
|
94
|
+
Bool.new(false)
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def isCanonical(str, program:) # rubocop:disable Naming/MethodName
|
|
99
|
+
value = program.call(str).value
|
|
100
|
+
|
|
101
|
+
Bool.new(value == __ip(value).to_s)
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# https://pkg.go.dev/k8s.io/apiserver/pkg/cel/library#IP
|
|
108
|
+
class IP < Literal
|
|
109
|
+
IPV4_LL_UC_CIDR = IPAddr.new("169.254.0.0/16")
|
|
110
|
+
IPV4_LL_MC_CIDR = IPAddr.new("224.0.0.0/24")
|
|
111
|
+
IPV6_LL_UC_CIDR = IPAddr.new("fe80::/10")
|
|
112
|
+
IPV6_LL_MC_CIDR = IPAddr.new("ff00::/8")
|
|
113
|
+
|
|
114
|
+
def initialize(val)
|
|
115
|
+
value = IPAddr.new(val)
|
|
116
|
+
|
|
117
|
+
if value.ipv6?
|
|
118
|
+
raise EvaluateError, "IP Address with zone value is not allowed" if value.zone_id
|
|
119
|
+
|
|
120
|
+
if Resolv::IPv4::Regex.match?(val.split(":").last)
|
|
121
|
+
raise EvaluateError, "IPv4-mapped IPv6 address is not allowed"
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
super(:ip, value)
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
define_cel_method(:==) do |other|
|
|
129
|
+
case other
|
|
130
|
+
when IP
|
|
131
|
+
if @value.ipv4? && other.value.ipv6? && other.value.ipv4_mapped?
|
|
132
|
+
# convert ipv4 to ipv4-mapped ipv6 addr
|
|
133
|
+
@value.ipv4_mapped == other.value
|
|
134
|
+
elsif @value.ipv6? && other.value.ipv4? && @value.ipv4_mapped?
|
|
135
|
+
# convert ipv4 to ipv4-mapped ipv6 addr
|
|
136
|
+
@value == other.value.ipv4_mapped
|
|
137
|
+
else
|
|
138
|
+
# same family?
|
|
139
|
+
@value == other.value
|
|
140
|
+
end
|
|
141
|
+
else
|
|
142
|
+
super
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
define_cel_method(:family) do
|
|
147
|
+
Number.new(:int, @value.ipv6? ? 6 : 4)
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
define_cel_method(:isUnspecified) do
|
|
151
|
+
Bool.cast(@value == (@value.ipv6? ? "::" : "0.0.0.0"))
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
define_cel_method(:isLoopback) do
|
|
155
|
+
Bool.cast(@value.loopback?)
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
define_cel_method(:isGlobalUnicast) do
|
|
159
|
+
if @value.ipv6?
|
|
160
|
+
# not a link-local unicast, loopback or multicast address.
|
|
161
|
+
Bool.cast(!@value.loopback? && !IPV6_LL_UC_CIDR.include?(@value) && !IPV6_LL_MC_CIDR.include?(@value))
|
|
162
|
+
else
|
|
163
|
+
# not zero or 255.255.255.255
|
|
164
|
+
Bool.cast(@value != "255.255.255.255" && @value != "0.0.0.0")
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
define_cel_method(:isLinkLocalMulticast) do
|
|
169
|
+
Bool.cast(@value.ipv6? ? IPV6_LL_MC_CIDR.include?(@value) : IPV4_LL_MC_CIDR.include?(@value))
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
define_cel_method(:isLinkLocalUnicast) do
|
|
173
|
+
Bool.cast(@value.ipv6? ? IPV6_LL_UC_CIDR.include?(@value) : IPV4_LL_UC_CIDR.include?(@value))
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
class CIDR < Literal
|
|
178
|
+
def initialize(val)
|
|
179
|
+
value = IPAddr.new(val)
|
|
180
|
+
|
|
181
|
+
if value.ipv6?
|
|
182
|
+
raise EvaluateError, "IP Address with zone value is not allowed" if value.zone_id
|
|
183
|
+
|
|
184
|
+
val, = val.split("/", 2)
|
|
185
|
+
if Resolv::IPv4::Regex.match?(val.split(":").last)
|
|
186
|
+
raise EvaluateError, "IPv4-mapped IPv6 address is not allowed"
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
super(:cidr, value)
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
define_cel_method(:containsIP) do |other|
|
|
194
|
+
Bool.cast(@value.include?(other.value))
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
define_cel_method(:containsCIDR) do |other|
|
|
198
|
+
Bool.cast(@value.include?(other.value))
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
define_cel_method(:ip) do
|
|
202
|
+
IP.new(@value.to_s)
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
define_cel_method(:masked) do
|
|
206
|
+
self
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
define_cel_method(:prefixLength) do
|
|
210
|
+
Number.new(:int, @value.prefix)
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
def to_str
|
|
214
|
+
@value.cidr
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
TYPES[:ip] = Type.new(:ip)
|
|
219
|
+
TYPES[:cidr] = Type.new(:cidr)
|
|
220
|
+
end
|
data/lib/cel/extensions.rb
CHANGED
|
@@ -4,6 +4,7 @@ require_relative "extensions/math"
|
|
|
4
4
|
require_relative "extensions/string"
|
|
5
5
|
require_relative "extensions/bind"
|
|
6
6
|
require_relative "extensions/encoders"
|
|
7
|
+
require_relative "extensions/network"
|
|
7
8
|
|
|
8
9
|
module Cel
|
|
9
10
|
EXTENSIONS = {
|
|
@@ -11,5 +12,7 @@ module Cel
|
|
|
11
12
|
strings: Extensions::String,
|
|
12
13
|
base64: Extensions::Encoders::Base64,
|
|
13
14
|
cel: Extensions::Bind,
|
|
15
|
+
net: Extensions::Network,
|
|
16
|
+
ip: Extensions::Network::IP,
|
|
14
17
|
}.freeze
|
|
15
18
|
end
|
data/lib/cel/parser.rb
CHANGED
data/lib/cel/program.rb
CHANGED
|
@@ -208,10 +208,14 @@ module Cel
|
|
|
208
208
|
return evaluate_invoke(invoke, proto_type)
|
|
209
209
|
end
|
|
210
210
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
211
|
+
if @environment.extensions.key?(var.to_sym)
|
|
212
|
+
var = @environment.extensions[var.to_sym]
|
|
213
|
+
elsif args.nil?
|
|
214
|
+
# try stuff like a.b.c
|
|
215
|
+
return evaluate_identifier(invoke.to_s)
|
|
216
|
+
else
|
|
217
|
+
evaluate_identifier(var)
|
|
218
|
+
end
|
|
215
219
|
|
|
216
220
|
when Invoke
|
|
217
221
|
if args.nil?
|
|
@@ -265,7 +269,7 @@ module Cel
|
|
|
265
269
|
raise EvaluateError, "#{invoke} is not supported" unless var.class.method_defined?(func, false)
|
|
266
270
|
|
|
267
271
|
# eliminate protobuf API from the lookup
|
|
268
|
-
raise NoMatchingOverloadError.new(var, func) if Protobuf.base_class.
|
|
272
|
+
raise NoMatchingOverloadError.new(var, func) if Protobuf.base_class.method_defined?(func)
|
|
269
273
|
|
|
270
274
|
# If e evaluates to a message and f is not declared in this message, the
|
|
271
275
|
# runtime error no_such_field is raised.
|
|
@@ -274,16 +278,18 @@ module Cel
|
|
|
274
278
|
var.cel_send(func, *args)
|
|
275
279
|
when Literal
|
|
276
280
|
# eliminate ruby API from the lookup
|
|
277
|
-
raise NoMatchingOverloadError.new(var, func) if Literal.
|
|
281
|
+
raise NoMatchingOverloadError.new(var, func) if Literal.method_defined?(func)
|
|
278
282
|
|
|
279
283
|
# If e evaluates to a message and f is not declared in this message, the
|
|
280
284
|
# runtime error no_such_field is raised.
|
|
281
285
|
raise NoSuchFieldError.new(var, func) unless var.respond_to?(func)
|
|
282
286
|
|
|
287
|
+
args ?
|
|
288
|
+
var.cel_send(func, *Array(args).map(&method(:call))) :
|
|
283
289
|
var.cel_send(func)
|
|
284
290
|
when Protobuf.base_class
|
|
285
291
|
# eliminate protobuf API from the lookup
|
|
286
|
-
raise NoMatchingOverloadError.new(var, func) if Protobuf.base_class.
|
|
292
|
+
raise NoMatchingOverloadError.new(var, func) if Protobuf.base_class.method_defined?(func)
|
|
287
293
|
|
|
288
294
|
# If e evaluates to a message and f is not declared in this message, the
|
|
289
295
|
# runtime error no_such_field is raised.
|
|
@@ -344,6 +350,12 @@ module Cel
|
|
|
344
350
|
func = funcall.func
|
|
345
351
|
args = funcall.args
|
|
346
352
|
|
|
353
|
+
unless Module.respond_to?(func)
|
|
354
|
+
@environment.implicit_extensions.each do |ext|
|
|
355
|
+
return ext.__send__(func, *args, program: self) if ext.respond_to?(func)
|
|
356
|
+
end
|
|
357
|
+
end
|
|
358
|
+
|
|
347
359
|
case func
|
|
348
360
|
when :type
|
|
349
361
|
operand = args.first
|
data/lib/cel/version.rb
CHANGED
data/lib/cel.rb
CHANGED
|
@@ -22,8 +22,7 @@ end
|
|
|
22
22
|
|
|
23
23
|
begin
|
|
24
24
|
require_relative "cel/ast/elements/protobuf"
|
|
25
|
-
rescue LoadError
|
|
26
|
-
puts e
|
|
25
|
+
rescue LoadError
|
|
27
26
|
module Cel
|
|
28
27
|
class << self
|
|
29
28
|
# returns a struct class containing the attributes that one would
|
|
@@ -114,26 +113,27 @@ rescue LoadError => e
|
|
|
114
113
|
module Protobuf
|
|
115
114
|
module_function
|
|
116
115
|
|
|
117
|
-
|
|
116
|
+
class BaseClass < Object
|
|
117
|
+
end
|
|
118
118
|
|
|
119
119
|
def base_class
|
|
120
120
|
Struct
|
|
121
121
|
end
|
|
122
122
|
|
|
123
123
|
def enum_class
|
|
124
|
-
|
|
124
|
+
BaseClass
|
|
125
125
|
end
|
|
126
126
|
|
|
127
127
|
def map_class
|
|
128
|
-
|
|
128
|
+
BaseClass
|
|
129
129
|
end
|
|
130
130
|
|
|
131
131
|
def timestamp_class
|
|
132
|
-
|
|
132
|
+
BaseClass
|
|
133
133
|
end
|
|
134
134
|
|
|
135
135
|
def duration_class
|
|
136
|
-
|
|
136
|
+
BaseClass
|
|
137
137
|
end
|
|
138
138
|
|
|
139
139
|
def convert_to_proto(message_type, values)
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: cel
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.6.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Tiago Cardoso
|
|
@@ -109,6 +109,7 @@ files:
|
|
|
109
109
|
- lib/cel/extensions/bind.rb
|
|
110
110
|
- lib/cel/extensions/encoders.rb
|
|
111
111
|
- lib/cel/extensions/math.rb
|
|
112
|
+
- lib/cel/extensions/network.rb
|
|
112
113
|
- lib/cel/extensions/string.rb
|
|
113
114
|
- lib/cel/macro.rb
|
|
114
115
|
- lib/cel/parser.rb
|