Skip to content

Commit 5edb849

Browse files
committed
we need a seperate not overloaded wildcard for execs
Signed-off-by: entlein <einentlein@gmail.com>
1 parent a1f6963 commit 5edb849

8 files changed

Lines changed: 228 additions & 34 deletions

File tree

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ require (
3434
github.com/joncrlsn/dque v0.0.0-20241024143830-7723fd131a64
3535
github.com/kubescape/backend v0.0.39
3636
github.com/kubescape/go-logger v0.0.32
37-
github.com/kubescape/k8s-interface v0.0.213
37+
github.com/kubescape/k8s-interface v0.0.214
3838
github.com/kubescape/storage v0.0.278
3939
github.com/kubescape/workerpool v0.0.0-20250526074519-0e4a4e7f44cf
4040
github.com/moby/sys/mountinfo v0.7.2

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -903,6 +903,8 @@ github.com/kubescape/go-logger v0.0.32 h1:4mI+XJOV8VFCMewrEE9VIFEIOhzXokYT3nFpNf
903903
github.com/kubescape/go-logger v0.0.32/go.mod h1:Alj7JBQ8/WCxbXe8Ura6ZheSRK45E0p21M3xeqedX90=
904904
github.com/kubescape/k8s-interface v0.0.213 h1:JaEVzgE5qwQ3rEjQ8tBMp48YX4yveitLfYNaCIk8j/A=
905905
github.com/kubescape/k8s-interface v0.0.213/go.mod h1:WNYUG93aZ5kDmuaRKFLtVhp18Yc6EfaHdD1gLYtVTN4=
906+
github.com/kubescape/k8s-interface v0.0.214 h1:j7KP0/5VvYOoQdBGV2+gRM3qnR8PWLAGF8RM/k/DmJ0=
907+
github.com/kubescape/k8s-interface v0.0.214/go.mod h1:WNYUG93aZ5kDmuaRKFLtVhp18Yc6EfaHdD1gLYtVTN4=
906908
github.com/kubescape/storage v0.0.278 h1:/pOtKul443yb2Fzg/4MFq29oOaFoJg1okQaCGbcEVOk=
907909
github.com/kubescape/storage v0.0.278/go.mod h1:FpV6tCrYXlp2kKWza4yr7zf2Y1q7IGgx871ndN7SMNo=
908910
github.com/kubescape/storage v0.0.278 h1:/pOtKul443yb2Fzg/4MFq29oOaFoJg1okQaCGbcEVOk=

pkg/objectcache/containerprofilecache/test32_realpipeline_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import (
2424
// R0040 silence comes from the projection (missing/empty vector) or from
2525
// somewhere else (capture-side args).
2626
func TestTest32_RealProjectionPipeline(t *testing.T) {
27-
const wild = dynamicpathdetector.WildcardIdentifier
27+
const wild = dynamicpathdetector.ExecArgsWildcard
2828

2929
// curl-32-overlay user AP. Container name MUST be "nginx" to match the
3030
// InstanceID that primeSharedData/eventContainer build. The argv shapes
@@ -140,7 +140,7 @@ func keysOf(m map[string]struct{}) []string {
140140
// raw-CompareExecArgs walk (deployed behaviour) returns the WRONG answer and
141141
// the MatchExecArgs (strict) walk (the fix) returns the RIGHT answer.
142142
func TestTest32_BaseCPBareVectorPoisonsR0040(t *testing.T) {
143-
const wild = dynamicpathdetector.WildcardIdentifier
143+
const wild = dynamicpathdetector.ExecArgsWildcard
144144

145145
// Base consolidated CP: the recorded /bin/busybox startup exec, bare args.
146146
baseCP := &v1beta1.ContainerProfile{

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

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -114,9 +114,11 @@ func (l *apLibrary) wasExecutedWithArgs(containerID, path, args ref.Val) ref.Val
114114
// an empty recorded vector matches ONLY an empty runtime argv, so
115115
// a recorder/synthetic "ran with no args" entry does NOT act as a
116116
// wildcard and poison the multi-vector OR (the #805 production
117-
// failure). The matcher handles WildcardIdentifier "*", bare
118-
// DynamicIdentifier "⋯", and embedded-⋯ path tokens (the postgres
119-
// versioned-binary case) — see storage's compare_exec_args.go.
117+
// failure). In exec args the wildcards are dedicated sentinels:
118+
// ExecArgsWildcard "⋯⋯" (zero-or-more whole args), DynamicIdentifier
119+
// "⋯" (one arg / one embedded segment — the postgres versioned-binary
120+
// case); a "*" is a LITERAL character, never a wildcard — see
121+
// storage's compare_exec_args.go.
120122
if _, ok := cp.Execs.Values[pathStr]; ok {
121123
if vectors, ok := cp.ExecsByPath[pathStr]; ok {
122124
for _, profileArgs := range vectors {

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

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -476,9 +476,9 @@ func TestExecWithArgsCompilation(t *testing.T) {
476476
//
477477
// Test_32 has 4 subtests; this pins the contract for each:
478478
//
479-
// sh_dash_c_matches_wildcard_trailing — argv matches profile [sh, -c, *].
479+
// sh_dash_c_matches_wildcard_trailing — argv matches profile [sh, -c, ⋯⋯].
480480
// sh_dash_x_mismatches_R0040 — argv mismatches at literal anchor.
481-
// echo_hello_matches_wildcard_trailing — argv matches profile [echo, hello, *].
481+
// echo_hello_matches_wildcard_trailing — argv matches profile [echo, hello, ⋯⋯].
482482
// echo_goodbye_mismatches_R0040 — argv mismatches at literal "hello".
483483
func TestExecWithArgsBusyboxMultiVector(t *testing.T) {
484484
objCache := objectcachev1.RuleObjectCacheMock{
@@ -499,9 +499,9 @@ func TestExecWithArgsBusyboxMultiVector(t *testing.T) {
499499
// shapes. The projection layer appends them all into
500500
// ExecsByPath["/bin/busybox"]; the matcher walks every
501501
// vector and accepts if ANY matches.
502-
{Path: "/bin/busybox", Args: []string{"/bin/sleep", dynamicpathdetector.WildcardIdentifier}},
503-
{Path: "/bin/busybox", Args: []string{"/bin/sh", "-c", dynamicpathdetector.WildcardIdentifier}},
504-
{Path: "/bin/busybox", Args: []string{"/bin/echo", "hello", dynamicpathdetector.WildcardIdentifier}},
502+
{Path: "/bin/busybox", Args: []string{"/bin/sleep", dynamicpathdetector.ExecArgsWildcard}},
503+
{Path: "/bin/busybox", Args: []string{"/bin/sh", "-c", dynamicpathdetector.ExecArgsWildcard}},
504+
{Path: "/bin/busybox", Args: []string{"/bin/echo", "hello", dynamicpathdetector.ExecArgsWildcard}},
505505
},
506506
})
507507
objCache.SetApplicationProfile(profile)
@@ -583,7 +583,7 @@ func TestExecWithArgsBusyboxMultiVector(t *testing.T) {
583583
// every argv mismatch in #805 CT runs through the "side-effects" tip.
584584
//
585585
// In a real merged profile the SAME path can carry both a constrained vector
586-
// (from the user-defined ApplicationProfile, e.g. [echo, hello, *]) AND a
586+
// (from the user-defined ApplicationProfile, e.g. [echo, hello, ⋯⋯]) AND a
587587
// bare vector with no args (from the recorder, or a synthesised base CP, e.g.
588588
// /bin/busybox observed with empty Args). extractExecsByPath stores the bare
589589
// entry as an empty []string{}.
@@ -615,8 +615,8 @@ func TestExecWithArgsEmptyVectorDoesNotPoisonMatch(t *testing.T) {
615615
// the clean multi-vector test above.
616616
{Path: "/bin/busybox", Args: nil},
617617
// User-defined constrained vectors.
618-
{Path: "/bin/busybox", Args: []string{"/bin/echo", "hello", dynamicpathdetector.WildcardIdentifier}},
619-
{Path: "/bin/busybox", Args: []string{"/bin/sh", "-c", dynamicpathdetector.WildcardIdentifier}},
618+
{Path: "/bin/busybox", Args: []string{"/bin/echo", "hello", dynamicpathdetector.ExecArgsWildcard}},
619+
{Path: "/bin/busybox", Args: []string{"/bin/sh", "-c", dynamicpathdetector.ExecArgsWildcard}},
620620
},
621621
})
622622
objCache.SetApplicationProfile(profile)

tests/chart/templates/node-agent/default-rules.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -692,7 +692,7 @@ spec:
692692
- name: "Unexpected process arguments"
693693
enabled: true
694694
id: "R0040"
695-
description: "Detects an exec event whose path IS in the profile but whose argv vector does not match any recorded argv pattern for that path. Consumes ap.was_executed_with_args, which walks the ExecsByPath projection surface added by node-agent#807 and delegates argv comparison to dynamicpathdetector.CompareExecArgs (storage). Stays silent when the path is unknown (R0001 covers that case) and when the argv vector matches any recorded pattern (including the trailing-wildcard form `[…, *]`)."
695+
description: "Detects an exec event whose path IS in the profile but whose argv vector does not match any recorded argv pattern for that path. Consumes ap.was_executed_with_args, which walks the ExecsByPath projection surface added by node-agent#807 and delegates argv comparison to dynamicpathdetector.MatchExecArgs (storage). Stays silent when the path is unknown (R0001 covers that case) and when the argv vector matches any recorded pattern (including the trailing zero-or-more form `[…, ⋯⋯]` and the single-arg form `[…, ⋯]`); a `*` in a recorded arg is a literal character, not a wildcard."
696696
expressions:
697697
message: "'Unexpected process arguments: ' + event.comm + ' with PID ' + string(event.pid) + ' argv=' + event.args.map(a, string(a)).join(' ')"
698698
uniqueId: "event.comm + '_' + event.exepath + '_' + event.args.map(a, string(a)).join(' ')"

0 commit comments

Comments
 (0)