Skip to content

Commit a292bec

Browse files
authored
Adding cache bypass when no profile available (#679)
* Adding cache bypass when no profile available Signed-off-by: Amit Schendel <[email protected]> * Fixing test Signed-off-by: Amit Schendel <[email protected]> --------- Signed-off-by: Amit Schendel <[email protected]>
1 parent 1ad518a commit a292bec

File tree

10 files changed

+125
-51
lines changed

10 files changed

+125
-51
lines changed

pkg/rulemanager/cel/libraries/applicationprofile/ap.go

Lines changed: 32 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,10 @@ func (l *apLibrary) Declarations() map[string][]cel.FunctionOpt {
5353
return l.wasExecuted(args[0], args[1])
5454
}
5555
cachedFunc := l.functionCache.WithCache(wrapperFunc, "ap.was_executed")
56-
return cachedFunc(values[0], values[1])
56+
result := cachedFunc(values[0], values[1])
57+
// Convert "profile not available" error to false after cache layer
58+
// This ensures: 1) error is not cached, 2) rule evaluation continues normally
59+
return cache.ConvertProfileNotAvailableErrToBool(result, false)
5760
}),
5861
),
5962
},
@@ -68,7 +71,10 @@ func (l *apLibrary) Declarations() map[string][]cel.FunctionOpt {
6871
return l.wasExecutedWithArgs(args[0], args[1], args[2])
6972
}
7073
cachedFunc := l.functionCache.WithCache(wrapperFunc, "ap.was_executed_with_args")
71-
return cachedFunc(values[0], values[1], values[2])
74+
result := cachedFunc(values[0], values[1], values[2])
75+
// Convert "profile not available" error to false after cache layer
76+
// This ensures: 1) error is not cached, 2) rule evaluation continues normally
77+
return cache.ConvertProfileNotAvailableErrToBool(result, false)
7278
}),
7379
),
7480
},
@@ -83,7 +89,8 @@ func (l *apLibrary) Declarations() map[string][]cel.FunctionOpt {
8389
return l.wasPathOpened(args[0], args[1])
8490
}
8591
cachedFunc := l.functionCache.WithCache(wrapperFunc, "ap.was_path_opened")
86-
return cachedFunc(values[0], values[1])
92+
result := cachedFunc(values[0], values[1])
93+
return cache.ConvertProfileNotAvailableErrToBool(result, false)
8794
}),
8895
),
8996
},
@@ -98,7 +105,8 @@ func (l *apLibrary) Declarations() map[string][]cel.FunctionOpt {
98105
return l.wasPathOpenedWithFlags(args[0], args[1], args[2])
99106
}
100107
cachedFunc := l.functionCache.WithCache(wrapperFunc, "ap.was_path_opened_with_flags")
101-
return cachedFunc(values[0], values[1], values[2])
108+
result := cachedFunc(values[0], values[1], values[2])
109+
return cache.ConvertProfileNotAvailableErrToBool(result, false)
102110
}),
103111
),
104112
},
@@ -113,7 +121,8 @@ func (l *apLibrary) Declarations() map[string][]cel.FunctionOpt {
113121
return l.wasPathOpenedWithSuffix(args[0], args[1])
114122
}
115123
cachedFunc := l.functionCache.WithCache(wrapperFunc, "ap.was_path_opened_with_suffix")
116-
return cachedFunc(values[0], values[1])
124+
result := cachedFunc(values[0], values[1])
125+
return cache.ConvertProfileNotAvailableErrToBool(result, false)
117126
}),
118127
),
119128
},
@@ -128,7 +137,8 @@ func (l *apLibrary) Declarations() map[string][]cel.FunctionOpt {
128137
return l.wasPathOpenedWithPrefix(args[0], args[1])
129138
}
130139
cachedFunc := l.functionCache.WithCache(wrapperFunc, "ap.was_path_opened_with_prefix")
131-
return cachedFunc(values[0], values[1])
140+
result := cachedFunc(values[0], values[1])
141+
return cache.ConvertProfileNotAvailableErrToBool(result, false)
132142
}),
133143
),
134144
},
@@ -143,7 +153,8 @@ func (l *apLibrary) Declarations() map[string][]cel.FunctionOpt {
143153
return l.wasSyscallUsed(args[0], args[1])
144154
}
145155
cachedFunc := l.functionCache.WithCache(wrapperFunc, "ap.was_syscall_used")
146-
return cachedFunc(values[0], values[1])
156+
result := cachedFunc(values[0], values[1])
157+
return cache.ConvertProfileNotAvailableErrToBool(result, false)
147158
}),
148159
),
149160
},
@@ -158,7 +169,8 @@ func (l *apLibrary) Declarations() map[string][]cel.FunctionOpt {
158169
return l.wasCapabilityUsed(args[0], args[1])
159170
}
160171
cachedFunc := l.functionCache.WithCache(wrapperFunc, "ap.was_capability_used")
161-
return cachedFunc(values[0], values[1])
172+
result := cachedFunc(values[0], values[1])
173+
return cache.ConvertProfileNotAvailableErrToBool(result, false)
162174
}),
163175
),
164176
},
@@ -173,7 +185,8 @@ func (l *apLibrary) Declarations() map[string][]cel.FunctionOpt {
173185
return l.wasEndpointAccessed(args[0], args[1])
174186
}
175187
cachedFunc := l.functionCache.WithCache(wrapperFunc, "ap.was_endpoint_accessed")
176-
return cachedFunc(values[0], values[1])
188+
result := cachedFunc(values[0], values[1])
189+
return cache.ConvertProfileNotAvailableErrToBool(result, false)
177190
}),
178191
),
179192
},
@@ -188,7 +201,8 @@ func (l *apLibrary) Declarations() map[string][]cel.FunctionOpt {
188201
return l.wasEndpointAccessedWithMethod(args[0], args[1], args[2])
189202
}
190203
cachedFunc := l.functionCache.WithCache(wrapperFunc, "ap.was_endpoint_accessed_with_method")
191-
return cachedFunc(values[0], values[1], values[2])
204+
result := cachedFunc(values[0], values[1], values[2])
205+
return cache.ConvertProfileNotAvailableErrToBool(result, false)
192206
}),
193207
),
194208
},
@@ -203,7 +217,8 @@ func (l *apLibrary) Declarations() map[string][]cel.FunctionOpt {
203217
return l.wasEndpointAccessedWithMethods(args[0], args[1], args[2])
204218
}
205219
cachedFunc := l.functionCache.WithCache(wrapperFunc, "ap.was_endpoint_accessed_with_methods")
206-
return cachedFunc(values[0], values[1], values[2])
220+
result := cachedFunc(values[0], values[1], values[2])
221+
return cache.ConvertProfileNotAvailableErrToBool(result, false)
207222
}),
208223
),
209224
},
@@ -218,7 +233,8 @@ func (l *apLibrary) Declarations() map[string][]cel.FunctionOpt {
218233
return l.wasEndpointAccessedWithPrefix(args[0], args[1])
219234
}
220235
cachedFunc := l.functionCache.WithCache(wrapperFunc, "ap.was_endpoint_accessed_with_prefix")
221-
return cachedFunc(values[0], values[1])
236+
result := cachedFunc(values[0], values[1])
237+
return cache.ConvertProfileNotAvailableErrToBool(result, false)
222238
}),
223239
),
224240
},
@@ -233,7 +249,8 @@ func (l *apLibrary) Declarations() map[string][]cel.FunctionOpt {
233249
return l.wasEndpointAccessedWithSuffix(args[0], args[1])
234250
}
235251
cachedFunc := l.functionCache.WithCache(wrapperFunc, "ap.was_endpoint_accessed_with_suffix")
236-
return cachedFunc(values[0], values[1])
252+
result := cachedFunc(values[0], values[1])
253+
return cache.ConvertProfileNotAvailableErrToBool(result, false)
237254
}),
238255
),
239256
},
@@ -248,7 +265,8 @@ func (l *apLibrary) Declarations() map[string][]cel.FunctionOpt {
248265
return l.wasHostAccessed(args[0], args[1])
249266
}
250267
cachedFunc := l.functionCache.WithCache(wrapperFunc, "ap.was_host_accessed")
251-
return cachedFunc(values[0], values[1])
268+
result := cachedFunc(values[0], values[1])
269+
return cache.ConvertProfileNotAvailableErrToBool(result, false)
252270
}),
253271
),
254272
},

pkg/rulemanager/cel/libraries/applicationprofile/capability.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55

66
"github.com/google/cel-go/common/types"
77
"github.com/google/cel-go/common/types/ref"
8+
"github.com/kubescape/node-agent/pkg/rulemanager/cel/libraries/cache"
89
"github.com/kubescape/node-agent/pkg/rulemanager/profilehelper"
910
)
1011

@@ -24,7 +25,7 @@ func (l *apLibrary) wasCapabilityUsed(containerID, capabilityName ref.Val) ref.V
2425

2526
container, _, err := profilehelper.GetContainerApplicationProfile(l.objectCache, containerIDStr)
2627
if err != nil {
27-
return types.Bool(false)
28+
return cache.NewProfileNotAvailableErr("%v", err)
2829
}
2930

3031
if slices.Contains(container.Capabilities, capabilityNameStr) {

pkg/rulemanager/cel/libraries/applicationprofile/exec.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"github.com/google/cel-go/common/types/ref"
99
"github.com/kubescape/go-logger"
1010
"github.com/kubescape/go-logger/helpers"
11+
"github.com/kubescape/node-agent/pkg/rulemanager/cel/libraries/cache"
1112
"github.com/kubescape/node-agent/pkg/rulemanager/cel/libraries/celparse"
1213
"github.com/kubescape/node-agent/pkg/rulemanager/profilehelper"
1314
)
@@ -33,7 +34,9 @@ func (l *apLibrary) wasExecuted(containerID, path ref.Val) ref.Val {
3334

3435
container, _, err := profilehelper.GetContainerApplicationProfile(l.objectCache, containerIDStr)
3536
if err != nil {
36-
return types.Bool(false)
37+
// Return a special error that will NOT be cached, allowing retry when profile becomes available.
38+
// The caller should convert this to false after the cache layer.
39+
return cache.NewProfileNotAvailableErr("%v", err)
3740
}
3841

3942
for _, exec := range container.Execs {
@@ -76,7 +79,9 @@ func (l *apLibrary) wasExecutedWithArgs(containerID, path, args ref.Val) ref.Val
7679

7780
container, _, err := profilehelper.GetContainerApplicationProfile(l.objectCache, containerIDStr)
7881
if err != nil {
79-
return types.Bool(false)
82+
// Return a special error that will NOT be cached, allowing retry when profile becomes available.
83+
// The caller should convert this to false after the cache layer.
84+
return cache.NewProfileNotAvailableErr("%v", err)
8085
}
8186

8287
for _, exec := range container.Execs {

pkg/rulemanager/cel/libraries/applicationprofile/http.go

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77

88
"github.com/google/cel-go/common/types"
99
"github.com/google/cel-go/common/types/ref"
10+
"github.com/kubescape/node-agent/pkg/rulemanager/cel/libraries/cache"
1011
"github.com/kubescape/node-agent/pkg/rulemanager/cel/libraries/celparse"
1112
"github.com/kubescape/node-agent/pkg/rulemanager/profilehelper"
1213
"github.com/kubescape/storage/pkg/registry/file/dynamicpathdetector"
@@ -29,7 +30,7 @@ func (l *apLibrary) wasEndpointAccessed(containerID, endpoint ref.Val) ref.Val {
2930

3031
container, _, err := profilehelper.GetContainerApplicationProfile(l.objectCache, containerIDStr)
3132
if err != nil {
32-
return types.Bool(false)
33+
return cache.NewProfileNotAvailableErr("%v", err)
3334
}
3435

3536
for _, ep := range container.Endpoints {
@@ -62,7 +63,7 @@ func (l *apLibrary) wasEndpointAccessedWithMethod(containerID, endpoint, method
6263

6364
container, _, err := profilehelper.GetContainerApplicationProfile(l.objectCache, containerIDStr)
6465
if err != nil {
65-
return types.Bool(false)
66+
return cache.NewProfileNotAvailableErr("%v", err)
6667
}
6768

6869
for _, ep := range container.Endpoints {
@@ -98,7 +99,7 @@ func (l *apLibrary) wasEndpointAccessedWithMethods(containerID, endpoint, method
9899

99100
container, _, err := profilehelper.GetContainerApplicationProfile(l.objectCache, containerIDStr)
100101
if err != nil {
101-
return types.Bool(false)
102+
return cache.NewProfileNotAvailableErr("%v", err)
102103
}
103104

104105
for _, ep := range container.Endpoints {
@@ -131,7 +132,7 @@ func (l *apLibrary) wasEndpointAccessedWithPrefix(containerID, prefix ref.Val) r
131132

132133
container, _, err := profilehelper.GetContainerApplicationProfile(l.objectCache, containerIDStr)
133134
if err != nil {
134-
return types.Bool(false)
135+
return cache.NewProfileNotAvailableErr("%v", err)
135136
}
136137

137138
for _, ep := range container.Endpoints {
@@ -160,7 +161,7 @@ func (l *apLibrary) wasEndpointAccessedWithSuffix(containerID, suffix ref.Val) r
160161

161162
container, _, err := profilehelper.GetContainerApplicationProfile(l.objectCache, containerIDStr)
162163
if err != nil {
163-
return types.Bool(false)
164+
return cache.NewProfileNotAvailableErr("%v", err)
164165
}
165166

166167
for _, ep := range container.Endpoints {
@@ -189,19 +190,22 @@ func (l *apLibrary) wasHostAccessed(containerID, host ref.Val) ref.Val {
189190

190191
// Check HTTP endpoints for host access
191192
container, _, err := profilehelper.GetContainerApplicationProfile(l.objectCache, containerIDStr)
192-
if err == nil {
193-
for _, ep := range container.Endpoints {
194-
// Parse the endpoint URL to extract host
195-
if parsedURL, err := url.Parse(ep.Endpoint); err == nil && parsedURL.Host != "" {
196-
if parsedURL.Host == hostStr || parsedURL.Hostname() == hostStr {
197-
return types.Bool(true)
198-
}
199-
}
200-
// Also check if the endpoint contains the host as a substring (for cases where it's not a full URL)
201-
if strings.Contains(ep.Endpoint, hostStr) {
193+
if err != nil {
194+
return cache.NewProfileNotAvailableErr("%v", err)
195+
}
196+
197+
for _, ep := range container.Endpoints {
198+
// Parse the endpoint URL to extract host
199+
if parsedURL, err := url.Parse(ep.Endpoint); err == nil && parsedURL.Host != "" {
200+
if parsedURL.Host == hostStr || parsedURL.Hostname() == hostStr {
202201
return types.Bool(true)
203202
}
204203
}
204+
// Also check if the endpoint contains the host as a substring (for cases where it's not a full URL)
205+
if strings.Contains(ep.Endpoint, hostStr) {
206+
return types.Bool(true)
207+
}
205208
}
209+
206210
return types.Bool(false)
207211
}

pkg/rulemanager/cel/libraries/applicationprofile/open.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55

66
"github.com/google/cel-go/common/types"
77
"github.com/google/cel-go/common/types/ref"
8+
"github.com/kubescape/node-agent/pkg/rulemanager/cel/libraries/cache"
89
"github.com/kubescape/node-agent/pkg/rulemanager/cel/libraries/celparse"
910
"github.com/kubescape/node-agent/pkg/rulemanager/profilehelper"
1011
"github.com/kubescape/storage/pkg/registry/file/dynamicpathdetector"
@@ -26,7 +27,7 @@ func (l *apLibrary) wasPathOpened(containerID, path ref.Val) ref.Val {
2627

2728
container, _, err := profilehelper.GetContainerApplicationProfile(l.objectCache, containerIDStr)
2829
if err != nil {
29-
return types.Bool(false)
30+
return cache.NewProfileNotAvailableErr("%v", err)
3031
}
3132

3233
for _, open := range container.Opens {
@@ -60,7 +61,7 @@ func (l *apLibrary) wasPathOpenedWithFlags(containerID, path, flags ref.Val) ref
6061

6162
container, _, err := profilehelper.GetContainerApplicationProfile(l.objectCache, containerIDStr)
6263
if err != nil {
63-
return types.Bool(false)
64+
return cache.NewProfileNotAvailableErr("%v", err)
6465
}
6566

6667
for _, open := range container.Opens {
@@ -90,7 +91,7 @@ func (l *apLibrary) wasPathOpenedWithSuffix(containerID, suffix ref.Val) ref.Val
9091

9192
container, _, err := profilehelper.GetContainerApplicationProfile(l.objectCache, containerIDStr)
9293
if err != nil {
93-
return types.Bool(false)
94+
return cache.NewProfileNotAvailableErr("%v", err)
9495
}
9596

9697
for _, open := range container.Opens {
@@ -118,7 +119,7 @@ func (l *apLibrary) wasPathOpenedWithPrefix(containerID, prefix ref.Val) ref.Val
118119

119120
container, _, err := profilehelper.GetContainerApplicationProfile(l.objectCache, containerIDStr)
120121
if err != nil {
121-
return types.Bool(false)
122+
return cache.NewProfileNotAvailableErr("%v", err)
122123
}
123124

124125
for _, open := range container.Opens {

pkg/rulemanager/cel/libraries/applicationprofile/syscall.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55

66
"github.com/google/cel-go/common/types"
77
"github.com/google/cel-go/common/types/ref"
8+
"github.com/kubescape/node-agent/pkg/rulemanager/cel/libraries/cache"
89
"github.com/kubescape/node-agent/pkg/rulemanager/profilehelper"
910
)
1011

@@ -24,7 +25,7 @@ func (l *apLibrary) wasSyscallUsed(containerID, syscallName ref.Val) ref.Val {
2425

2526
container, _, err := profilehelper.GetContainerApplicationProfile(l.objectCache, containerIDStr)
2627
if err != nil {
27-
return types.Bool(false)
28+
return cache.NewProfileNotAvailableErr("%v", err)
2829
}
2930

3031
if slices.Contains(container.Syscalls, syscallNameStr) {

pkg/rulemanager/cel/libraries/cache/function_cache.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,41 @@ import (
1010
"github.com/hashicorp/golang-lru/v2/expirable"
1111
)
1212

13+
// ProfileNotAvailableErr is a sentinel error message used to indicate that a profile
14+
// is not yet available. This error is NOT cached, allowing retry when profile becomes available.
15+
// After the cache layer, this error should be converted to a default value (e.g., false)
16+
// to allow rule evaluation to continue without failing.
17+
const ProfileNotAvailableErr = "profile not available"
18+
19+
// NewProfileNotAvailableErr creates a new "profile not available" error.
20+
// This error will NOT be cached, allowing the function to be re-evaluated
21+
// when the profile becomes available.
22+
func NewProfileNotAvailableErr(format string, args ...any) ref.Val {
23+
return types.NewErr(ProfileNotAvailableErr+": "+format, args...)
24+
}
25+
26+
// IsProfileNotAvailableErr checks if the given value is a "profile not available" error.
27+
func IsProfileNotAvailableErr(val ref.Val) bool {
28+
if !types.IsError(val) {
29+
return false
30+
}
31+
errVal, ok := val.Value().(error)
32+
if !ok {
33+
return false
34+
}
35+
return strings.Contains(errVal.Error(), ProfileNotAvailableErr)
36+
}
37+
38+
// ConvertProfileNotAvailableErrToBool converts a "profile not available" error to a boolean value.
39+
// This should be called AFTER the cache layer to ensure the error is not cached,
40+
// but the rule evaluation can continue with a default value.
41+
func ConvertProfileNotAvailableErrToBool(val ref.Val, defaultVal bool) ref.Val {
42+
if IsProfileNotAvailableErr(val) {
43+
return types.Bool(defaultVal)
44+
}
45+
return val
46+
}
47+
1348
type FunctionCacheConfig struct {
1449
MaxSize int `mapstructure:"maxSize"`
1550
TTL time.Duration `mapstructure:"ttl"`

0 commit comments

Comments
 (0)